参考链接:An Introduction to Laravel Authorization Gates
这个微型博客系统包含两个用户角色(
和
- 作者
),它们的权限如下:
- 编辑
- laravel new blog
- > php artisan -V
- Laravel Framework 5.4.21
或者使用 composer create-project
- composer create-project --prefer-dist laravel/laravel blog
- .env
配置数据库连接信息。
- ...
- APP_URL=http://localhost
- ...
- DB_CONNECTION=mysql
- DB_HOST=127.0.0.1
- DB_PORT=3306
- DB_DATABASE=dbname
- DB_USERNAME=dbuser
- DB_PASSWORD=yoursecretdbuserpassword
- ...
- php artisan make:model Post -m -c
打开迁移文件,补充
方法内容。
- up
- Schema: :create('posts',
- function(Blueprint $table) {
- $table - >increments('id');
- $table - >string('title');
- $table - >string('slug') - >unique();
- $table - >text('body');
- $table - >boolean('published') - >
- default(false);
- $table - >unsignedInteger('user_id');
- $table - >timestamps();
- $table - >foreign('user_id') - >references('id') - >on('users');
- });
添加
表和关系表
- roles
。
- user_roles
表
- roles
- php artisan make:model Role -m
- Schema: :create('roles',
- function(Blueprint $table) {
- $table - >increments('id');
- $table - >string('name');
- $table - >string('slug') - >unique();
- $table - >jsonb('permissions'); // jsonb deletes duplicates
- $table - >timestamps();
- });
表
- user_roles
- php artisan make:migration create_user_roles_table
- public
- function up() {
- Schema: :create('user_roles',
- function(Blueprint $table) {
- $table - >unsignedInteger('user_id');
- $table - >unsignedInteger('role_id');
- $table - >timestamps();
- $table - >unique(['user_id', 'role_id']);
- $table - >foreign('user_id') - >references('id') - >on('users') - >onDelete('cascade');
- $table - >foreign('role_id') - >references('id') - >on('roles') - >onDelete('cascade');
- });
- }
- public
- function down() {
- Schema: :dropIfExists('user_roles');
- }
- php artisan make:seeder RolesTableSeeder
- use App\Role;
- class RolesTableSeeder extends Seeder
- {
- public function run()
- {
- $author = Role::create([
- 'name' => '作家',
- 'slug' => 'author',
- 'permissions' => [
- 'create-post' => true,
- ]
- ]);
- $editor = Role::create([
- 'name' => '编辑',
- 'slug' => 'editor',
- 'permissions' => [
- 'update-post' => true,
- 'publish-post' => true,
- 'delete-post' => true,
- ]
- ]);
- }
- }
修改
。
- DatabaseSeeder
- $this->call(RolesTableSeeder::class);
和
- User
Model
- Role
- class Role extends Model
- {
- protected $fillable = [
- 'name', 'slug', 'permissions',
- ];
- protected $casts = [
- 'permissions' => 'array',
- ];
- public function users()
- {
- return $this->belongsToMany(User::class, 'user_roles', 'role_id', 'user_id');
- }
- public function hasAccess(string $permission)
- {
- return $this->hasPermission($permission);
- }
- private function hasPermission(string $permission)
- {
- return $this->permissions[$permission] ?? false;
- }
- }
- class User extends Authenticatable
- {
- use Notifiable;
- protected $fillable = [
- 'name', 'email', 'password',
- ];
- protected $hidden = [
- 'password', 'remember_token',
- ];
- public function roles()
- {
- return $this->belongsToMany(Role::class, 'user_roles', 'user_id', 'role_id');
- }
- /**
- * Checks if User has access to $permission.
- */
- public function hasAccess(string $permission)
- {
- // check if the permission is available in any role
- foreach ($this->roles as $role) {
- if($role->hasAccess($permission)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Checks if the user belongs to role.
- */
- public function inRole(string $roleSlug)
- {
- return $this->roles()->where('slug', $roleSlug)->count() == 1;
- }
- }
迁移和数据注入
- php artisan migrate --seed
- > php artisan make:auth
- Authentication scaffolding generated successfully.
在
创建
- Controllers/Auth/RegisterController.php
方法(覆盖掉在
- showRegistrationForm
trait 中定义)。
- RegistersUsers
- Use App/Role;
- ...
- public function showRegistrationForm()
- {
- $roles = Role::orderBy('name')->pluck('name', 'id');
- return view('auth.register', compact('roles'));
- }
编辑
,添加角色选择项。
- resources/views/auth/register.blade.php
- ...
- <div class="form-group{{ $errors->has('role') ? ' has-error' : '' }}">
- <label for="role" class="col-md-4 control-label">User role</label>
- <div class="col-md-6">
- <select id="role" class="form-control" name="role" required>
- @foreach($roles as $id => $role)
- <option value="{{$id}}">{{$role}}</option>
- @endforeach
- </select>
- @if ($errors->has('role'))
- <span class="help-block">
- <strong>{{ $errors->first('role') }}</strong>
- </span>
- @endif
- </div>
- </div>
- ...
更新
中的
- RegisterController
方法。
- validator
- ...
- protected function validator(array $data)
- {
- return Validator::make($data, [
- 'name' => 'required|max:255',
- 'email' => 'required|email|max:255|unique:users',
- 'password' => 'required|min:6|confirmed',
- 'role' => 'required|exists:roles,id', // validating role
- ]);
- }
- ...
修改
方法。在添加用户时,标记用户角色。
- create
- ...
- protected function create(array $data)
- {
- $user = User::create([
- 'name' => $data['name'],
- 'email' => $data['email'],
- 'password' => bcrypt($data['password']),
- ]);
- $user->roles()->attach($data['role']);
- return $user;
- }
- ...
浏览器地址栏输入: http://localhost/blog/public/ 。看到欢迎页面、注册用户。
修改
文件。
- app/Providers/AuthServiceProvider.php
- use App\Post;...public
- function boot() {
- $this - >registerPolicies();
- $this - >registerPostPolicies();
- }
- public
- function registerPostPolicies() {
- Gate: :define('create-post',
- function($user) {
- return $user - >hasAccess('create-post');
- });
- Gate: :define('update-post',
- function($user, Post $post) {
- return $user - >hasAccess('update-post') or $user - >id == $post - >user_id;
- });
- Gate: :define('publish-post',
- function($user, Post $post) {
- return $user - >hasAccess('publish-post') or $user - >id == $post - >user_id;;
- });
- Gate: :define('delete-post',
- function($user, Post $post) {
- return $user - >hasAccess('delete-post') or $user - >id == $post - >user_id;
- });
- Gate: :define('see-all-drafts',
- function($user) {
- return $user - >inRole('editor');
- });
- }
修改
。
- routes/web.php
- Route::get('/posts', 'PostController@index')->name('list_posts');
- Route::group(['prefix' => 'posts'], function () {
- Route::get('/drafts', 'PostController@drafts')
- ->name('list_drafts')
- ->middleware('auth');
- Route::get('/show/{id}', 'PostController@show')
- ->name('show_post');
- Route::get('/create', 'PostController@create')
- ->name('create_post')
- ->middleware('can:create-post');
- Route::post('/create', 'PostController@store')
- ->name('store_post')
- ->middleware('can:create-post');
- Route::get('/edit/{post}', 'PostController@edit')
- ->name('edit_post')
- ->middleware('can:update-post,post');
- Route::post('/edit/{post}', 'PostController@update')
- ->name('update_post')
- ->middleware('can:update-post,post');
- Route::post('/delete/{post}', 'PostController@destory')
- ->name('delete_post')
- ->middleware('can:delete-post,post');
- // using get to simplify
- Route::get('/publish/{post}', 'PostController@publish')
- ->name('publish_post')
- ->middleware('can:publish-post');
- Route::get('/unpublish/{post}', 'PostController@unpublish')
- ->name('unpublish_post')
- ->middleware('can:publish-post,post');
- });
Model
- Post
- ...
- class Post extends Model
- {
- protected $fillable = [
- 'title', 'slug', 'body', 'user_id',
- ];
- public function owner()
- {
- return $this->belongsTo(User::class);
- }
- public function scopePublished($query)
- {
- return $query->where('published', true);
- }
- public function scopeUnpublished($query)
- {
- return $query->where('published', false);
- }
- }
- PostController
- use App\Post;
- ...
- public function index()
- {
- $posts = Post::published()->paginate();
- return view('posts.index', compact('posts'));
- }
- ...
创建
- resources/views/posts/index.blade.php
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">
- Posts
- @can('create-post')
- <a class="pull-right btn btn-sm btn-primary" href="{{ route('create_post') }}">New</a>
- @endcan
- </div>
- <div class="panel-body">
- <div class="row">
- @foreach($posts as $post)
- <div class="col-sm-6 col-md-4">
- <div class="thumbnail">
- <div class="caption">
- <h3><a href="{{ route('show_post', ['id' => $post->id]) }}">{{ $post->title }}</a></h3>
- <p>{{ str_limit($post->body, 50) }}</p>
- @can('update-post', $post)
- <p>
- <a href="{{ route('edit_post', ['id' => $post->id]) }}" class="btn btn-sm btn-default" role="button">Edit</a>
- </p>
- @endcan
- </div>
- </div>
- </div>
- @endforeach
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
- ...
- public function create()
- {
- return view('posts.create');
- }
- ...
。
- posts\create.blade.php
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">New Post</div>
- <div class="panel-body">
- <form class="form-horizontal" role="form" method="POST" action="{{ route('store_post') }}">
- {{ csrf_field() }}
- <div class="form-group{{ $errors->has('title') ? ' has-error' : '' }}">
- <label for="title" class="col-md-2 control-label">Title</label>
- <div class="col-md-9">
- <input id="title" type="text" class="form-control" name="title" value="{{ old('title') }}" required autofocus>
- @if ($errors->has('title'))
- <span class="help-block">
- <strong>{{ $errors->first('title') }}</strong>
- </span>
- @endif
- </div>
- </div>
- <div class="form-group{{ $errors->has('body') ? ' has-error' : '' }}">
- <label for="body" class="col-md-2 control-label">Body</label>
- <div class="col-md-9">
- <textarea name="body" id="body" cols="30" rows="10" class="form-control" required>{{ old('body') }}</textarea>
- @if ($errors->has('body'))
- <span class="help-block">
- <strong>{{ $errors->first('body') }}</strong>
- </span>
- @endif
- </div>
- </div>
- <div class="form-group">
- <div class="col-md-6 col-md-offset-2">
- <button type="submit" class="btn btn-primary">
- Create
- </button>
- <a href="{{ route('list_posts') }}" class="btn btn-primary">
- Cancel
- </a>
- </div>
- </div>
- </form>
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
- use Auth;
- use App\Http\Requests\StorePost as StorePostRequest;
- ...
- public function store(StorePostRequest $request)
- {
- $data = $request->only('title', 'body');
- $data['slug'] = str_slug($data['title']);
- $data['user_id'] = Auth::user()->id;
- $post = Post::create($data);
- return redirect()->route('edit_post', ['id' => $post->id]);
- }
- ...
- php artisan make:request StorePost
编辑
。
- app/Http/Requests/StorePost.php
- public function authorize()
- {
- return true; // gate will be responsible for access
- }
- public function rules()
- {
- return [
- 'title' => 'required|unique:posts',
- 'body' => 'required',
- ];
- }
- use Gate;
- ...
- public function drafts()
- {
- $postsQuery = Post::unpublished();
- if(Gate::denies('see-all-drafts')) {
- $postsQuery = $postsQuery->where('user_id', Auth::user()->id);
- }
- $posts = $postsQuery->paginate();
- return view('posts.drafts', compact('posts'));
- }
- ...
创建
。
- posts/drafts.blade.php
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">
- Drafts <a class="btn btn-sm btn-default pull-right" href="{{ route('list_posts') }}">Return</a>
- </div>
- <div class="panel-body">
- <div class="row">
- @foreach($posts as $post)
- <div class="col-sm-6 col-md-4">
- <div class="thumbnail">
- <div class="caption">
- <h3><a href="{{ route('show_post', ['id' => $post->id]) }}">{{ $post->title }}</a></h3>
- <p>{{ str_limit($post->body, 50) }}</p>
- <p>
- @can('publish-post', $post)
- <a href="{{ route('publish_post', ['id' => $post->id]) }}" class="btn btn-default" role="button">Publish</a>
- @endcan
- <a href="{{ route('edit_post', ['id' => $post->id]) }}" class="btn btn-default" role="button">Edit</a>
- </p>
- </div>
- </div>
- </div>
- @endforeach
- <div class="col-sm-12 col-md-12">
- {{ $posts->links() }}
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
修改
,添加 "草稿" 菜单。
- layouts/app.blade.php
- ...
- <ul class="dropdown-menu" role="menu">
- <li>
- <a href="{{ route('list_drafts') }}">Drafts</a>
- ...
- use App\Http\Requests\UpdatePost as UpdatePostRequest;
- ...
- public function edit(Post $post)
- {
- return view('posts.edit', compact('post'));
- }
- public function update(Post $post, UpdatePostRequest $request)
- {
- $data = $request->only('title', 'body');
- $data['slug'] = str_slug($data['title']);
- $post->fill($data)->save();
- return back();
- }
- php artisan make:request UpdatePost
编辑
- app/Http/Requests/UpdatePost.php
- use Illuminate\Validation\Rule;
- ...
- public function authorize()
- {
- return true;
- }
- public function rules()
- {
- $id = $this->route('post')->id;
- return [
- 'title' => [
- 'required',
- Rule::unique('posts')->where('id', '<>', $id),
- ],
- 'body' => 'required',
- ];
- }
创建视图
。
- posts/edit.blade.php
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">Update Post</div>
- <div class="panel-body">
- <form class="form-horizontal" role="form" method="POST" action="{{ route('update_post', ['post' => $post->id]) }}">
- {{ csrf_field() }}
- <div class="form-group{{ $errors->has('title') ? ' has-error' : '' }}">
- <label for="title" class="col-md-2 control-label">Title</label>
- <div class="col-md-9">
- <input id="title" type="text" class="form-control" name="title" value="{{ old('title', $post->title) }}" required autofocus>
- @if ($errors->has('title'))
- <span class="help-block">
- <strong>{{ $errors->first('title') }}</strong>
- </span>
- @endif
- </div>
- </div>
- <div class="form-group{{ $errors->has('body') ? ' has-error' : '' }}">
- <label for="body" class="col-md-2 control-label">Body</label>
- <div class="col-md-9">
- <textarea name="body" id="body" cols="30" rows="10" class="form-control" required>{{ old('body', $post->body) }}</textarea>
- @if ($errors->has('body'))
- <span class="help-block">
- <strong>{{ $errors->first('body') }}</strong>
- </span>
- @endif
- </div>
- </div>
- <div class="form-group">
- <div class="col-md-6 col-md-offset-2">
- <button type="submit" class="btn btn-primary">
- Update
- </button>
- @can('publish-post')
- @if (!$post->published)
- <a href="{{ route('publish_post', ['post' => $post->id]) }}" class="btn btn-primary">
- Publish
- </a>
- @else
- <a href="{{ route('unpublish_post', ['post' => $post->id]) }}" class="btn btn-primary">
- Unpublish
- </a>
- @endif
- @endcan
- <a href="{{ route('list_posts') }}" class="btn btn-primary">
- Cancel
- </a>
- </div>
- </div>
- </form>
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
为
添加方法
- PostController
和
- publish
。
- unpublish
- ...
- public function publish(Post $post)
- {
- $post->published = true;
- $post->save();
- return back();
- }
- public function unpublish(Post $post)
- {
- $post->published = false;
- $post->save();
- return back();
- }
- ...
- public function show(Post $post)
- {
- $post = Post::findOrFail($id);
- if ($post->published || $post->user_id == Auth::user()->id) {
- return view('posts.show', compact('post'));
- }
- abort(403, 'Unauthorized.');
- }
创建
。
- posts/show.blade.php
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">
- {{ $post->title }}
- <a class="btn btn-sm btn-default pull-right" href="{{ route('list_posts') }}">Return</a>
- </div>
- <div class="panel-body">
- {{ $post->body }}
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
修改
,添加
- PostController
方法。
- destory
- public function destory(Post $post)
- {
- $post->delete();
- return redirect()->route('list_post');;
- }
修改
,添加删除按钮。
- posts/edit.blade.php
- ...
- <div class="panel-heading">Update Post
- @can('delete-post', $post)
- <a class="pull-right btn btn-sm btn-danger" href="{{ route('delete_post', ['id' => $post->id]) }}"
- onclick="if(confirm('确定删除吗?') === false) { return false; } else {
- event.preventDefault(); document.getElementById('delete-post-form').submit();}">
- 删除
- </a>
- <form id="delete-post-form" action="{{ route('delete_post', ['post' => $post->id]) }}" method="POST" style="display: none;">
- {{ csrf_field() }}
- </form>
- @endcan
- </div>
- ...
在
下新建
- resources/views
目录,再在该目录下新建
- errors
和
- 404.blade.php
页面。
- 403.blade.php
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">403</div>
- <div class="panel-body">
- <h2>This Action is Unauthorized!</h2>
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
- @extends('layouts.app')
- @section('content')
- <div class="container">
- <div class="row">
- <div class="col-md-8 col-md-offset-2">
- <div class="panel panel-default">
- <div class="panel-heading">404</div>
- <div class="panel-body">
- <h2>Not Found</h2>
- </div>
- </div>
- </div>
- </div>
- </div>
- @endsection
这里要安装依赖包
,使用它将 Markdown 文本装换为 HTML。
- erusev/parsedown
- composer require erusev/parsedown
修改
- posts/show.blade.php
- <div class="panel-body">
- {!! Parsedown::instance()->text($post->body) !!}
- </div>
此刻,你就可以使用 Markdown 写博客了。
- Laravel
- 项目
来源: http://www.cnblogs.com/zhangbao/p/6807950.html