イントロダクション
このクイックスタートガイドはLaravelフレームワークの中級レベルの入門で、データベースマイグレーション、Eloqunt ORM、ルーティング、認証、認可、依存注入、バリデーション、ビュー、Bladeテンプレートを内容として含んでいます。Laravelフレームワークや一般的なPHPフレームワークの基本を習熟している方に最適です。
Laravelの持つ機能の基本的な部分を試せるように、完了したいタスク全てを管理できるタスクリストを構築してみます。言い換えれば、典型的な「ToDo」リストサンプルです。「基本的な」クイックスタートに対し、このチュートリアルではユーザーがアプリケーションへアカウントを作成したり、ログインできるようにします。このプロジェクトの最終の完全なソースコードはGitHubから取得可能です。
インストール
Laravelのインストール
もちろん、最初に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
カラムと、tasks
とusers
テーブルを結びつける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')->unsigned()->index();
$table->string('name');
$table->timestamps();
});
}
/**
* マイグレーションの巻き戻し
*
* @return void
*/
public function down()
{
Schema::drop('tasks');
}
}
マイグレーションを実行するには、migrate
Artisanコマンドを使います。Homesteadを使っている場合、ホストからは直接データベースへ接続できないため、このコマンドは仮想マシーンで実行してください。
php artisan migrate
このコマンドは全データベーステーブルを生成します。お好きなクライアントを使用し、データベーステーブルを調べてもらえば、マイグレーションで定義したカラムを含んだ新しいtasks
とusers
テーブルを見つけることができるでしょう。これでEloquent
ORMモデルを定義する準備ができました!
Eloquentモデル
EloquentはLaravelのデフォルトORM(object-relational mapper)です。Eloquentは明確に定義された「モデル」を用いることで、苦労せずにデータベースへのデータ保存/取得を行わせてくれます。通常各Eloquentモデルは、一つのデータベーステーブルに対応します。
User
モデル
最初にusers
データベーステーブルに対応するモデルが必要です。しかしプロジェクトのapp
ディレクトリを探してもらえば、Laravelが最初からUser
モデルを用意してあるのを発見するでしょう。ですから、自分で作成する必要はありません。
Task
モデル
では、作ったばかりのtasks
データベーステーブルに対応するTask
モデルを定義してみましょう。このモデルを生成するために、再度Artisanコマンドを使用します。この場合はmake:model
コマンドを使用します。
php artisan make:model Task
このモデルはアプリケーションのapp
ディレクトリに設置されます。デフォルトではこのクラスは空です。データベーステーブルはモデルの複数形の名前だと想定されているため、Eloquentモデルがどのテーブルに対応するかを明確に宣言する必要はありません。ですから、この場合Task
モデルはtasks
データベーステーブルと対応していると想定しています。
このモデルに何か追加していきましょう。最初はモデルに対して「複数代入」ができるようname
属性を登録します。これにより、Eloquentのcreate
メソッドを使う場合に、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;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
// 他の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
トレイトを使用しています。
認証ルートとビュー
さて、残りは何でしょう?えーと、ユーザ登録とログインのテンプレートが必要ですし、認証コントローラへのルート定義もまだです。これらは全部、make:auth
Artisanコマンドで片付きます。
php artisan make:auth
注意: これらのビューの完全なサンプルが必要であれば、アプリケーションの全コードはGitHubから入手できることを思い出してください。
これで、残りはルートファイルで認証ルートを追加すれば全て完了です。Route
ファサードのauth
メソッドで行います。このメソッドは、私達が必要としている認証、ログイン、パスワードリセットすべてのルートをまとめて登録してくれます。
// 認証ルート
Route::auth();
auth
ルートを登録したら、app/Http/Controllers/Auth/AuthController
コントローラの$redirectTo
プロパティに、/tasks
を確実にセットしてください。
protected $redirectTo = '/tasks';
さらに、app/Http/Middleware/RedirectIfAuthenticated.php
ファイルのリダイレクトパスも変更する必要があります。
return redirect('/tasks');
タスクコントローラー
タスクを取得したり、保存したりする必要がありますので、新しいコントローラーをapp/Http/Controllers
ディレクトリへ生成するArtisan
CLIを使い、TaskController
を作成しましょう。
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ではミドルウェアを使えばあっという間です。
コントローラーの全アクションに対して認証を要求するには、コントローラーのコンストラクターから、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>
<!-- CSSとJavaScript -->
</head>
<body>
<div class="container">
<nav class="navbar navbar-default">
<!-- ナビバーの内容 -->
</nav>
</div>
@yield('content')
</body>
</html>
レイアウトの@yield('content')
の部分に注目です。これはレイアウトを拡張する全部の子ページが、自身のコンテンツを親へ注入できる場所を指定するための特別なBladeディレクティブ(指定子)です。次に、このレイアウトを使用しメインコンテンツを表示する、子のビューを定義しましょう。
子ビューの定義
素晴らしい。アプリケーションのレイアウトは完成しました。次に新しいタスクを作成するためのフォームと、存在する全タスクを同時に表示するビューを定義する必要があります。TaskController
のindex
メソッドに対応する、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="{{ url('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: Current Tasks -->
@endsection
簡単な説明
先に進む前に、このテンプレートについて多少説明しましょう。最初に@extends
ディレクティブにより、resources/views/layouts/app.blade.php
に定義したレイアウトを使用することをBladeに指示しています。@section('content')
から@endsection
の間のコンテンツが、app.blade.php
レイアウトの中の@yield('content')
ディレクティブの場所に挿入されます。
@include('common.errors')
ディレクティブは、resources/views/common/errors.blade.php
にあるテンプレートをロードします。まだ、このテンプレートを作成していませんが、この後すぐに行います。
これでアプリケーションの基本レイアウトとビューが定義できました。続けてTaskController
のindex
メソッドから、このビューを返しましょう。
/**
* ユーザーの全タスクをリスト表示
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index');
}
次に、フォーム入力を処理し、データベースに新しいタスクを追加するPOST /task
ルートのコントローラーメソッドを追加しましょう。
タスク追加
バリデーション
これでビューにフォームが用意できましたので、フォームの入力の正当性を確認し(バリデーション)、新しいタスクを作成する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 = $request->user()->tasks()->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;
class TaskRepository
{
/**
* 指定ユーザーの全タスク取得
*
* @param User $user
* @return Collection
*/
public function forUser(User $user)
{
return $user->tasks()
->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">
Current Tasks
</div>
<div class="panel-body">
<table class="table table-striped task-table">
<!-- テーブルヘッダ -->
<thead>
<th>Task</th>
<th> </th>
</thead>
<!-- テーブル本体 -->
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- Task Name -->
<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="{{ url('task/'.$task->id) }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" id="delete-task-{{ $task->id }}" class="btn btn-danger">
<i class="fa fa-btn fa-trash"></i>削除
</button>
</form>
</td>
</tr>
見せかけのメソッドの説明
削除ボタンフォームのmethod
がPOST
を使用しているのにかかわらず、定義しているルートはRoute::delete
である点に注目です。HTMLフォームはGET
とPOST
HTTP動詞のみを許しています。そのため、フォームのDELETE
リクエストを見せかける手段が必要になります。
フォームの中でmethod_field('DELETE')
関数の結果を出力しています。この関数は、Laravelで認識され、実際のHTTPリクエストメソッドをオーバーライドする隠しフォーム入力を生成します。生成されるフィールドは次の内容です。
<input type="hidden" name="_method" value="DELETE">
ルートモデル結合
これでTaskController
のdestroy
メソッドを定義する準備が整いました。しかし、最初にこのルート定義とコントローラメソッドを見直してみましょう。
Route::delete('/task/{task}', 'TaskController@destroy');
/**
* 指定タスクの削除
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
//
}
ルート中の{task}
変数が、コントローラメソッド中の$task
変数定義と一致するため、Laravelの暗黙的モデル結合により、対応するタスクモデルのインスタンスが自動的に依存注入されます。
認可
これで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
と一致するかを調べるだけです。実際、全ポリシーメソッドはtrue
かfalse
を返す必要があります。
<?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 = [
'App\Task' => 'App\Policies\TaskPolicy',
];
アクションの認可
これでポリシーが書き上がりましたので、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
メソッドに付け加え、完成させましょう。データベースから指定したモデルインスタンスを削除するには、delete
Eloquentの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');
}