Laravel 5.1 中級者向けタスクリスト

イントロダクション

このクイックスタートガイドはLaravelフレームワークの中級レベルの入門で、データベースマイグレーション、Eloqunt ORM、ルーティング、認証、認可、依存注入、バリデーション、ビュー、Bladeテンプレートを内容として含んでいます。Laravelフレームワークや一般的なPHPフレームワークの基本を習熟している方に最適です。

Laravelの持つ機能の基本的な部分を試せるように、完了したいタスク全てを管理できるタスクリストを構築してみます。(典型的なToDoリスト)このチュートリアルによりユーザーがアプリケーションへアカウントを作成したり、ログインできるようになります。このプロジェクトの最終の完全なソースコードはGitHubから取得可能です。

インストール

もちろん、最初にLararavelフレームワークを真新しくインストールする必要があります。Homestead仮想マシーンか、フレームワークを実行するために選択したローカルのPHP環境を使用します。ローカル環境が整ったら、Composerを使用しLaravelフレームワークをインストールできます。

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

このクイックスタートの残りの部分をただ読み進めることもできますが、ソースコードをダウンロードしてローカルマシーンで実行したい場合は、Gitリポジトリーをクローンし、依存パッケージをインストールします。

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

ローカル開発環境の構築についてのドキュメントは、Homesteadインストールのドキュメントを参照してください。

データベースの準備

データベースマイグレーション

最初に全タスクを保持しておくためのデータベーステーブルを定義する、マイグレーション(migration:移行)を使ってみましょう。Laravelのデータベースマイグレーションはスラスラ書ける記述的なPHPコードを用いて、データベーステーブルの構造を定義し修正するための簡単な方法を提供しています。チームメンバーへ個別で用意しているデータベースのコピーへカラムを各自自分で追加するように伝える代わりに、あなたがソース管理にPushしたマイグレーションを実行してもらえます。

usersテーブル

ユーザーからアプリケーションにアカウントを作成してもらえるようにするには、ユーザー全員を保持するテーブルが必要です。ありがたいことに基本的なusersテーブルを作成するマイグレーションは最初からLaravelに用意されているので、自分で作成する必要はありません。デフォルトのusersテーブルのマイグレーションは、database/migrationsディレクトリーに設置されています。

tasksテーブル

つぎに全タスクを保持するデータベーステーブルを構築しましょう。Artisan CLIは様々なクラスの生成に利用でき、Laravelプロジェクトを構築するためにたくさんタイプする手間を省いてくれます。今回はtasksテーブルのために、新しいデータベースマイグレーションをmake:migrationコマンドを使って生成します。

php artisan make:migration create_tasks_table --create=tasks

マイグレーションはプロジェクトのdatabase/migrationsディレクトリーの中に設置されます。お分かりでしょうが、make:maigrationコマンドは、マイグレーションファイルへ自動増分IDとタイムスタンプの追加を始めに定義しています。このファイルを編集し、タスクの名前を保存するstringカラムと、tasksusersテーブルを結びつけるuser_idカラムを追加しましょう。

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTasksTable extends Migration
{
    /**
     * マイグレーション実行
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->index();
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * マイグレーションの巻き戻し
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('tasks');
    }
}

マイグレーションを実行するには、migrate Artisanコマンドを使います。Homesteadを使っている場合、ホストからは直接データベースへ接続できないため、このコマンドは仮想マシーンで実行してください。

php artisan migrate

このコマンドは全データベーステーブルを生成します。お好きなクライアントを使用し、データベーステーブルを調べてもらえば、マイグレーションで定義したカラムを含んだ新しいtasksusersテーブルを見つけることができるでしょう。これでEloquent ORMモデルを定義する準備ができました!

Eloquentモデル

Userモデル

最初にusersデータベーステーブルに対応するモデルが必要です。しかしプロジェクトのappディレクトリーを探してもらえば、Laravelが最初からUserモデルを用意してあるのを発見するでしょう。ですから、自分で作成する必要はありません。

Taskモデル

では、作ったばかりのtasksデータベーステーブルに対応するTaskモデルを定義してみましょう。このモデルを生成するために、再度Artisanコマンドを使用します。この場合はmake:modelコマンドを使用します。

php artisan make:model Task

このモデルはアプリケーションのappディレクトリーに設置されます。デフォルトではこのクラスは空です。データベーステーブルはモデルの複数形の名前だと想定されているため、Eloquentモデルがどのテーブルに対応するかを明確に宣言する必要はありません。ですから、この場合Taskモデルはtasksデータベーステーブルと対応していると想定しています。

このモデルにいくらか追加しましょう。最初にモデルに対し「複数代入」ができるようname属性を登録します。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    /**
     * 複数代入する属性
     *
     * @var array
     */
    protected $fillable = ['name'];
}

アプリケーションにルートを追加してから、Eloquentモデルについて更に学びましょう。もちろん、ご自由にEloquentの完全なドキュメントを読んで、更に情報を学んでもかまいません。

Eloquentリレーション

これでモデルが定義できましたので、関係づける必要があります。たとえばあるUserは複数のTaskインスタンスを所有することができます。一方、あるTaskは一つのUserに紐付けられます。関連付けを定義づけることにより、リレーションがスラスラと利用できるようになります。次のようにです。

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

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

tasksのリレーション

最初にUserモデルに対するtasksリレーションを定義付けましょう。Eloquentのリレーションはモデルのメソッドとして定義します。Eloquentは様々なタイプのリレーションをサポートしていますので、より詳細は完全なEloquentのドキュメントで確認してください。この場合は、Eloquentに用意されているhasManyメソッドを呼び出し、Userモデルに対するtasksメソッドを定義しています。

<?php

namespace App;

// 名前空間のインポート…

class User extends Model implements AuthenticatableContract,
                                    AuthorizableContract,
                                    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword;

    // 他のEloquentプロパティー

    /**
     * 特定ユーザーの全タスク取得
     */
    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
{
    /**
     * 複数代入する属性
     *
     * @var array
     */
    protected $fillable = ['name'];

    /**
     * タスク所有ユーザーの取得
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

すばらしい!これでリレーションは定義できました。コントローラーにとりかかりましょう!

ルーティング

タスクリストアプリケーションの基本バージョンでは、routes.phpファイルの中で全ロジックをクロージャーにより定義しました。このアプリケーションではルートを系統立てるためにコントローラーを使用します。コントローラーにより、HTTPリクエスト処理ロジックを複数のファイルに分割できるので、より組織立てられます。

ビューの表示

アプリケーションにやって来たゲストユーザーのためにシンプルなランディングページを表示する/ルートのみ、唯一クロージャーを使ってルート定義します。では、/ルートを書いてしまいましょう。このルートでは、「ウエルカム」ページのコンテンツのHTMLテンプレートを表示します。

Laravelの全HTMLテンプレートはresources/viewsディレクトリーに設置されます。ルートからこれらのテンプレートの一つを返すためにviewヘルパが使えます。

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

もちろんこのビューを実際に定義する必要があります。後ほど行いましょう!

認証

ユーザーにアカウントを作ってもらい、アプリケーションにログインしてもらう必要があることを思い出してください。通常、Webアプリケーションに認証レイヤー全体を組み込むのは退屈な仕事です。しかし、こうした共通したニーズに対し、Laravelは全く苦労なしに実現できるように努力しています。

既にapp/Http/Controllers/Auth/AuthControllerがLaravelアプリケーションに用意されていることに注目しましょう。このコントローラーはユーザーを作成したり、認証したりするのに必要なロジックを全部含んでいる、特別なAuthenticatesAndRegistersUsersトレイトを使用しています。

認証ルート

では、残りは何を行えばよいのでしょうか?そうですね…まだ登録とログインのテンプレートを作成する必要がありますし、同時に認証コントローラーを実行できるようにルートを定義しなくてはなりません。最初にapp/Http/routes.phpへルートを追加しましょう。

// 認証ルート…
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');

// 登録ルート…
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');

認証ビュー

認証を行うにはresources/views/authディレクトリーへlogin.blade.phpregister.blade.phpを作成する必要があります。もちろん、これらのビューのデザインやスタイルは重要ではありませんが、最低でも基本的なフィールドは用意しなくてはなりません。

register.blade.phpファイルはnameemailpasswordpassword_confirmationフィールドを含むフォームを構成し、/auth/registerルートへPOSTする必要があります。

login.blade.phpファイルはemailpasswordフィールドのフォームを含み、/auth/loginPOSTリクエストを送る必要があります。

注意: これらのビューの完全なサンプルが必要であれば、アプリケーションの全コードはGitHubから入手できることを思い出してください。

タスクコントローラー

タスクを取得したり、保存したりする必要がありますので、新しいコントローラーをapp/Http/Controllersディレクトリーへ生成するArtisan CLIを使い、TaskControllerを作成しましょう。

php artisan make:controller TaskController --plain

これでコントローラーが生成できました。続いてこのコントローラーを実行するルートをapp/Http/routes.phpファイルへスタブ(空の代用コード)として作成します。

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

全タスクルートの認証

このアプリケーションのタスクルートは全て認証ユーザーだけにアクセスを許します。言い換えれば、タスクを作成するにはアプリケーションに「ログイン」している必要があります。ですから、タスクルートは認証済みユーザーのみアクセスできるように制限する必要があります。Laravelではミドルウェアを使えばあっという間です。

コントローラーの全アクションに対して認証を要求するには、コントローラーのコンストラクターから、middlewareメソッドを呼び出します。利用可能な全ルートミドルウェアはapp/Http/Kernel.phpで定義されています。この場合、authミドルウェアをコントローラーの全アクションに対して適用します。

<?php

namespace App\Http\Controllers;

use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class TaskController extends Controller
{
    /**
     * 新しいコントローラーインスタンスの生成
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }
}

レイアウトとビューの構築

このアプリケーションは新しいタスクを追加するためのフォームを含み、同時に現在のタスクをリストするビューを一つだけ持ちます。ビューを想像するために役立つように、基本的なBootstrapのCSSスタイルを適用した、最終段階のアプリケーションのスナップショットをご覧ください。

アプリケーションイメージ

レイアウトの定義

ほとんど全てのアプリケーションでは同じレイアウトをページに渡り共有します。たとえばこのアプリケーションは全ページ(一つ以上のページが存在する場合)で表示する、典型的なトップナビバーがあります。LaravelはBladeレイアウトを使い、こうしたページ間共通のフューチャーを簡単に共有できるようになっています。

前に説明したように、Laravelの全ビューはresources/viewsに設置されます。ですから新しいレイアウトビューもresources/views/layouts/app.blade.phpとして定義します。.blade.php拡張子はビューを表示するときにBladeテンプレートエンジンを使用することをフレームワークへ指示します。もちろんLaravelでも普通のPHPテンプレートが使用できます。しかしBladeなら簡潔できれいなテンプレートを書くための便利なショートカットが利用できます。

app.blade.phpビューは以下のような構成になるでしょう。

// resources/views/layouts/app.blade.php

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Laravel Quickstart - Intermediate</title>

        <!-- CSSJavaScript -->
    </head>

    <body>
        <div class="container">
            <nav class="navbar navbar-default">
                <!-- ナビバーの内容 -->
            </nav>
        </div>

        @yield('content')
    </body>
</html>

レイアウトの@yield('content')の部分に注目です。これはレイアウトを拡張する全部の子ページが、自身のコンテンツを親へ注入できる場所を指定するための特別なBladeディレクティブ(指定子)です。次に、このレイアウトを使用しメインコンテンツを表示する、子のビューを定義しましょう。

子ビューの定義

素晴らしい。アプリケーションのレイアウトは完成しました。次に新しいタスクを作成するためのフォームと、存在する全タスクを同時に表示するビューを定義する必要があります。TaskControllerindexメソッドに対応する、resources/views/tasks.blade.phpを定義しましょう。

Bootstrap CSSの定形コードを省いて、重要な部分に焦点を当てましょう。アプリケーションの完全なソースコードは、GitHubからダウンロードできることは覚えておいてください。

// resources/views/tasks/index.blade.php

@extends('layouts.app')

@section('content')

    <!-- Bootstrapの定形コード… -->

    <div class="panel-body">
        <!-- バリデーションエラーの表示 -->
        @include('common.errors')

        <!-- 新タスクフォーム -->
        <form action="/task" method="POST" class="form-horizontal">
            {{ csrf_field() }}

            <!-- タスク名 -->
            <div class="form-group">
                <label for="task-name" 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>

            <!-- タスク追加ボタン -->
            <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: 現在のタスク -->
@endsection

簡単な説明

先に進む前に、このテンプレートについて多少説明しましょう。最初に@extendsディレクティブにより、resources/views/layouts/app.blade.phpに定義したレイアウトを使用することをBladeに指示しています。@section('content')から@endsectionの間のコンテンツが、app.blade.phpレイアウトの中の@yield('content')ディレクティブの場所に挿入されます。

これでアプリケーションの基本レイアウトとビューが定義できました。続けてTaskControllerindexメソッドから、このビューを返しましょう。

/**
 * ユーザーの全タスクをリスト表示
 *
 * @param  Request  $request
 * @return Response
 */
public function index(Request $request)
{
    return view('tasks.index');
}

次に、フォーム入力を処理し、データベースに新しいタスクを追加するPOST /taskルートのコントローラーメソッドを追加しましょう。

注意: @include('common.errors)ディレクティブはresources/views/common/errors.blade.phpのテンプレートをロードします。このテンプレートは定義していませんが、すぐに行います!

タスク追加

バリデーション

これでビューにフォームが用意できましたので、フォームの入力の正当性を確認し(バリデーション)、新しいタスクを作成するTaskController@storeメソッドを追加しましょう。最初に、入力のバリデーションです。

このフォームでは、nameフィールドの入力が必須で、内容が255文字以下であることを確認しましょう。バリデーションに失敗したら、ユーザーを/tasksのURLへリダイレクトし、同時に以前の入力とエラーをsessionへフラッシュデータとして保存します。

/**
 * 新タスク作成
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    $this->validate($request, [
        'name' => 'required|max:255',
    ]);

    // タスクの作成…
}

基本のクイックスタートを行っているなら、このバリデーションコードは全く異なっていることに気がつくでしょう!コントローラーを使っていますので、Laravelのベースコントローラーで取り込んでいるValidatesRequestsトレイトの機能を活用できます。このトレイトはリクエストと配列のバリデーションルールを引数に取る、シンプルなvalidateメソッドを提供しています。

バリデーションが失敗したかとか、リダイレクトとかを自分で行う必要さえありません。指定したルールのバリデーションに失敗したら、ユーザーを自動的に直前のページヘリダイレクトし、エラーも自動的にセッションへフラッシュデーターとして保存されます!

$errors変数

フォームのバリデーションエラーを表示するために@include('common.errors')ディレクティブをビューで使用したことを思い出してください。common.errorsによりバリデーションエラーを同じ形式で、全ページに渡り簡単に表示できるようにしています。このビューの内容を定義しましょう。

// resources/views/common/errors.blade.php

@if (count($errors) > 0)
    <!-- フォームのエラーリスト -->
    <div class="alert alert-danger">
        <strong>おや?何かがおかしいようです!</strong>

        <br><br>

        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

注意: $errors変数は全てのLaravelビューの中で参照できます。バリデーションエラーが存在しない場合は、ViewErrorBagの空のインスタンスです。

タスク作成

これで入力のバリデーションは処理できました。新しいタスクを実際に作成するためにルート処理の定義を続けましょう。新しくタスクを生成したら、ユーザーを/tasksのURLへリダイレクトします。タスクを作成するために、Eloquentリレーションのパワーを活用しましょう。

ほとんどのLaravelリレーションではcreateメソッドが準備されます。このメソッドは属性の配列を受け取り、データベースへ保存する前に、関連するモデルの外部キー値を自動的に設定します。以下の場合、createメソッドは、$request->user()でアクセスできる現在の認証済みユーザのIDを指定したタスクのuser_idプロパティへ自動的に設定します。

/**
 * 新しいタスクの作成
 *
 * @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');
}

いいですね!これでタスクを作成できるようになりました。次に存在する全タスクをリストするビューを追加していきましょう。

既存タスク表示

最初に、TaskController@indexメソッドを編集し、既存の全タスクをビューに渡しましょう。view関数は第2引数に、ビューで使用するデータを配列で受け付けます。配列のキーはビューの中で変数となります。たとえば次のように使用します。

/**
 * ユーザーの全タスクをリスト表示
 *
 * @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,
    ]);
}

これでも動作しますが、全データアクセスに対して利用するTaskRepositoryをLaravelの依存注入能力を使い、TaskControllerに注入することにしましょう。

依存注入

Laravelのサービスコンテナはフレームワーク全体で最もパワフルな機能です。このクイックスタートを読んだ後、コンテナのドキュメントを全て読んでください。

リポジトリーの作成

説明した通り、Taskモデルとの全アクセスロジックを持つ、TaskRepositoryを定義しましょう。これは特にアプリケーションが大きくなり、アプリケーション全体からEloquentクエリーを共有する必要が起きるようになると有効な手段です。

では、app/Repositoriesディレクトリーを作成し、TaskRepositoryクラスを追加しましょう。LaravelのappフォルダーはPSR-4オートロード規約に従いオートロードされますので、必要に応じていくらでも追加のフォルダーを作成できます。

<?php

namespace App\Repositories;

use App\User;
use App\Task;

class TaskRepository
{
    /**
     * 指定ユーザーの全タスク取得
     *
     * @param  User  $user
     * @return Collection
     */
    public function forUser(User $user)
    {
        return Task::where('user_id', $user->id)
                    ->orderBy('created_at', 'asc')
                    ->get();
    }
}

リポジトリーの注入

ポジトリーが定義できたら、TaskControllerのコンストラクターで「タイプヒント」するだけで、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
{
    /**
     * タスクリポジトリーインスタンス
     *
     * @var TaskRepository
     */
    protected $tasks;

    /**
     * 新しいコントローラーインスタンスの生成
     *
     * @param  TaskRepository  $tasks
     * @return void
     */
    public function __construct(TaskRepository $tasks)
    {
        $this->middleware('auth');

        $this->tasks = $tasks;
    }

    /**
     * ユーザーの全タスクをリスト表示
     *
     * @param  Request  $request
     * @return Response
     */
    public function index(Request $request)
    {
        return view('tasks.index', [
            'tasks' => $this->tasks->forUser($request->user()),
        ]);
    }
}

タスク表示

データを渡したら、tasks/index.blade.phpビューの中でタスクを反復処理し、テーブルとして表示します。とても早く通常のPHPコードにコンパイルできる@foreach Blade構造文で簡単にループが記述できます。

@extends('layouts.app')

@section('content')
    <!-- タスクフォームの作成… -->

    <!-- 現在のタスク -->
    @if (count($tasks) > 0)
        <div class="panel panel-default">
            <div class="panel-heading">
                現在のタスク
            </div>

            <div class="panel-body">
                <table class="table table-striped task-table">

                    <!-- テーブルヘッダー -->
                    <thead>
                        <th>Task</th>
                        <th>&nbsp;</th>
                    </thead>

                    <!-- テーブルボディー -->
                    <tbody>
                        @foreach ($tasks as $task)
                            <tr>
                                <!-- タスク名 -->
                                <td class="table-text">
                                    <div>{{ $task->name }}</div>
                                </td>

                                <td>
                                    <!-- TODO: 削除ボタン -->
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    @endif
@endsection

タスクアプリケーションはほとんど完成です。しかし、終了した既存タスクを削除する手段がありません。次に実装しましょう。

タスク削除

削除ボタンの追加

コード中、削除ボタンを設置する場所に"TODO"を残してあります。では、tasks/index.blade.phpビューのタスクリストの各行に、削除ボタンを追加しましょう。小さなボタンひとつのフォームをリストの各タスクごとに作成します。ボタンがクリックされると、DELETE /taskリクエストがアプリケーションに送信され、TaskController@destroyメソッドが起動されます。

<tr>
    <!-- タスク名 -->
    <td class="table-text">
        <div>{{ $task->name }}</div>
    </td>

    <!-- 削除ボタン -->
    <td>
        <form action="/task/{{ $task->id }}" method="POST">
            {{ csrf_field() }}
            {{ method_field('DELETE') }}

            <button>タスク削除</button>
        </form>
    </td>
</tr>

見せかけのメソッドの説明

削除ボタンフォームのmethodPOSTを使用しているのにかかわらず、定義しているルートはRoute::deleteである点に注目です。HTMLフォームはGETPOST HTTP動詞のみを許しています。そのため、フォームのDELETEリクエストを見せかける手段が必要になります。

フォームの中でmethod_field('DELETE')関数の結果を出力しています。この関数は、Laravelで認識され、実際のHTTPリクエストメソッドをオーバーライドする隠しフォーム入力を生成します。生成されるフィールドは次の内容です。

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

ルートモデル結合

これでTaskControllerdestroyメソッドを定義する準備が整いました。しかし、最初にこのルート定義を見てみましょう。

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

他に追加のコードを加えること無く、Laravelは指定されたタスクIDをTaskController@destroyメソッドへ、次のように注入できます。

/**
 * 指定タスクの削除
 *
 * @param  Request  $request
 * @param  string  $taskId
 * @return Response
 */
public function destroy(Request $request, $taskId)
{
    //
}

しかしながらこの前に、このメソッドへ指定されたIDを使いデータベースからTaskインスタンスを取得させる必要があります。最初の時点でLaravelがIDの一致するTaskインスタンスを注入してくれたら、素敵だと思いませんか?そうしましょう!

app/Providers/RouteServiceProvider.phpファイルのbootメソッドに、次のコードを追加してください。

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

この僅かなコードで、ルート宣言に{task}があれば、いつでも指定されたIDに対応するTaskモデルを取得するようにLaravelへ指示しています。これにより、destroyメソッドは次のように定義できます。

/**
 * 指定タスクの削除
 *
 * @param  Request  $request
 * @param  Task  $task
 * @return Response
 */
public function destroy(Request $request, Task $task)
{
    //
}

認可

これでTaskインスタンスはdestroyメソッドへ注入できました。しかし、認証済みのユーザーが指定したタスクを「所有」している保証はありません。たとえば他のユーザーのタスクを削除するために/tasks/{task}のURLへランダムなタスクIDを渡すことで、悪意のあるリクエストを仕込むことが可能です。そこでルートに注入されたTaskインスタンスが実際に認証済みユーザーが所有していることを確認するため、Laravelの認可機能を使う必要があります。

ポリシー

認証ロジックを単純で小さなクラスへ系統立てるため、Laravelは「ポリシー」を使っています。通常、各ポリシーはモデルに対応しています。では、Artisan CLIを使い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;

    /**
     * 指定されたユーザーが指定されたタスクを削除できるか決定
     *
     * @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プロパティに一行加えることで可能です。これによりTaskインスタンスに対するアクションを認可したい場合に毎回使われるべきポリシーをLaravelに指定しています。

/**
 * アプリケーションにマップするポリシー
 *
 * @var array
 */
protected $policies = [
    Task::class => TaskPolicy::class,
];

アクションの認可

これでポリシーが書き上がりましたので、destroyメソッドで使用しましょう。Laravelの全コントローラーから、AuthorizesRequestトレイトにより提供されているauthorizeメソッドを呼び出せます。

/**
 * 指定タスクの削除
 *
 * @param  Request  $request
 * @param  Task  $task
 * @return Response
 */
public function destroy(Request $request, Task $task)
{
    $this->authorize('destroy', $task);

    // タスクの削除…
}

このメソッド呼び出しを少し調べてみましょう。authorizeメソッド最初の引数は、呼び出したいポリシーメソッドの名前です。2つ目の引数は現在関心を向けているモデルインスタンスです。現在TaskPolicyに関連付けたTaskモデルについてLaravelに語りかけているわけですから、フレームワークはどのポリシーのdestroyメソッドを起動すればよいのか理解しています。現在のユーザーは自動的にポリシーメソッドに送られますので、わざわざ渡す必要はありません。

アクションが許可されたら、コードは通常通り続けられます。しかし、アクションが非許可になれば(つまりポリシーのdestroyメソッドがfalseを返したら)、403例外が投げられ、ユーザーにエラーページが表示されます。

注意: Laravelが提供する認可サービスを取り扱う方法は、他にもたくさんあります。認可のドキュメントを十分に読んでください。

タスク削除

最後に、指定したタスクを実際に削除するロジックをdestroyメソッドに付け加え、完成させましょう。データベースから指定したモデルインスタンスを削除するには、deleteEloquentのdeleteメソッドを使います。レコードを削除したら、ユーザーを/tasksのURLへリダイレクトします。

/**
 * 指定タスクの削除
 *
 * @param  Request  $request
 * @param  Task  $task
 * @return Response
 */
public function destroy(Request $request, Task $task)
{
    $this->authorize('destroy', $task);

    $task->delete();

    return redirect('/tasks');
}

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

バージョン

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる