[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 表單")和顯示已存在任務的列表。為了讓你更直觀的查看該視圖,我們將已完成的應用截圖如下:
[](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
- 任務調度
- 測試
- 驗證
- 新手入門指南
- 簡單任務管理系統
- 帶用戶功能的任務管理系統