[TOC]
### **1、简介**
本进阶指南提供了对[Laravel](http://laravelacademy.org/tags/laravel "View all posts in Laravel")框架更深入的介绍,包括数据库迁移、Eloquent ORM、[路由](http://laravelacademy.org/tags/%e8%b7%af%e7%94%b1 "View all posts in 路由")、[认证](http://laravelacademy.org/tags/%e8%ae%a4%e8%af%81 "View all posts in 认证")、[授权](http://laravelacademy.org/tags/%e6%8e%88%e6%9d%83 "View all posts in 授权")、[依赖注入](http://laravelacademy.org/tags/%e4%be%9d%e8%b5%96%e6%b3%a8%e5%85%a5 "View all posts in 依赖注入")、[验证](http://laravelacademy.org/tags/%e9%aa%8c%e8%af%81 "View all posts in 验证")、[视图](http://laravelacademy.org/tags/%e8%a7%86%e5%9b%be "View all posts in 视图")以及Blade模板。如果你对Laravel框架或其他PHP框架已经有了基本的认识,本章节将是你新的起点,如果你完全还是新手,请从[新手入门指南](http://laravelacademy.org/post/3291.html)开始。
本节的示例仍然是构建一个任务系统,但是在上一节基础上,本任务系统将允许用户注册登录,同样完整的代码已经放到GitHub上:[https://github.com/laravel/quickstart-intermediate](https://github.com/laravel/quickstart-intermediate)。
### **2、安装**
##### **安装Laravel**
首先你需要安装一个新的Laravel应用。你可以使用[Homestead虚拟机](http://laravelacademy.org/post/3291.html)或者本地PHP开发环境来运行应用。开发环境准备完毕后,可以使用Composer来安装应用:
~~~
composer create-project laravel/laravel quickstart --prefer-dist
~~~
##### **安装Quickstart项目**
你可以继续往下阅读,也可以选择去GitHub下载项目源码并在本地运行:
~~~
git clone https://github.com/laravel/quickstart-intermediate quickstart
cd quickstart
composer install
php artisan migrate
~~~
关于构建本地开发环境的详细[文档](http://laravelacademy.org/tags/%e6%96%87%e6%a1%a3 "View all posts in 文档")可查看[Homestead](http://laravelacademy.org/post/3291.html)和[安装](http://laravelacademy.org/post/2720.html)文档。
### **3、准备好数据库**
#### **3.1 数据库迁移**
首先,我们使用迁移来定于处理所有任务的数据库。Laravel的数据库迁移使用平滑、优雅的PHP代码来提供一个简单的方式定义和修改数据表结构。团队成员们无需在本地数据库手动添加或删除列,只需要简单运行你提交到源码控制系统中的迁移即可。
##### **users表**
由于我们允许用户注册,所以需要一张用来存储用户的表。幸运的是 Laravel已经自带了这个迁移用于创建基本的users表,我们不需要手动生成。该迁移文件默认位于`database/migrations`目录下。
##### **tasks表**
接下来,让我们来创建用于处理所有任务的数据表`tasks`。我们使用Artisan命令`make:migration`来为`tasks`生成一个新的数据库迁移:
~~~
php artisan make:migration create_tasks_table --create=tasks
~~~
生成的新迁移文件位于`database/migrations`目录下。你可能已经注意到了,`make:migration`命令已经在迁移文件中添加了自增ID和时间戳。我们将编辑该文件添加更多的字段到任务表`tasks`:
~~~
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTasksTable extends Migration{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->index();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('tasks');
}
}
~~~
其中,`user_id`用于建立`tasks`表与`users`表之间的关联。
要运行迁移,可以使用`migrate`命令。如果你使用的是Homestead,需要在虚拟机中运行该命令,因为你的主机不能直接访问Homestead上的数据库:
~~~
php artisan migrate
~~~
该命令将会创建迁移中定义的尚未创建的所有数据表。如果你使用MySQL客户端(如Navicat For MySQL)查看数据表,你将会看到新的`users`表和`tasks`表。下一步,我们将要定义Eloquent ORM[模型](http://laravelacademy.org/tags/%e6%a8%a1%e5%9e%8b "View all posts in 模型")。
#### **3.2 Eloquent模型**
Eloquent是Laravel默认的ORM,Eloquent使用“模型”这一概念使得从数据库存取数据变得轻松。通常,每个Eloquent模型都对应一张数据表。
##### **User模型**
首先,我们一个与`users`表相对应的模型`User`。Laravel已经自带了这一模型`app/User`,所以我们不需要重复创建了。
##### **Task模型**
接下来,我们来定义与`tasks`表相对应的模型Task。同样,我们使用Artisan命令来生成模型类,在本例中,我们使用`make:model`命令:
~~~
php artisan make:model Task
~~~
该模型位于应用的`app`目录下,默认情况下,该模型类是空的。我们不需要明确告诉Eloquent模型对应哪张表,Laravel底层会有一个映射规则,这一点在之前[Eloquent文档](http://laravelacademy.org/post/2995.html)已有说明,按照规则,这里`Task`模型默认对应`tasks`表。
接下来,让我们在`Task`模型类中加一些代码。首先,我们声明模型上的name属性支持“批量赋值”(关于批量赋值说明可查看[这篇文章](http://laravelacademy.org/post/984.html#ipt_kb_toc_984_3)):
~~~
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
}
~~~
我们将在后续添加路由到应用中学习更多如何使用Eloquent模型。当然,你也可以先去查看完整的[Eloquent文档](http://laravelacademy.org/post/2995.html)了解更多。
#### **3.3 Eloquent[关联关系](http://laravelacademy.org/tags/%e5%85%b3%e8%81%94%e5%85%b3%e7%b3%bb "View all posts in 关联关系")**
现在,模型已经定义好了,我们需要将它们关联起来。例如,一个`User`实例对应多个`Task`实例,而一个`Task`实例从属于某个`User`。定义关联关系后将允许我们更方便的获取关联模型:
~~~
$user = App\User::find(1);
foreach ($user->tasks as $task) {
echo $task->name;
}
~~~
##### **tasks关联关系**
首先,我们在`User`模型中定义`tasks`关联关系。Eloquent关联关系被定义成模型的方法,并且支持多种不同的关联关系类型(查看完整的[Eloquent关联关系文档](http://laravelacademy.org/post/3011.html)了解更多)。在本例中,我们将会在`User`模型中定义`tasks`方法并在其中调用Eloquent提供的`hasMany`方法:
~~~
<?php
namespace App;
// Namespace Imports...
class User extends Model implements AuthenticatableContract,
AuthorizableContract,CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
// Other Eloquent Properties...
/**
* Get all of the tasks for the user.
*/
public function tasks()
{
return $this->hasMany(Task::class);
}
}
~~~
##### **user关联关系**
接下来,我们会在`Task`模型中定义`user`关联关系。同样,我们将其定义为模型的方法。在本例中,我们使用Eloquent提供的`belongsTo`方法来定义该关联关系:
~~~
<?php
namespace App;
use App\User;
use Illuminate\Database\Eloquent\Model;
class Task extends Model{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
/**
* Get the user that owns the task.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
~~~
好极了!现在我们已经定义好了关联关系,接下来可以正式开始创建控制器了!
### **4、路由**
在[新手入门指南](http://laravelacademy.org/post/3291.html)创建的任务管理系统中,我们在`routes.php`中使用闭包定义所有的业务逻辑。而实际上,大部分应用都会使用[控制器](http://laravelacademy.org/post/2816.html)来组织路由。
#### **4.1 显示视图**
我们还保留一个路由使用闭包:/路由,该路由是用于展示给游客的引导页,我们将在该路由中渲染欢迎页面。
在Laravel中,所有的HTML模板都位于`resources/views`目录,并且我们使用`view`函数从路由中返回其中一个模板:
~~~
Route::get('/', function () {
return view('welcome');
});
~~~
当然,我们需要创建这个视图,稍后就会。
#### **4.2 用户认证**
此外,我们还要让用户注册并登录到应用。通常,在web应用中构建整个登录认证层是一件相当冗长乏味的工作,然而,由于它是一个如此通用的需求,Laravel试图将这一过程变得简单而轻松。
首先,注意到新安装的Laravel应用中已经包含了`app/Http/Controllers/AuthController`这个控制器,该控制器中使用了一个特殊的`AuthenticatesAndRegistersUsers` trait,而这个trait中包含了用户注册登录的所必须的相关逻辑。
##### **认证路由&视图**
那么接下来我们该怎么做呢?我们仍然需要创建注册和登录模板并定义指向认证控制器`AuthController`的路由。我们可以通过Artisan命令make:auth来完成所有工作:
`php artisan make:auth --views`
注:如果你想要查看这些视图的完整示例,可以去下载相应的GitHub项目:[https://github.com/laravel/quickstart-intermediate](https://github.com/laravel/quickstart-intermediate)
接下来,我们还要添加认证路由到路由文件,我们可以通过使用Route门面上的auth方法来实现这一目的,该方法会注册我们所需的所有认证路由,包括注册、登录和密码重置:
`// Authentication Routes...`
Route::auth();
#### **4.3 任务控制器**
由于我们需要获取和保存任务,所以还需要使用Artisan命令创建一个`TaskController`,生成的控制器位于`app/Http/Controllers`目录:
~~~
php artisan make:controller TaskController
~~~
现在这个控制器已经生成了,让我们去`app/Http/routes.php`中定义一些指向该控制器的路由吧:
~~~
Route::get('/tasks', 'TaskController@index');
Route::post('/task', 'TaskController@store');
Route::delete('/task/{task}', 'TaskController@destroy');
~~~
##### **设置所有任务路由需要登录才能访问**
对本应用而言,我们想要所有任务需要登录用户才能访问,换句话说,用户必须登录到系统才能创建新任务。所以,我们需要限制访问任务路由的用户为登录用户。Laravel使用[中间件](http://laravelacademy.org/post/2803.html)来处理这种限制。
如果要限制登录用户才能访问该控制器的所有动作,可以在控制器的构造函数中添加对`middleware`方法的调用。所有有效的路由[中间件](http://laravelacademy.org/tags/%e4%b8%ad%e9%97%b4%e4%bb%b6 "View all posts in 中间件")都定义在app/Http/Kernel.php文件中。在本例中,我们想要定义一个`auth`中间件到`TaskController`上的所有动作:
~~~
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TaskController extends Controller{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}
~~~
### **5、创建布局&视图**
本应用仍然只有一个视图,该视图包含了用于添加新任务的[表单](http://laravelacademy.org/tags/%e8%a1%a8%e5%8d%95 "View all posts in 表单")和显示已存在任务的列表。为了让你更直观的查看该视图,我们将已完成的应用截图如下:
[![basic-overview](http://laravelacademy.org/wp-content/uploads/2015/10/basic-overview.png)](http://laravelacademy.org/wp-content/uploads/2015/10/basic-overview.png)
#### **5.1 定义布局**
几乎所有的web应用都会在不同界面共享同一布局,例如,本应用顶部的导航条将会在每个页面显示。Laravel使用Blade让不同页面共享这些公共特性变得简单。
正如我们之前讨论的,所有Laravel视图都存放在`resources/views`中,因此,我们在`resources/views/layouts/app.blade.php`中定义一个新的布局视图,`.blade.php`扩展表明框架使用Blade模板引擎来渲染视图,当然,你可以使用原生的PHP模板,然而,Blade提供了的标签语法可以帮助我们编写更加清爽、简短的模板。
编辑app.blade.php内容如下:
~~~
// resources/views/layouts/app.blade.php
<!DOCTYPE html><html lang="en">
<head>
<title>Laravel Quickstart - Advanced</title>
<!-- CSS And JavaScript -->
</head>
<body>
<div class="container">
<nav class="navbar navbar-default">
<!-- Navbar Contents -->
</nav>
</div>
@yield('content')
</body>
</html>
~~~
注意布局中的`@yield('content')`部分,这是一个Blade指令,用于指定继承布局的子页面在这里可以注入自己的内容。接下来,我们来定义使用该布局的子视图来提供主体内容。
#### **[5.2](http://laravelacademy.org/tags/5-2 "View all posts in 5.2") 定义子视图**
好了,我们已经创建了应用的布局视图,下面我们需要定义一个包含创建新任务的表单和已存在任务列表的视图,该视图文件存放在`resources/views/tasks.blade.php`,对应`TaskController`中的`index`方法。
我们将跳过Bootstrap CSS的样板文件而只专注在我们所关注的事情上,不要忘了,你可以从[GitHub](https://github.com/laravel/quickstart-intermediate)下载本应用的所有资源:
~~~
// resources/views/tasks.blade.php
@extends('layouts.app')
@section('content')
<!-- Bootstrap Boilerplate... -->
<div class="panel-body">
<!-- Display Validation Errors -->
@include('common.errors')
<!-- New Task Form -->
<form action="/task" method="POST" class="form-horizontal">
{{ csrf_field() }}
<!-- Task Name -->
<div class="form-group">
<label for="task" class="col-sm-3 control-label">Task</label>
<div class="col-sm-6">
<input type="text" name="name" id="task-name" class="form-control">
</div>
</div>
<!-- Add Task Button -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-default">
<i class="fa fa-plus"></i> Add Task
</button>
</div>
</div>
</form>
</div>
<!-- TODO: Current Tasks -->
@endsection
~~~
##### **一些需要解释的地方**
在继续往下之前,让我们简单谈谈这个模板。首先,我们使用`@extends`指令告诉Blade我们要使用定义在`resources/views/layouts/app.blade.php`的布局,所有`@section('content')`和`@endsection`之间的内容将会被注入到`app.blade.php`布局的`@yield('contents')`指令位置。
@include(‘common.errors’)指令会加载 resources/views/common/errors.blade.php模板,我们后续会定义这个模板。
现在,我们已经为应用定义了基本的布局和视图,然后我们回到`TaskController`的`index`方法:
~~~
/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request){
return view('tasks.index');
}
~~~
接下来,让我们继续添加代码到`POST /task`路由的控制器方法来处理表单输入并添加新任务到数据库。
### **6、添加任务**
#### **6.1 验证表单输入**
现在我们已经在视图中定义了表单,接下来需要编写代码到`TaskController@store`方法来处理表单请求并创建一个新任务。
对这个表单而言,我们将`name`字段设置为必填项,而且长度不能超过255个字符。如果表单验证失败,将会跳转到`/tasks`页面,并且将错误信息存放到一次性session中:
~~~
/**
* Create a new task.
*
* @param Request $request
* @return Response
*/
public function store(Request $request){
$this->validate($request, [
'name' => 'required|max:255',
]);
// Create The Task...
}
~~~
如果你已经看过新手入门[教程](http://laravelacademy.org/tags/%e6%95%99%e7%a8%8b "View all posts in 教程"),那么你可能会注意到这里的验证代码与之前大不相同,这是因为我们现在在控制器中,可以方便地调用`ValidatesRequests` trait(包含在Laravel控制器基类中)提供的`validate`方法。
我们甚至不需要手动判读是否验证失败然后重定向。如果验证失败,用户会自动被重定向到来源页面,而且错误信息也会被存放到一次性Session中。简直太棒了,有木有!
##### **$errors变量**
我们在视图中使用了`@include('common.errors')`指令来渲染表单验证错误信息,`common.errors`允许我们在所有页面以统一格式显示错误信息。我们定义`common.errors`内容如下:
~~~
// resources/views/common/errors.blade.php
@if (count($errors) > 0)
<!-- Form Error List -->
<div class="alert alert-danger">
<strong>Whoops! Something went wrong!</strong>
<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
~~~
> 注:`$errors`变量在每个Laravel视图中都可以访问,如果没有错误信息的话它就是一个空的`ViewErrorBag`实例。
#### **6.2 创建任务**
现在输入验证已经做好了,接下来正式开始创建一个新任务。一旦新任务创建成功,页面会跳转到`/tasks`。要创建任务,可以借助Eloquent模型的关联关系。
大部分Laravel的关联关系提供了`save`方法,该方法接收一个关联模型实例并且会在保存到数据库之前自动设置外键值到关联模型上。在本例中,`save`方法会自动将当前用户登录认证用户的ID赋给给给定任务的`user_id`属性。我们通过`$request->user()`获取当前登录用户实例:
~~~
/**
* Create a new task.
*
* @param Request $request
* @return Response
*/
public function store(Request $request){
$this->validate($request, [
'name' => 'required|max:255',
]);
$request->user()->tasks()->create([
'name' => $request->name,
]);
return redirect('/tasks');
}
~~~
很好,到了这里,我们已经可以成功创建任务,接下来,我们继续添加代码到视图来显示所有任务列表。
### **7、显示已存在的任务**
首先,我们需要编辑`TaskController@index`传递所有已存在任务到视图。`view`函数接收一个数组作为第二个参数,我们可以将数据通过该数组传递到视图中:
~~~
/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request){
$tasks = Task::where('user_id', $request->user()->id)->get();
return view('tasks.index', [
'tasks' => $tasks,
]);
}
~~~
这里,我们还要讲讲Laravel的依赖注入,这里我们将`TaskRepository`注入到`TaskController`,以方便对`Task`模型所有数据的访问和使用。
#### **7.1 依赖注入**
Laravel的[服务容器](http://laravelacademy.org/post/2910.html)是整个框架中最重要的特性,在看完快速入门教程后,建议去研习下服务容器的文档。
##### **创建Repository**
正如我们之前提到的,我们想要定义一个`TaskRepository`来处理所有对`Task`模型的数据访问,随着应用的增长当你需要在应用中共享一些Eloquent查询时这就变得特别有用。
因此,我们创建一个`app/Repositories`目录并在其中创建一个`TaskRepository`类。记住,Laravel项目的`app`文件夹下的所有目录都使用 PSR-4 自动加载标准被自动加载,所以你可以在其中随心所欲地创建需要的目录:
~~~
<?php
namespace App\Repositories;
use App\User;
use App\Task;
class TaskRepository{
/**
* Get all of the tasks for a given user.
*
* @param User $user
* @return Collection
*/
public function forUser(User $user)
{
return Task::where('user_id', $user->id)
->orderBy('created_at', 'asc')
->get();
}
}
~~~
##### **注入Repository**
Repository创建好了之后,可以简单通过在`TaskController`的构造函数中以类型提示的方式注入该Repository,然后就可以在`index`方法中使用 —— 由于Laravel使用容器来解析所有控制器,所以我们的依赖会被自动注入到控制器实例:
~~~
<?php
namespace App\Http\Controllers;
use App\Task;use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Repositories\TaskRepository;
class TaskController extends Controller{
/**
* The task repository instance.
*
* @var TaskRepository
*/
protected $tasks;
/**
* Create a new controller instance.
*
* @param TaskRepository $tasks
* @return void
*/
public function __construct(TaskRepository $tasks)
{
$this->middleware('auth');
$this->tasks = $tasks;
}
/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index', [
'tasks' => $this->tasks->forUser($request->user()),
]);
}
}
~~~
#### **7.2 显示任务**
数据被传递到视图后,我们可以在`tasks/index.blade.php`中以表格形式显示所有任务。Blade中使用`@foreach`处理循环数据:
~~~
@extends('layouts.app')
@section('content')
<!-- Create Task Form... -->
<!-- Current Tasks -->
@if (count($tasks) > 0)
<div class="panel panel-default">
<div class="panel-heading">
Current Tasks
</div>
<div class="panel-body">
<table class="table table-striped task-table">
<!-- Table Headings -->
<thead>
<th>Task</th>
<th> </th>
</thead>
<!-- Table Body -->
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- Task Name -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<td>
<!-- TODO: Delete Button -->
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@endsection
~~~
至此,本应用基本完成。但是,当任务完成时我们还没有途径删除该任务,接下来我们就来处理这件事。
### **8、删除任务**
#### **8.1 添加删除按钮**
我们在`tasks/index.blade.php`视图中留了一个“TODO”注释用于放置删除按钮。当删除按钮被点击时,`DELETE /task`请求被发送到应用后台并触发`TaskController@destroy`方法:
~~~
<tr>
<!-- Task Name -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<!-- Delete Button -->
<td>
<form action="/task/{{ $task->id }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button>Delete Task</button>
</form>
</td>
</tr>
~~~
##### **关于方法伪造**
尽管我们使用的路由是`Route::delete`,但我们在删除按钮表单中使用的请求方法为`POST`,HTML表单只支持GET和POST两种请求方式,因此我们需要使用某种方式来伪造`DELETE`请求。
我们可以在表单中通过输出`method_field('DELETE')`来伪造`DELETE`请求,该函数生成一个隐藏的表单输入框,然后Laravel识别出该输入并使用其值覆盖实际的HTTP请求方法。生成的输入框如下:
~~~
<input type="hidden" name="_method" value="DELETE">
~~~
#### **8.2 路由模型绑定**
现在,我们准备在`TaskController`中定义`destroy`方法,但是,在此之前,让我们回顾下路由中对删除任务的定义:
~~~
Route::delete('/task/{task}', 'TaskController@destroy');
~~~
对应控制器TaskController中删除方法destroy定义如下:
~~~
/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
~~~
由于路由中的`{task}`变量与控制器方法中的`$task`变量相匹配,Laravel的[隐式模型绑定](http://laravelacademy.org/post/2784.html#route-model-binding)特性将会自动注入相应的`Task`模型实例到`destroy`方法中。
#### **8.3 用户授权**
现在,我们已经将`Task`实例注入到`destroy`方法;然而,我们并不能保证当前登录认证用户是给定任务的实际拥有人。例如,一些恶意请求可能尝试通过传递随机任务ID到`/tasks/{task}`链接删除另一个用户的任务。因此,我们需要使用Laravel的授权功能来确保当前登录用户拥有对注入到路由中的`Task`实例进行删除的权限。
##### **创建Policy**
Laravel使用“策略”来将授权逻辑组织到单个类中,通常,每个策略都对应一个模型,因此,让我们使用Artisan命令创建一个`TaskPolicy`,生成的文件位于`app/Policies/TaskPolicy.php`:
~~~
php artisan make:policy TaskPolicy
~~~
接下来,让我们添加`destroy`方法到策略中,该方法会获取一个`User`实例和一个`Task`实例。该方法简单检查用户ID和任务的`user_id`值是否相等。实际上,所有的策略方法都会返回`true`或`false`:
~~~
<?php
namespace App\Policies;
use App\User;
use App\Task;
use Illuminate\Auth\Access\HandlesAuthorization;
class TaskPolicy{
use HandlesAuthorization;
/**
* Determine if the given user can delete the given task.
*
* @param User $user
* @param Task $task
* @return bool
*/
public function destroy(User $user, Task $task)
{
return $user->id === $task->user_id;
}
}
~~~
最后,我们需要关联`Task`模型和`TaskPolicy`,这可以通过在`app/Providers/AuthServiceProvider.php`文件的`policies`属性中添加注册来实现,注册后会告知Laravel无论何时我们尝试授权动作到`Task`实例时该使用哪个策略类进行判断:
~~~
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Task' => 'App\Policies\TaskPolicy',
];
~~~
##### **授权动作**
现在我们编写好了策略,让我们在`destroy`方法中使用它。所有的Laravel控制器中都可以调用`authorize`方法,该方法由`AuthorizesRequest` trait提供:
~~~
/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task){
$this->authorize('destroy', $task);
// Delete The Task...
}
~~~
我们可以检查下该方法调用:传递给`authorize`方法的第一个参数是我们想要调用的策略方法名,第二个参数是当前操作的模型实例。由于我们在之前告诉过Laravel,`Task`模型对应的策略类是`TaskPolicy`,所以框架知道触发哪个策略类上的`destroy`方法。当前用户实例会被自动传递到策略方法,所以我们不需要手动传递。
如果授权成功,代码会继续执行。如果授权失败,会抛出一个403异常并显示一个错误页面给用户。
> 注:除此之外,Laravel还提供了其它授权服务实现方式,可以查看[授权文档](http://laravelacademy.org/post/577.html)了解更多。
#### **8.4 删除任务**
最后,让我们添加业务逻辑到路由中执行删除操作,我们可以使用Eloquent提供的`delete`方法从数据库中删除给定的模型实例。记录被删除后,跳转到`/tasks`页面:
~~~
/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task){
$this->authorize('destroy', $task);
$task->delete();
return redirect('/tasks');
}
~~~
- 序言
- 发行版本说明
- 升级指南
- 贡献代码
- 开始
- 安装
- 配置
- Laravel Homestead
- 基础
- HTTP 路由
- HTTP 中间件
- HTTP 控制器
- HTTP 请求
- HTTP 响应
- 视图
- Blade 模板引擎
- 架构
- 一次请求的生命周期
- 应用目录结构
- 服务提供者
- 服务容器
- 门面(Facades)
- 数据库
- 起步
- 查询构建器
- 迁移
- 填充数据
- Eloquent ORM
- 起步
- 关联关系
- 集合
- 访问器&修改器
- 序列化
- 服务
- 用户认证
- 用户授权
- Artisan Console
- 订阅支付实现:Laravel Cashier
- 缓存
- 集合
- 集成前端资源:Laravel Elixir
- 加密
- 错误&日志
- 事件
- 文件系统/云存储
- 哈希
- 辅助函数
- 本地化
- 邮件
- 包开发
- 分页
- Redis
- 队列
- Session
- Envoy Task Runner
- 任务调度
- 测试
- 验证
- 新手入门指南
- 简单任务管理系统
- 带用户功能的任务管理系统