鍍金池/ 教程/ PHP/ 帶用戶功能的任務管理系統(tǒng)
門面
Laravel Homestead
安裝及配置
測試
HTTP 中間件
加密
升級指南
幫助函數(shù)
應用目錄結構
集合
新手入門指南-簡單任務管理系統(tǒng)
任務調度
查詢構建器
視圖
驗證
Laravel Cashier(訂購&支付&發(fā)票)
本地化
隊列
調整器
分頁
文件系統(tǒng)/云存儲
貢獻代碼
哈希
HTTP 控制器
緩存
遷移
HTTP 請求
Laravel Elixir
發(fā)行版本說明
Envoy 任務運行器(SSH任務)
序列化
Session
起步
帶用戶功能的任務管理系統(tǒng)
起步
用戶授權
郵件
事件
填充數(shù)據(jù)
HTTP 路由
服務提供者
Blade 模板引擎
包開發(fā)
用戶認證
Artisan 控制臺
HTTP 響應
集合
服務容器
關聯(lián)關系
一次請求的生命周期
契約
Redis
錯誤&日志

帶用戶功能的任務管理系統(tǒng)

本進階指南提供了對 Laravel 框架更深入的介紹,包括數(shù)據(jù)庫遷移、Eloquent ORM、路由、認證、授權、依賴注入、驗證、視圖以及 Blade 模板。如果你對 Laravel 框架或其他 PHP 框架已經有了基本的認識,本章節(jié)將是你新的起點,如果你完全還是新手,請從新手入門指南開始。

本節(jié)的示例仍然是構建一個任務系統(tǒng),但是在上一節(jié)基礎上,本任務系統(tǒng)將允許用戶注冊登錄,同樣完整的代碼已經放到 GitHub 上:https://github.com/laravel/quickstart-intermediate。

1、安裝

當然,首先你需要安裝一個新的 Laravel 應用。你可以使用 Homestead 虛擬機或者本地 PHP 開發(fā)環(huán)境來運行應用。開發(fā)環(huán)境準備完畢后,可以使用 Composer 來安裝應用:

composer create-project laravel/laravel quickstart --prefer-dist

你可以繼續(xù)往下閱讀,也可以選擇去 GitHub 下載項目源碼并在本地運行:

git clone https://github.com/laravel/quickstart-intermediate quickstart
cd quickstart
composer install
php artisan migrate

關于構建本地開發(fā)環(huán)境的詳細文檔可查看 Homestead 和安裝文檔。

2、準備好數(shù)據(jù)庫

數(shù)據(jù)庫遷移

首先,我們使用遷移來定于處理所有任務的數(shù)據(jù)庫。Laravel 的數(shù)據(jù)庫遷移使用平滑、優(yōu)雅的 PHP 代碼來提供一個簡單的方式定義和修改數(shù)據(jù)表結構。團隊成員們無需在本地數(shù)據(jù)庫手動添加或刪除列,只需要簡單運行你提交到源碼控制系統(tǒng)中的遷移即可。

users 表

由于我們允許用戶注冊,所以需要一張用來存儲用戶的表。幸運的是 Laravel 已經自帶了這個遷移用于創(chuàng)建基本的 users 表,我們不需要手動生成。該遷移文件默認位于 database/migrations 目錄下。

tasks 表

接下來,讓我們來創(chuàng)建用于處理所有任務的數(shù)據(jù)表 tasks。我們使用 Artisan 命令 make:migration 來為 tasks 生成一個新的數(shù)據(jù)庫遷移:

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 表之間的關聯(lián)。

要運行遷移,可以使用 migrate 命令。如果你使用的是 Homestead,需要在虛擬機中運行該命令,因為你的主機不能直接訪問 Homestead 上的數(shù)據(jù)庫:

php artisan migrate

該命令將會創(chuàng)建遷移中定義的尚未創(chuàng)建的所有數(shù)據(jù)表。如果你使用 MySQL 客戶端(如 Navicat For MySQL)查看數(shù)據(jù)表,你將會看到新的 users 表和 tasks 表。下一步,我們將要定義 Eloquent ORM模型。

Eloquent 模型

Eloquent 是 Laravel 默認的 ORM,Eloquent 使用“模型”這一概念使得從數(shù)據(jù)庫存取數(shù)據(jù)變得輕松。通常,每個 Eloquent 模型都對應一張數(shù)據(jù)表。

User 模型

首先,我們一個與 users 表相對應的模型 User。Laravel 已經自帶了這一模型 app/User,所以我們不需要重復創(chuàng)建了。

Task 模型

接下來,我們來定義與 tasks 表相對應的模型 Task。同樣,我們使用 Artisan 命令來生成模型類,在本例中,我們使用 make:model 命令:

php artisan make:model Task

該模型位于應用的 app 目錄下,默認情況下,該模型類是空的。我們不需要明確告訴 Eloquent 模型對應哪張表,Laravel 底層會有一個映射規(guī)則,這一點在之前 Eloquent 文檔已有說明,按照規(guī)則,這里 Task 模型默認對應 tasks 表。

接下來,讓我們在 Task 模型類中加一些代碼。首先,我們聲明模型上的 name 屬性支持“批量賦值”(關于批量賦值說明可查看這篇文章):

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

我們將在后續(xù)添加路由到應用中學習更多如何使用 Eloquent 模型。當然,你也可以先去查看完整的 Eloquent 文檔了解更多。

Eloquent 關聯(lián)關系

現(xiàn)在,模型已經定義好了,我們需要將它們關聯(lián)起來。例如,一個 User 實例對應多個 Task 實例,而一個 Task 實例從屬于某個 User。定義關聯(lián)關系后將允許我們更方便的獲取關聯(lián)模型:

$user = App\User::find(1);

foreach ($user->tasks as $task) {
    echo $task->name;
}

tasks 關聯(lián)關系

首先,我們在 User 模型中定義 tasks 關聯(lián)關系。Eloquent 關聯(lián)關系被定義成模型的方法,并且支持多種不同的關聯(lián)關系類型(查看完整的 Eloquent 關聯(lián)關系文檔了解更多)。在本例中,我們將會在 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 關聯(lián)關系

接下來,我們會在 Task 模型中定義 user 關聯(lián)關系。同樣,我們將其定義為模型的方法。在本例中,我們使用 Eloquent 提供的 belongsTo 方法來定義該關聯(lián)關系:

<?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);
    }
}

好極了!現(xiàn)在我們已經定義好了關聯(lián)關系,接下來可以正式開始創(chuàng)建控制器了!

3、路由

在新手入門指南創(chuàng)建的任務管理系統(tǒng)中,我們在 routes.php 中使用閉包定義所有的業(yè)務邏輯。而實際上,大部分應用都會使用控制器來組織路由。

顯示視圖

我們還保留一個路由使用閉包:/路由,該路由是用于展示給游客的引導頁,我們將在該路由中渲染歡迎頁面。

在 Laravel 中,所有的 HTML 模板都位于 resources/views 目錄,并且我們使用 view 函數(shù)從路由中返回其中一個模板:

Route::get('/', function () {
    return view('welcome');
});

當然,我們需要創(chuàng)建這個視圖,稍后就會。

登錄認證

此外,我們還要讓用戶注冊并登錄到應用。通常,在 web 應用中構建整個登錄認證層是一件相當冗長乏味的工作,然而,由于它是一個如此通用的需求,Laravel 試圖將這一過程變得簡單而輕松。

首先,注意到新安裝的 Laravel 應用中已經包含了 app/Http/Controllers/AuthController 這個控制器,該控制器中使用了一個特殊的 AuthenticatesAndRegistersUsers trait,而這個 trait 中包含了用戶注冊登錄的所必須的相關邏輯。

認證路由

那么接下來我們該怎么做呢?我們仍然需要創(chuàng)建注冊和登錄模板并定義指向認證控制器 AuthController 的路由。首先,添加我們需要的路由到 app/Http/routes.php 文件:

// Authentication Routes...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');
// Registration Routes...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');

認證視圖

完成認證還需要我們在 resources/views/auth 目錄下創(chuàng)建 login.blade.phpregister.blade.php,當然,這些個頁面的設計和樣式并不重要,只是需要包含一些基本的字段即可。

register.blade.php 文件需要一個包含 nameemail、passwordpassword_confirmation 字段的表單,該表單請求方式為 POST,請求頁面路由為/auth/register。

login.blade.php 文件應該需要一個包含 emailpassword 字段的表單,該表單請求方式為 POST,請求頁面路由為/auth/login。

注:如果你想要查看這些視圖的完整示例,可以去下載相應的 GitHub 項目:https://github.com/laravel/quickstart-intermediate

任務控制器

由于我們需要獲取和保存任務,所以還需要使用 Artisan 命令創(chuàng)建一個 TaskController,生成的控制器位于 app/Http/Controllers 目錄:

php artisan make:controller TaskController --plain

現(xiàn)在這個控制器已經生成了,讓我們去 app/Http/routes.php 中定義一些指向該控制器的路由吧:

Route::get('/tasks', 'TaskController@index');
Route::post('/task', 'TaskController@store');
Route::delete('/task/{task}', 'TaskController@destroy');

設置所有任務路由需要登錄才能訪問

對本應用而言,我們想要所有任務需要登錄用戶才能訪問,換句話說,用戶必須登錄到系統(tǒng)才能創(chuàng)建新任務。所以,我們需要限制訪問任務路由的用戶為登錄用戶。Laravel 使用中間件來處理這種限制。

如果要限制登錄用戶才能訪問該控制器的所有動作,可以在控制器的構造函數(shù)中添加對 middleware 方法的調用。所有有效的路由中間件都定義在 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');
    }
}

4、創(chuàng)建布局&視圖

本應用仍然只有一個視圖,該視圖包含了用于添加新任務的表單和顯示已存在任務的列表。為了讓你更直觀的查看該視圖,我們將已完成的應用截圖如下:

http://wiki.jikexueyuan.com/project/laravel-5.1/images/basic-overview.png" alt="" />

定義布局

幾乎所有的 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 指令,用于指定繼承布局的子頁面在這里可以注入自己的內容。接下來,我們來定義使用該布局的子視圖來提供主體內容。

定義子視圖

好了,我們已經創(chuàng)建了應用的布局視圖,下面我們需要定義一個包含創(chuàng)建新任務的表單和已存在任務列表的視圖,該視圖文件存放在 resources/views/tasks.blade.php,對應 TaskController 中的 index 方法。

我們將跳過 Bootstrap CSS 的樣板文件而只專注在我們所關注的事情上,不要忘了,你可以從 GitHub 下載本應用的所有資源:

// 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

一些需要解釋的地方

在繼續(xù)往下之前,讓我們簡單談談這個模板。首先,我們使用@extends 指令告訴 Blade 我們要使用定義在 resources/views/layouts/app.blade.php 的布局,所有@section('content')@endsection 之間的內容將會被注入到 app.blade.php 布局的@yield('contents')指令位置。

現(xiàn)在,我們已經為應用定義了基本的布局和視圖,然后我們回到 TaskControllerindex 方法:

/**
 * Display a list of all of the user's task.
 *
 * @param Request $request
 * @return Response
 */
public function index(Request $request){
    return view('tasks.index');
}

接下來,讓我們繼續(xù)添加代碼到 POST /task 路由的控制器方法來處理表單輸入并添加新任務到數(shù)據(jù)庫。

注:@include('common.errors')指令將會加載 resources/views/common/errors.blade.php 模板中的內容,我們還沒有定義這個模板,但很快就會了!

6、添加任務

驗證表單輸入

現(xiàn)在我們已經在視圖中定義了表單,接下來需要編寫代碼到 TaskController@store 方法來處理表單請求并創(chuàng)建一個新任務。

對這個表單而言,我們將 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...
}

如果你已經看過新手入門教程,那么你可能會注意到這里的驗證代碼與之前大不相同,這是因為我們現(xiàn)在在控制器中,可以方便地調用 ValidatesRequests trait(包含在 Laravel 控制器基類中)提供的 validate 方法。

我們甚至不需要手動判讀是否驗證失敗然后重定向。如果驗證失敗,用戶會自動被重定向到來源頁面,而且錯誤信息也會被存放到一次性 Session 中。簡直太棒了,有木有!

$errors 變量

我們在視圖中使用了@include('common.errors')指令來渲染表單驗證錯誤信息,common.errors 允許我們在所有頁面以統(tǒng)一格式顯示錯誤信息。我們定義 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 實例。

創(chuàng)建任務

現(xiàn)在輸入驗證已經做好了,接下來正式開始創(chuàng)建一個新任務。一旦新任務創(chuàng)建成功,頁面會跳轉到/ tasks。要創(chuàng)建任務,可以借助 Eloquent 模型的關聯(lián)關系。

大部分 Laravel 的關聯(lián)關系提供了 save 方法,該方法接收一個關聯(lián)模型實例并且會在保存到數(shù)據(jù)庫之前自動設置外鍵值到關聯(lián)模型上。在本例中,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');
}

很好,到了這里,我們已經可以成功創(chuàng)建任務,接下來,我們繼續(xù)添加代碼到視圖來顯示所有任務列表。

7、顯示已存在的任務

首先,我們需要編輯 TaskController@index 傳遞所有已存在任務到視圖。view 函數(shù)接收一個數(shù)組作為第二個參數(shù),我們可以將數(shù)據(jù)通過該數(shù)組傳遞到視圖中:

/**
 * 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 模型所有數(shù)據(jù)的訪問和使用。

依賴注入

Laravel 的服務容器是整個框架中最重要的特性,在看完快速入門教程后,建議去研習下服務容器的文檔。

創(chuàng)建 Repository

正如我們之前提到的,我們想要定義一個 TaskRepository 來處理所有對 Task 模型的數(shù)據(jù)訪問,隨著應用的增長當你需要在應用中共享一些 Eloquent 查詢時這就變得特別有用。

因此,我們創(chuàng)建一個 app/Repositories 目錄并在其中創(chuàng)建一個 TaskRepository 類。記住,Laravel 項目的 app 文件夾下的所有目錄都使用 PSR-4 自動加載標準被自動加載,所以你可以在其中隨心所欲地創(chuàng)建需要的目錄:

<?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 創(chuàng)建好了之后,可以簡單通過在 TaskController 的構造函數(shù)中以類型提示的方式注入該 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()),
        ]);
    }
}

顯示任務

數(shù)據(jù)被傳遞到視圖后,我們可以在 tasks/index.blade.php 中以表格形式顯示所有任務。Blade 中使用@foreach 處理循環(huán)數(shù)據(jù):

@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>&nbsp;</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

至此,本應用基本完成。但是,當任務完成時我們還沒有途徑刪除該任務,接下來我們就來處理這件事。

7、刪除任務

添加刪除按鈕

我們在 tasks/index.blade.php 視圖中留了一個“TODO”注釋用于放置刪除按鈕。當刪除按鈕被點擊時,DELETE /task 請求被發(fā)送到應用后臺并觸發(fā) 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 請求,該函數(shù)生成一個隱藏的表單輸入框,然后 Laravel 識別出該輸入并使用其值覆蓋實際的 HTTP 請求方法。生成的輸入框如下:

<input type="hidden" name="_method" value="DELETE">

路由模型綁定

現(xiàn)在,我們準備在 TaskController 中定義 destroy 方法,但是,在此之前,讓我們回顧下路由中對刪除任務的定義:

Route::delete('/task/{task}', 'TaskController@destroy');

在沒有添加任何額外代碼的情況下,Laravel 會自動注入給定任務 ID 到 TaskController@destroy,就像這樣:

/**
 * Destroy the given task.
 *
 * @param Request $request
 * @param string $taskId
 * @return Response
 */
public function destroy(Request $request, $taskId){
    //
}

然而,在這個方法中首當其沖需要處理的就是通過給定 ID 從數(shù)據(jù)庫中獲取對應的 Task 實例,因此,如果 Laravel 能夠從一開始注入與給定 ID 匹配的 Task 實例豈不是很好?下面就讓我們來實現(xiàn)這個!

app/Providers/RouteServiceProvider.php 文件的 boot 方法中,我們添加如下這行代碼:

$router->model('task', 'App\Task');

這一小行代碼將會告知 Laravel 一旦在路由聲明中找到{task},就會獲取與給定 ID 匹配的 Task 模型?,F(xiàn)在我們可以像這樣定義 destroy 方法:

/**
 * Destroy the given task.
 *
 * @param Request $request
 * @param Task $task
 * @return Response
 */
public function destroy(Request $request, Task $task){
    //
}

權限&授權

現(xiàn)在,我們已經將 Task 實例注入到 destroy 方法;然而,我們并不能保證當前登錄認證用戶是給定任務的實際擁有人。例如,一些惡意請求可能嘗試通過傳遞隨機任務 ID 到/tasks/{task}鏈接刪除另一個用戶的任務。因此,我們需要使用 Laravel 的授權功能來確保當前登錄用戶擁有對注入到路由中的 Task 實例進行刪除的權限。

創(chuàng)建 Policy

Laravel 使用“策略”來將授權邏輯組織到單個類中,通常,每個策略都對應一個模型,因此,讓我們使用 Artisan 命令創(chuàng)建一個 TaskPolicy,生成的文件位于 app/Policies/TaskPolicy.php

php artisan make:policy TaskPolicy

接下來,讓我們添加 destroy 方法到策略中,該方法會獲取一個 User 實例和一個 Task 實例。該方法簡單檢查用戶 ID 和任務的 user_id 值是否相等。實際上,所有的策略方法都會返回 truefalse

<?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;
    }
}

最后,我們需要關聯(lián) Task 模型和 TaskPolicy,這可以通過在 app/Providers/AuthServiceProvider.php 文件的 policies 屬性中添加注冊來實現(xiàn),注冊后會告知 Laravel 無論何時我們嘗試授權動作到 Task 實例時該使用哪個策略類進行判斷:

/**
 * The policy mappings for the application.
 *
 * @var array
 */
protected $policies = [
    'App\Task' => 'App\Policies\TaskPolicy',
];

授權動作

現(xiàn)在我們編寫好了策略,讓我們在 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 方法的第一個參數(shù)是我們想要調用的策略方法名,第二個參數(shù)是當前操作的模型實例。由于我們在之前告訴過 Laravel,Task 模型對應的策略類是 TaskPolicy,所以框架知道觸發(fā)哪個策略類上的 destroy 方法。當前用戶實例會被自動傳遞到策略方法,所以我們不需要手動傳遞。

如果授權成功,代碼會繼續(xù)執(zhí)行。如果授權失敗,會拋出一個403異常并顯示一個錯誤頁面給用戶。

注:除此之外,Laravel 還提供了其它授權服務實現(xiàn)方式,可以查看授權文檔了解更多。

刪除任務

最后,讓我們添加業(yè)務邏輯到路由中執(zhí)行刪除操作,我們可以使用 Eloquent 提供的 delete 方法從數(shù)據(jù)庫中刪除給定的模型實例。記錄被刪除后,跳轉到/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');
}
上一篇:測試下一篇:Artisan 控制臺