Open hysryt opened 7 years ago
PHP7.2以上必須
$ laravel new laraveltest
laraveltest
は任意のプロジェクト名
大量のライブラリがインストールされる
$ cd laraveltest
$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>
プロジェクトフォルダへ移動する
php artisan serve
で、PHPに付属する簡易サーバ上で動かすことができる
http://127.0.0.1:8000/ にアクセスすると実際に動いていることがわかる
ルーディングの設定はプロジェクトフォルダ/routes/web.php
に記述する
Route::get('/', function() {
return view('welcome');
});
get
メソッドでルーティングの設定ができる
第一引数はリクエスト時のパス
第二引数は関数またはコントローラクラスの名前を入れる
Route::get('/', 'TestController@index');
コントローラクラスを指定する場合はコントローラクラス名@アクションメソッド名
の形式で記述する
@アクションメソッド名
を省略した場合、__invoke
メソッドが使われる
アクションが一つしかない場合はこの方法でもいい
Route::get('/', 'TestController@index');
Route::get('/search', 'TestController@search');
Route::get('/form', 'TestController@form');
複数ルーティングを指定する場合はget
メソッドを複数記述する
クラスメソッドを使った仕組みについては以下を参照 http://www.1x1.jp/blog/2014/03/laravel-facade-class.html
artisan
コマンドでコントローラクラスの雛形を作成できる
$ php artisan make:controller コントローラクラス名
$ php artisan make:controller TestController
$ ls app/Http/Controllers/
Auth Controller.php TestController.php
作成したコントローラクラスはプロジェクトフォルダ/app/Http/Controllers/
配下に置かれる
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TestController extends Controller
{
//
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class TestController extends Controller
{
public function index(Request $request, Response $response) {
$response->setContent('<h1>aiueo</h1>');
return $response;
}
}
TestController
コントローラにindex
アクションを定義
アクションは引数にRequest
とResponse
を取れる
返り値としてResponse
のインスタンスを返す
Response
インスタンスはsetContent
メソッドでHTMLを設定できる
<?php
Route::get('/', 'TestController@index');
パス/
へのアクセスはTestController
コントローラのindex
アクションを実行する
LaravelではBladeというテンプレートエンジンを使用する
テンプレートファイルはプロジェクトフォルダ/resources/views/
配下に作成する
一般的にはさらにコントローラ名のディレクトリを作成し、その中に作成する
ファイル名はファイル名.blade.php
とする
$ mkdir resources/views/test
$ vi resources/views/test/index.blade.php
test
ディレクトリを作成し、その中にテンプレートファイルindex.blade.php
を作成
<html>
<head></head>
<body>
{{$message}}
</body>
</html>
{{$message}}
のように{{$変数名}}
とすることでそこに変数を出力できる
テンプレートファイルを使用するにはview
関数を使用する
<?php
Route::get('/', function() {
$data = [
"message" => "こんにちは"
];
return view("test.index", $data);
});
view
関数の第一引数にテンプレートファイルを指定する
指定する形式はフォルダ名.ファイル名
第二引数にテンプレートで使用する変数を格納した連想配列を指定する
連想配列のキーがそのまま変数名となってテンプレートファイルに埋め込まれる
view
関数はResponse
インスタンスを返す
Route::get
の第二引数でコントローラを指定した場合は、アクションメソッド内で同じように記述する
{{$message}}
にこんにちはが出力され以下のHTMLがブラウザで表示される
<html>
<head></head>
<body>
こんにちは
</body>
</html>
プロジェクトフォルダ/config/app.php
を以下のように修正する
timezone
: Asia/Tokyo
locale
: ja
(略)
'timezone' => 'UTC',
(略)
'locale' => 'en',
(略)
(略)
'timezone' => 'Asia/Tokyo',
(略)
'locale' => 'ja',
(略)
ディレクティブを使うことでBladeテンプレート内で制御構文を記述できる
<html>
<head></head>
<body>
@if ($hour < 12)
午前
@else
午後
@endif
</body>
</html>
@if
、@else
、@endif
ディレクティブを使って条件分岐を行なっている
<?php
Route::get('/', function() {
$data = [
"hour" => (new DateTime())->format("G") // 時刻
];
return view("test.index", $data);
});
ディレクティブ | 説明 | ディレクティブ | 説明 |
---|---|---|---|
@if br>`@elseif`<br@else @endif |
条件分岐 | @for br>`@endfor`<br@while @endwhile |
繰り返し |
@break @continue |
繰り返し制御 | 他多数 |
使用するディレクティブ:@yield
@extends
@section
@endsection
別のテンプレートを基にテンプレートを作成することができる 別のテンプレートを基に作成することを継承と呼ぶ
<html>
<head></head>
<body>
<header>
<h1>ヘッダー部分</h1>
</header>
@yield('content')
<footer>
フッター部分
</footer>
</body>
</html>
@extends('test.layout')
@section('content')
<div>
コンテンツ部分
</div>
@endsection
index.blade.php
はlayout.blade.php
を継承している
継承には@extends
ディレクティブを使用する
index.blade.php
に記述した@section('content')
〜@endsection
に記述した内容が、
layout.blade.php
の@yield('content')
に埋め込まれる
継承することで差分のみの記述ですむ
結果、以下のHTMLがブラウザで表示される
<html>
<head></head>
<body>
<header>
<h1>ヘッダー部分</h1>
</header>
<div>
コンテンツ部分
</div>
<footer>
フッター部分
</footer>
</body>
</html>
使用するディレクティブ:@component
@endcomponent
@slot
@endslot
テンプレートファイル間で再利用したい部品がある場合は、コンポーネントとして別ファイルに分離できる
<div>
<input type="text">
<input type="button" value="検索">
</div>
<html>
<head></head>
<body>
@component('test.searchbox')
@endcomponent
<div>
アイウエオ
</div>
@component('test.searchbox')
@endcomponent
</body>
</html>
@component
〜@endcomponent
にsearchbox.blade.php
の内容が埋め込まれ、以下のHTMLがブラウザに表示される
<html>
<head></head>
<body>
<div>
<input type="text">
<input type="button" value="検索">
</div>
<div>
アイウエオ
</div>
<div>
<input type="text">
<input type="button" value="検索">
</div>
</body>
</html>
コンポーネントに引数を渡すこともできる
<div>
<h1>{{$title}}</h1>
<input type="text">
<input type="button" value="検索">
</div>
<html>
<head></head>
<body>
@component('test.searchbox')
@slot('title')
検索窓1
@endslot
@endcomponent
<div>
アイウエオ
</div>
@component('test.searchbox')
@slot('title')
検索窓2
@endslot
@endcomponent
</body>
</html>
引数を渡すには@slot
ディレクティブを使用する
以下のHTMLがブラウザに表示される
<html>
<head></head>
<body>
<div>
<h1>検索窓1</h1>
<input type="text">
<input type="button" value="検索">
</div>
<div>
アイウエオ
</div>
<div>
<h1>検索窓2</h1>
<input type="text">
<input type="button" value="検索">
</div>
</body>
</html>
ミドルウェアを使うことによってアクションの前後に処理を挟むことができる 特定のアクションのみに対して設定することもできるし、全てのアクションに対して設定することもできる
ミドルウェアの雛形はartisan
コマンドで作成できる
$ php artisan make:middleware TestMiddleware
プロジェクトフォルダ/app/Http/Middleware/
配下に作成される
<?php
namespace App\Http\Middleware;
use Closure;
class TestMiddleware
{
public function handle($request, Closure $next)
{
return $next($request);
}
}
引数で渡されたnext
関数の先でアクションの処理が実行される
前後に処理を挟みたい場合は以下のように記述する
<?php
namespace App\Http\Middleware;
use Closure;
class TestMiddleware
{
public function handle($request, Closure $next)
{
// アクション前処理
$response = $next($request);
// アクション後処理
return $response;
}
}
特定のアクションに設定する場合は、ルート設定時に記述する
use App\Http\Middleware\TestMiddleware;
Route::get('/', 'TestController@index')
->middleware(TestMiddleware::class);
ミドルウェアを複数設定することもできる
use App\Http\Middleware\TestMiddleware;
Route::get('/', 'TestController@index')
->middleware(FirstMiddleware::class);
->middleware(SecondMiddleware::class);
->middleware(ThirdMiddleware::class);
プロジェクトフォルダ/app/Http/Kernel.php
の$middleware
配列にミドルウェアを追加する
(略)
protected $middleware = [
(略)
\App\Http\Middleware\TestMiddleware::class,
];
(略)
バリデーションには以下の方法がある
validate
メソッドでバリデーション
validate
メソッドを使用する$errors
変数でバリデーション結果を取得できるValidator::make
でバリデータを作成するhttps://laravel.com/docs/6.x/middleware
クライアントとWebアプリの間に追加する仕組み。 全てのHTTPリクエストをフィルタリングすることができる。 ユーザーの認証などもミドルウェアで行う。 HTTPヘッダを書き換えたり、ログをとったりする機能などにも使われる。
クライアントからWebアプリへのリクエストに追加することもできるし、Webアプリからクライアントへのレスポンスに追加することもできる。両方に追加することもできる。
php artisan make:middleware CheckAge
app/Http/Middleware
ディレクトリ内に作成される。
ミドルウェアに依存関係が存在する場合は Service Container に記述する。
ミドルウェアを全てのHTTPリクエストに適用させる場合は app/Http/Kernel.php
の $middleware
に追加する。
ルートにミドルウェアを適用する場合はまず app/Http/Kernel.php
の $routeMiddleware
にキーとクラスを追加する。
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
...
];
そして追加したキーでルートにミドルウェアを適用する。
適用には middleware()
を使用する。
Route::get('admin/profile', function () {
//
})->middleware('auth');
app/Http/Kernel.php
の $middlewareGroups
でミドルウェアをグループ化できる。
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
...
];
ミドルウェアグループも単独のミドルウェアと同じように midlleware()
で適用できる。
Route::get('/', function () {
//
})->middleware('web');
ミドルウェアの優先度は app/Http/Kernel.php
の $middlewarePriority
で指定する。
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
ミドルウェアを適用する際に:
と,
でパラメータを渡すことができる。
次の例では role
ミドルウェアに editor
というパラメータを渡している。
Route::put('post/{id}', function ($id) {
//
})->middleware('role:editor');
渡したパラメータは handle()
の第3引数以降で受け取ることができる。
public function handle($request, Closure $next, $role)
{
if (! $request->user()->hasRole($role)) {
// Redirect...
}
return $next($request);
}
ターミナブルミドルウェアはブラウザにレスポンスを送った後に発火するミドルウェア。 FastCGI を使用している必要がある。
ターミナブルミドルウェアを作るにはミドルウェアクラスに terminate()
を実装する。
public function terminate($request, $response)
{
// Store the session data...
}
https://laravel.com/docs/6.x/routing
URLとクロージャを指定する。
Route::get('foo', function () {
return 'Hello World';
});
ルーティング情報は routes
ディレクトリ内に保存する。これらは全てフレームワークによって自動的に読み込まれる。
routes/web.php
には Web 用のルーティング情報を記述する。
ここに書かれたルートには web
ミドルウェアが適用される。
routes/api.php
は API 用のルーティング情報用のファイルであり、こちらには api
ミドルウェアが適用される。
GET
用のルートは Route::get()
、 POST
用のルートは Route::post()
で指定する。
複数のメソッドに指定したい場合は Route::match()
を使用する。
Route::match(['get', 'post'], '/', function () {
//
});
全てのメソッドに指定する場合は Route::any()
が使える。
Route::any('/', function () {
//
});
リダイレクトを設定する場合は Route::redirect()
を使用する。
Route::redirect('/here', '/there');
Route::redirect('/here', '/there', 301); // ステータスコードの指定が可能
Route::permanentRedirect('/here', '/there'); // 上と同等
直接 View を返す場合は Route::view()
を使用する。
Route::view('/welcome', 'welcome');
URLの一部をパラメータとして使用することが可能。
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
// 複数も可
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
ルートパラメータの任意版。 デフォルト値は必ず設定する必要がある。
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
正規表現でルートパラメータに制約をつけることができる。
where()
を使用する。
Route::get('user/{name}', function ($name) {
//
})->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
//
})->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
//
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
ルートに名前をつけることでURLの取得やリダイレクトの設定が簡単になる。
次はルートに profile
という名前をつけた例。
Route::get('user/profile', function () {
//
})->name('profile');
// URLの取得
$url = route('profile');
// リダイレクトの設定
return redirect()->route('profile');
コントローラークラスでは関連するルートをグループ化でき、これによってプロジェクト全体の見通しをよくすることができる。
コントローラークラスは app/Http/Controllers
ディレクトリに配置する。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\User;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return View
*/
public function show($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
コントローラークラスは Controller
クラスを継承することが多いが、必須ではない。
Controller
クラスには便利なメソッド(middleware
, validate
, dispatch
など)がいくつかあり、継承すればそれらを使うことができる。
ルーティングの際以下のように設定することで、コントローラーへのルーティングが可能。
次は「URLが user/{id}
に一致する場合 UserController
クラスの show
メソッドを実行する」例。
Route::get('user/{id}', 'UserController@show');
指定するコントローラー名は App\Http\Controllers
名前空間から検索される。
コントローラーが App\Http\Controllers\Photos\AdminController
の場合は次のように設定する。
Route::get('foo', 'Photos\AdminController@method');
コントローラーが一つのアクションしか行わない場合は、そのアクションを __invoke
メソッドとして実装することでルーティング設定時の記述が簡潔になる。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\User;
class ShowProfile extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return View
*/
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
上記のように __invoke
を実装した場合、次のようにクラス名の指定だけで良い。
Route::get('user/{id}', 'ShowProfile');
このコントローラクラスはCLIから作成することもできる。
php artisan make:controller ShowProfile --invokable
ミドルウェアはルートに対して適用する方法の他に、コントローラのコンストラクタ内で適用する方法がある。
public function __construct()
{
$this->middleware('auth');
}
コントローラクラス内の特定のメソッドに対して適用することもできる。
public function __construct()
{
$this->middleware('auth')->only('index');
}
middleware
メソッドを使うには Controller
クラスを継承する必要がある。
リソース用に CRUD に対応したコントローラをリソースコントローラと呼ぶ。 リソースコントローラの雛形は次のコマンドで生成することができる。
php artisan make:controller PhotoController --resource
リソースコントローラへのルーティングは resource
メソッドで適用できる。
Route::resource('photos', 'PhotoController');
これによって登録されるルートは以下の通り
メソッド | URI | アクション | ルート名 |
---|---|---|---|
GET | /photos | index | photos.index |
GET | /photos/create | create | photos.create |
POST | /photos | store | photos.store |
GET | /photos/{photo} | show | photos.show |
GET | /photos/{photo}/edit | edit | photos.edit |
PUT/PATCH | /photos/{photo} | update | photos.update |
DELETE | /photos/{photo} | destroy | photos.destroy |
store
や show
などに渡す引数のタイプヒンティングとして --model
を指定することができる。
php artisan make:controller PhotoController --resource --model=Photo
HTMLフォームでは PUT、PATCH、DELETE を送信できないため、代わりに _method
フィールドでメソッドを指定する。
Bladeでは @method
ディレクティブでフィールドできる。
<form action="/foo/bar" method="POST">
@method('PUT')
</form>
全てのアクションが必要ない場合は、必要とするアクションのみ指定することができる。
Route::resource('photos', 'PhotoController')->only([
'index', 'show'
]);
apiResource
メソッドを使うとAPIに不要な(HTMLフォームへのルート) create
や edit
のアクションを除くことができる。
Route::apiResource('photos', 'PhotoController');
API用の(create
、edit
を除いた)コントローラの雛形は次のコマンドで生成できる。
php artisan make:controller API/PhotoController --api
写真に対するコメント、のようにあるリソースに対するリソースを指定することもできる。
Route::resource('photos.comments', 'PhotoCommentController');
URLは以下のようになる。
photos/{photos}/comments/{comments}
Laravelのサービスコンテナは全てのコントローラを解決する。
アプリケーションがコントローラベースのルートのみを使用している場合はルートキャッシュを使用できる。 ルートキャッシュを使用することで、ルート登録にかかるコストが大幅に削減される。 次のコマンドでルートキャッシュを生成できる。
php artisan route:cache
新しくルートを追加した場合はこのコマンドを再度実行する必要がある。 そのため、デプロイ時に実行すれば良い。
ルートキャッシュをクリアする場合は次のコマンドを使用する。
php artisan route:clear
HTMLを提供する役割を持つ。 アプリケーションからプレゼンテーションロジックを分離することで「関心の分離」を実現する。
ビューは resources/views
ディレクトリに設置する。
以下にビューの例を示す。
<!-- View stored in resources/views/greeting.blade.php -->
<html>
<body>
<h1>Hello, {{ $name }}</h1>
</body>
</html>
コードからビューを取得するには view
メソッドを使用する。
Route::get('/', function () {
return view('greeting', ['name' => 'James']);
});
resources/views
内にディレクトリがある場合、view
では /
ではなく .
で指定する。
// resources/views/admin/profile.blade.php
return view('admin.profile', $data);
View::exists
を使用してビューか存在するかどうか確認できる。
use Illuminate\Support\Facades\View;
if (View::exists('emails.customer')) {
//
}
first
メソッドは複数のビューの中から存在するものを取得する機能を持つ。
次の場合、配列で指定したビューを順に見ていき、そのビューが存在すればそれを返す。
return view()->first(['custom.admin', 'admin'], $data);
view
メソッドの第二引数に指定したデータがビューに渡される。
この時、データは連想配列(キーバリューペア)である必要がある。
return view('greetings', ['name' => 'Victoria']);
サービスプロバイダの boot
メソッドで View::share
メソッドを使うことで全てのビューで使用するデータを指定できる。
ビューコンポーザーとはビューの描画時に呼び出される処理のことである。 ビューコンポーザーファイルの設置位置は定められていないためどこに設置しても良い。
ビューコンポーザーの登録はサービスプロバイダーで行う。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
// Using class based composers...
View::composer(
'profile', 'App\Http\View\Composers\ProfileComposer'
);
// Using Closure based composers...
View::composer('dashboard', function ($view) {
//
});
}
}
View::composer
の第一引数にビュー名、第二引数にビューコンポーザー用クラス名またはクロージャを設定する。
ビューコンポーザーに新しくサービスプロバイダーを作った場合は config/app.php
の providers
に追加する必要がある。
ビューコンポーザーをクラスで登録した場合、そのクラスの compose
メソッドがビュー描画時に呼び出される。
<?php
namespace App\Http\View\Composers;
use App\Repositories\UserRepository;
use Illuminate\View\View;
class ProfileComposer
{
/**
* The user repository implementation.
*
* @var UserRepository
*/
protected $users;
/**
* Create a new profile composer.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
// Dependencies automatically resolved by service container...
$this->users = $users;
}
/**
* Bind data to the view.
*
* @param View $view
* @return void
*/
public function compose(View $view)
{
$view->with('count', $this->users->count());
}
}
compose
には表示するビューが引数として渡されるため、 with
を用いて表示データを追加できる。
View::creator
でビューに対してビュークリエイターを設定できる。
View::creator('profile', 'App\Http\View\Creators\ProfileCreator');
ビュークリエイターはビューがインスタンス化されたとき即座に実行される。
https://laravel.com/docs/6.x/facades
ファサードはクラスの静的メソッドのインターフェースを提供する。 Laravelでは多くのファサードを用意しており、Laravelのほぼ全ての機能にアクセスできるようになっている。 Laravelファサードは下層レイヤークラスの静的プロキシとして機能し、従来の静的メソッドよりもテスト容易性と柔軟性を維持しながら、簡潔で表現力豊かな構文を提供する。
Laravelのファサードは Illuminate\Support\Facades
名前空間で定義されている。
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
ファサードには多くの利点がある。 長い名前のクラス名を覚える必要なく、簡潔な構文でLaravelの機能を使うことができる。 さらに、PHPの動的メソッドを独自に使用しているため、簡単にテストできる。
ただし、いくつか注意がある。 主な注意点は、クラススコープの肥大化である。 ファサードはインジェクションを必要とせず、簡単に使用できるため、クラスが肥大化する傾向にある。 依存注入する際はコンストラクタが大きくなっていることでクラスが肥大化していることに気づくことができる。 ファサードを使用する際はクラスの責任範囲について特別な注意を払う必要がある。
依存性注入の一番の利点は、注入するクラスを容易に交換できることである。 モックやスタブを使うことができるため、テスト時に役立つ。
通常、クラスメソッドはモック化やスタブ化することはできない。 ただしファサードは内部でサービスコンテナーから解決されたオブジェクトへのメソッド呼び出しを行っているため、依存性注入と同じようにテストができる。
例えば次のルートの場合、
Route::get('/cache', function () {
return Cache::get('key');
});
テストを次のようにすることで、Cache::get
が期待通り呼び出されたことを確認できる。
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Laravelにはビューの生成、イベントの発火、ジョブのディスパッチ、HTTPレスポンスの送信などを様々なヘルパー関数が存在する。 ヘルパー関数の多くはファサードと同じ機能を実行する。
以下の二つは同等と言える。
return View::make('profile');
return view('profile');
ヘルパー関数は内部でファサードを使用するため、全く同じと言っていい。
Laravelアプリケーションでは、ファサードはコンテナー内のオブジェクトにアクセスするための方法を提供するクラスである。これは Facade
クラスが持つ機能あり、Laravel内のファサードは Illuminate\Support\Facades\Facade
を継承している。
Facade
規定クラスは __callStatic
マジックメソッドを利用して、コンテナ内のオブジェクトを遅延呼び出しする。
次の例ではLaravelのキャッシュシステムが呼び出されている。
Cache
クラスのクラスメソッドget
が呼び出されている。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
この例では Cache
ファサードをインポートしている。
Cache
ファサードは Illuminate\Contracts\Cache\Factory
インターフェースの実装クラスにアクセスするプロキシとして動作する。
このファサードへの呼び出しは全てLaravelのキャッシュサービスに渡される。
Illuminate\Support\Facades\Cache
クラスに get
クラスメソッドは存在しない。
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
代わりに getFacadeAccessor()
が存在する。
このメソッドはサービスコンテナにバインドされている名前を返す役割を持つ。
Cache
ファサードにクラスメソッドの呼び出しがあったとき、LaravelはgetFacadeAccessor()
の戻り値にバインドされているオブジェクトに対して呼び出しを行う。
リアルタムファサードを使うことで、アプリケーション内のクラスをファサードのように扱うことが可能になる。
例えば Podcast
クラスに publish
メソッドがあり、このメソッドには Publisher
インスタンスを渡す必要があるとする。
<?php
namespace App;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @param Publisher $publisher
* @return void
*/
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
Publisher
をメソッドで渡すようにすることで、モック化が可能になり、テストが容易になる。
しかし、publish
メソッドを呼ぶときに常に Publisher
インスタンスを渡さなくてはならなくなる。
このような時、リアルタイムファサードを使うことでテストの容易性を保ったまま Publisher インスタンスを明示的に渡さなくても良い仕組みを作ることができる。
リアルタイムファサードを生成する場合、インポートするクラスの名前空間の先頭に Facades
をつける
<?php
namespace App;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @return void
*/
public function publish()
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}
リアルタイムファサードが使用されると、自動的にサービスコンテナで Facades プレフィックスが含まれるクラス(またはインターフェース)の実装が解決される。 テスト時にはLaravelのFacadeと同じようにテストが可能になる。
<?php
namespace Tests\Feature;
use App\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* A test example.
*
* @return void
*/
public function test_podcast_can_be_published()
{
$podcast = factory(Podcast::class)->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
ファサードと実装クラスの関連
https://laravel.com/docs/6.x/container
サービスコンテナは、クラスの依存関係の管理や依存性の注入を行うツールである。
簡単な例を見てみる。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\User;
class UserController extends Controller
{
/**
* The user repository implementation.
*
* @var UserRepository
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
この例では UserContoroller
はデータソースからユーザーを取得する必要があり、そのためユーザーを取得できるサービスを注入する必要がある。
このコンテキストでは UserRepository
はデータベースからおそらく Eloquent を使用してユーザー情報を取得している。
UserRepository
はコンストラクタによって外部から挿入されているため、他の実装やテスト用のモックなどと簡単に交換できる。
強力で大規模なアプリケーションを作るには、サービスコンテナの深い理解は不可欠となる。
クラスがインターフェースに依存してない場合は、コンテナに明示的にバインドする必要はない。リフレクションを利用して自動的に生成される。
サービスプロバイダ内であればどこであっても $this->app
プロパティでコンテナにアクセスできる。
bind
メソッドにクラス名(またはインスターフェース名)とインスタンスを返すクロージャを渡すことでバインディングできる。
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
クロージャの引数にはサービスコンテナが渡されるため、さらにその先の依存関係を解決できる。
singleton
メソッドでシングルトンとしてバインディングできる。
$this->app->singleton('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
instance
メソッドでインスタンスをバインディングできる。
$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\API', $api);
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);
サービスプロバイダの特徴はインターフェースを実装にバインディングできる点である。
例えば EventPusher
インターフェースと RedisEventPusher
クラスがあるとする。
次のようにすることでこの二つをバインディングできる。
$this->app->bind(
'App\Contracts\EventPusher',
'App\Services\RedisEventPusher'
);
これによって EventPusher
を解決する場面で RedisEventPusher
を注入する必要があることをコンテナに伝えることができる。
use App\Contracts\EventPusher;
/**
* Create a new class instance.
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}
同じインターフェースを実装するクラスが複数あることは多くある。
例えば2つのコントローラが Illuminate\Contracts\Filesystem\Filesystem
を実装する別々のクラスに依存しているとする。
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
場合によっては特定のカテゴリのバインディングを全て解決する必要がある。
例えば Report
インターフェースを実装する複数のクラスを配列で返す場合。
bind
でバインディングを行った後、tag
でタグを割り当てることができる。
$this->app->bind('SpeedReport', function () {
//
});
$this->app->bind('MemoryReport', function () {
//
});
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
タグ付けしたものは tagged
で取得できる。
$this->app->bind('ReportAggregator', function ($app) {
return new ReportAggregator($app->tagged('reports'));
});
extend
メソッドで解決されたサービスを変更できる。
$this->app->extend(Service::class, function ($service, $app) {
return new DecoratedService($service);
});
$service
には解決されたサービスが渡される。
make
メソッドを使ってインスタンスの取得ができる。
$api = $this->app->make('HelpSpot\API');
コンテナにアクセスできない場所では resolve
ヘルパー関数を使用する。
$api = resolve('HelpSpot\API');
クラスの依存関係の一部がコンテナで解決できない場合は makeWith
メソッドを使用する。
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
タイプヒントによって自動注入が可能。
<?php
namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller
{
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the user with the given ID.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}
resolving
メソッドでコンテナのイベントをフックできる。
$this->app->resolving(function ($object, $app) {
// 解決が行われたときに常に呼び出される。
});
$this->app->resolving(HelpSpot\API::class, function ($api, $app) {
// HelpSpot\API が解決されたときに呼び出される。
});
PSR-11 に準拠している。
したがって Psr\Container\ContainerInterface
のタイプヒントでコンテナを取得できる。
use Psr\Container\ContainerInterface;
Route::get('/', function (ContainerInterface $container) {
$service = $container->get('Service');
//
});
サービスプロバイダはアプリケーションの中心部分であり、Laravelコアを含む全てのサービスはサービスプロバイダを介してブートストラップされる。
ブートストラップとはサービスのバインドや、イベントリスナー、ミドルウェア、ルートの登録を意味する。
config/app.php
に $providers
配列があり、これはアプリケーションで読み込まれる全てのサービスプロバイダを指定している。
これらの多くは遅延プロバイダーであり、必要な場合のみ読み込まれる。
サービスプロバイダは Illuminate\Support\ServiceProvider
を継承し、 register
メソッドと boot
メソッドを持つ。
register
メソッド内ではサービスコンテナへのバインディングのみ行うべきである。イベントリスナーやルートは register
で登録すべきではない。
サービスプロバイダは make:provider
コマンドで生成できる。
php artisan make:provider RiakServiceProvider
上述したように、 register
メソッド内ではサービスコンテナへのバインディングのみ行うべきであり、イベントリスナーやルートの登録を行なってはならない。
まだ読み込んでいないサービスプロバイダのサービスを使用してしまう可能性があるからである。
基本的なサービスプロバイダを見てみよう。
サービスプロバイダーのメソッド内では $this->app
でサービスコンテナにアクセスできる。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Riak\Connection;
class RiakServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}
この例では register
メソッドのみ定義しており、メソッド内では Riak\Connection
の実装をサービスコンテナにバインディングしている。
$bindings
プロパティと $singletons
プロパティ複数のサービスをバインディングする場合は $bindings
プロパティと $singletons
プロパティを使うと完結にすむ。
これらは自動的に読み込まれ、バインディングされる。
<?php
namespace App\Providers;
use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* All of the container bindings that should be registered.
*
* @var array
*/
public $bindings = [
ServerProvider::class => DigitalOceanServerProvider::class,
];
/**
* All of the container singletons that should be registered.
*
* @var array
*/
public $singletons = [
DowntimeNotifier::class => PingdomDowntimeNotifier::class,
ServerToolsProvider::class => ServerToolsProvider::class,
];
}
ビューコンポーザの登録は boot
メソッドで行う。
このメソッドは全てのサービスプロバイダが読み込まれた後に実行される。
つまり、他のサービス全てにアクセスできるということである。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
view()->composer('view', function () {
//
});
}
}
タイプヒントで依存性を注入できる。
use Illuminate\Contracts\Routing\ResponseFactory;
public function boot(ResponseFactory $response)
{
$response->macro('caps', function ($value) {
//
});
}
全てのサービスプロバイダは config/app.php
内で登録される。
このファイルの $providers
にサービスプロバイダのクラスを列挙する。
初期値は Laravel コアのサービスプロバイダが指定されており、メーラー、キャッシュ、キューなどがブートストラップされる。
サービスプロバイダを登録する場合はこの配列に追加する。
'providers' => [
// Other Service Providers
App\Providers\ComposerServiceProvider::class,
],
もしサービスプロバイダがサービスコンテナへのバインディングのみ行う場合、必要とされるまで登録を遅らせる「遅延プロバイダ」とすることができる。 必要な時しか読み込まれないため、パフォーマンスがよくなる場合がある。
サービスプロバイダを遅延プロバイダとする場合、\Illuminate\Contracts\Support\DeferrableProvider
インターフェースを実装し、provides
メソッドを定義する。
provides
メソッドの戻り値には register
メソッドでバインディングしたサービスのバインドサキクラス名のリストを設定する。
<?php
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Riak\Connection;
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection($app['config']['riak']);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [Connection::class];
}
}
https://laravel.com/docs/6.x/database
Laravel では生のSQL、Fluent Query Builder、Eloquent ORM のいずれかを使ってデータベースにアクセスする。 Laravel では次のデータベースをサポートしている。
データベース設定は config/database.php
に記述する。
DB設定は通常、 host
、database
、username
、password
など複数の情報によって構成される。
これらの情報にはそれぞれ環境変数が対応づけられており、本番サーバーではこれらの環境変数を管理する必要がある。
Heroku などのデータベースプロバイダではデータベースの接続情報を単一の文字列で表現する。 これをデータベースURLという。
driver://username:password@host:port/database?options
以下のような形になる。
mysql://root:password@127.0.0.1/forge?charset=UTF-8
Laravel でもデータベースURLをサポートする。
url
(環境変数では DATABASE_URL
)にデータベースURLを指定すればこちらが使用される。
まれに読み込み(SELECT)用のデータベースと書き込み(UPDATE、INSERT、DELETE)用のデータベースを分けたい場合がある。 そのような場合は以下のように設定を行う。
'mysql' => [
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'196.168.1.3',
],
],
'sticky' => true,
'driver' => 'mysql',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
],
read
、write
、sticky
を追加した。
read
および write
は host
を持つ。
sticky
オプションデータベースの設定によっては読み込み用のデータベースと書き込み用のデータベースに不整合が発生する場合がある。
その場合、sticky
オプションを有効化する。
sticky
オプションが有効な場合、書き込み操作がされたデータベースに対してはその後の読み取りも書き込み用データベースに対して行われる。
これによって不整合が解消される。
コネクション設定が複数ある場合は DB::connection
メソッドにコネクション名を渡す。
$users = DB::connection('foo')->select(...);
DB
ファサードからクエリを実行できる。
DB
ファサードには select
、update
、insert
、delete
、statement
メソッドが用意されている。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
/**
* Show a list of all of the application's users.
*
* @return Response
*/
public function index()
{
$users = DB::select('select * from users where active = ?', [1]);
return view('user.index', ['users' => $users]);
}
}
プレースホルダーを使ったSQL文を第一引数に設定し、パラメータを第二引数に設定する。
select
メソッドは常に配列を返す。
配列の要素は stdClass
オプジェクト。
foreach ($users as $user) {
echo $user->name;
}
$results = DB::select('select * from users where id = :id', ['id' => 1]);
DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);
$affected = DB::update('update users set votes = 100 where name = ?', ['John']);
$deleted = DB::delete('delete from users');
DB::statement('drop table users');
DB::listen
メソッドでクエリイベント発火時のイベントを取得できる。
ログをとるときに便利。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
DB::listen(function ($query) {
// $query->sql
// $query->bindings
// $query->time
});
}
}
DB::transaction
メソッドでトランザクションを作成できる。
コミットやロールバックは自動的に行われる。
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});
デッドロックが生じた際の再試行回数を第二引数で指定できる。 最終的に実行できなかった場合、例外が投げられる。
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
}, 5);
beginTransaction
、 commit
、rollBack
が使用できる。
DB::beginTransaction();
DB::rollBack();
DB::commit();
認証の改善点を見てきましたが、Laravel 5.3の認証には何があるのかを見てみましょう。
注意: 以下で説明するいくつかの機能は、元々 5.2 のサイクルの途中で導入されたものです。しかし、これらの機能はすべて 5.3 のために微調整され、洗練されていますので、ここではそれらの機能を取り上げます。
すでにLaravelでの権限付与に慣れている方は、すぐに読み飛ばしても構いません。
Laravelの認可は、主に2つのコンセプトを中心に構築されています。
Gateクラス。Gateは、誰がどのような能力を持っているか(つまり、誰が何をすることができるか)の正式な権限を持っています。 Gateを使ってユーザーの能力を登録し、後でそのユーザーが与えられたアクションを実行できるかどうかをGateでチェックします。
Policyクラス。Policyは、単一のモデルタイプの能力をチェックする責任があります。 システム内の各モデルクラスに対して認証を行いたい場合は、一致するPolicyクラスがあります。
これがどのように動作するかを確認するために、
php artisan make:policy TaskPolicy
を実行して、理論的なTaskモデル用のPolicyクラスを生成してみましょう。
作成したら、ユーザーが与えられたTaskを更新できるかどうかをチェックするシンプルな update
メソッドを追加します。
namespace App\Policies;
use App\Task;
use App\User;
class TaskPolicy
{
public function update(User $user, Task $task)
{
return $user->id === $task->user_id;
}
}
次に、このPolicyをAuthServiceProviderのPolicyマッピングに追加して、Gateに登録します。
protected $policies = [
\App\Task::class => \App\Policies\TaskPolicy::class,
];
裏では、GateにPolicyを登録しています。 これからは、Taskに対する権限チェックはすべてTaskPolicyにルーティングされます。
具体的には、与えられたTaskを更新するための簡単なRouteを作成してみましょう。
Route::put('tasks/{task}', function (App\Task $task) {
abort_unless(Gate::allows('update', $task), 403);
$task->update(request()->input());
});
ユーザーが与えられたタスクを更新できるかどうかをゲートでチェックします。
GateはPolicyの update
メソッドを呼び出し、現在認証されているユーザーと与えられたTaskの両方を渡します。
(ユーザーがログインしていない場合、Gateは自動的にすべての能力の問い合わせを拒否します)
能力が付与されていない場合、403で中止します。
(ところで、あのabort_unless関数が好きになりませんか?つまり、あれを見てください! まるで一文のように読めます。"GateがTaskの更新を許可しない限り、abortします。これが最高のLaravelです)。
LaravelのコントローラはAuthorizesRequestsトレイトを使用して、任意のリクエストを簡単に認可します。 コントローラ内からauthorizeメソッドを呼び出すことができ、Gateで能力をチェックします。 もし能力が拒否された場合、リクエストは自動的に403レスポンスで中断されます。
これを実際に見るために、Routeをコントローラに移動させてみましょう。
use App\Task;
class TaskController
{
public function update(Task $task)
{
$this->authorize('update', $task);
$task->update(request()->input());
}
}
前と同じように、GateはTaskPolicyの update
メソッドを参照し、その結果を返します。
Laravelは実際にはもう一歩進んで、authorize
メソッドを呼び出す際にアビリティ名を省略することができます。
アビリティ名を省略した場合、Laravelは自動的にコントローラのメソッドからアビリティ名を取得します。
サンプルコントローラで $this->authorize($task)
をコールすると、
チェックのために TaskPolicy@update
を参照することになります。なかなかいいですね!
Laravel 5.2では、自動能力名は常にそれを呼び出すコントローラメソッドの名前になります。 以下のコントローラを与えます。
use App\Task;
class TaskController
{
public function show(Task $task)
{
$this->authorize($task);
return view('tasks.show')->with('task', $task);
}
public function create()
{
$this->authorize(Task::class);
return view('tasks.create');
}
}
...コントローラのメソッド名と一致するメソッド名を持つPolicyを持つことになります。
use App\Task;
use App\User;
class TaskPolicy
{
public function show(User $user, Task $task)
{
// check if the user can view the given $task
}
public function create(User $user)
{
// check if the user can create new tasks
}
}
2016年8月8日更新:この記事を公開した後、テイラーはオーソリティにいくつかの追加変更を加えました。これらの変更を反映して、以下を更新しました。
表面上は非常に直感的なように見えますが、よく見るとこの規約にはいくつかの亀裂があることがわかります。
この慣習がどこで崩れるのか、この2つのサンプルを考えてみましょう。
1.コントローラ以外のアビリティをチェックする コントローラのメソッド名は、能力をチェックするための最も直感的な方法とは限りません。 このテンプレートを見てみましょう。
@foreach ($tasks as $task)
@if ($user->can('show', $task))
<a href="{{ url('tasks', $task) }}">View task</a>
@endif
@endforeach
うーん... これはちょっとおかしいですね。 user->can('show', $task) は何をチェックしているのでしょうか? ユーザがタスクを表示できるかどうかをチェックしているのでしょうか? それはどういう意味なのでしょうか? 表示できるかどうかを知りたいのです!
2.Policyメソッドの重複 新しいTaskを作成するためにフォームを表示するかどうかのチェックは、 ユーザーにそのTaskの保存を許可するかどうかを決定するのとまったく同じチェックです。 コントローラのメソッド名を勝手にマッピングすると、2つの同じメソッドを持つPolicyになってしまいます。
use App\User;
class TaskPolicy
{
public function create(User $user)
{
// check if the user can create new tasks
}
public function store(User $user)
{
// check if the user can create new tasks
}
}
この2つは全く同じチェックを実行しますが、コントローラ上の2つの異なるメソッドには両方が必要です。
edit
とstore
にも同じ重複が適用されます。
Laravel 5.3では、自動能力名がアップグレードされました。
やみくもにメソッド名を使うのではなく、あなたが完了しようとしているアクションに基づいて、
Laravelは適切なアビリティ名を選びます。
アイテムを表示しようとしている場合は、ビューアビリティ名を使用します。
新しいレコードを作成しようとしている場合は、コントローラの store
メソッドから呼び出しても、
作成アビリティ名を使用します。
Laravelは、メソッドとアビリティの間のシンプルなマッピングによってこれを行います。 これがソースからの完全なマップです。
[
'show' => 'view',
'create' => 'create',
'store' => 'create',
'edit' => 'update',
'update' => 'update',
'destroy' => 'delete',
]
これは、コントローラのコードを変更することなく自動的に行われます。
ビューで $user->can('view', $task)
をチェックできるようになり、より直感的になりました。
今では、コントローラが能力名がコントローラのメソッドと一致するゲートを常に呼び出すとは限らないので、 Policyにはそれらのメソッドがないはずです。このコントローラがあるとします。
use App\Task;
class TaskController
{
public function create()
{
$this->authorize(Task::class);
return view('tasks.create');
}
public function store()
{
$this->authorize(Task::class);
Task::create(request()->input());
}
}
...createメソッドだけでポリシーを持つことになります。
use App\User;
class TaskPolicy
{
public function create(User $user)
{
// check if the user can create tasks
}
}
この作成メソッドは、コントローラ内の作成と保存の両方から呼び出されます。とてもすっきりしました。
これらの変更を支援するために、Policy生成機能が大幅に強化されています。 ジェネレータにモデル名を渡すと、必要なPolicyメソッドを完全にスタブアウトして、すべてのデフォルトのリソースコントローラメソッドをカバーします。
php artisan make:policy TaskPolicy --model=Task
Laravelのソースコードにある生のスタブを見ることができ、生成されたメソッドについてより良いイメージが得られるはずです。
アビリティをインラインで認可するのはうまくいきますが、ルートレベルで認可する方が理にかなっていることもあります。 Laravelには、この目的のためのcanミドルウェアが付属しています。
access-dashboardという一般的な能力を定義してみましょう。
Gate::define('access-dashboard', function ($user) {
return $user->isAdmin();
});
不正アクセスからRouteのグループ全体をガードするには、canミドルウェアでグループ化することができます。
Route::group(['middleware' => 'can:access-dashboard'], function () {
// define all dashboard routes...
});
これで、このグループ内の任意のルートを実行する前に、Gateがアクセスダッシュボードの能力をチェックされます。
ミドルウェアに第二引数を渡すこともできます。 これはモデルのクラス名か、与えられたモデルのインスタンスをチェックするためのモデルのルートパラメータ名のどちらかです。
Route::get('tasks/{task}', [
'uses' => 'TaskController@show',
'middleware' => 'can:view,task',
]);
Route::post('tasks', [
'uses' => 'TaskController@store',
'middleware' => 'can:create,App\Task',
]);
ルートのパラメータ名を渡すと、ミドルウェアはルート自体からモデルインスタンスを引き出します。 ルートモデルのバインディングについてはLaravelのドキュメントを参照してください。
認可システムのもう一つの端正な宝石は、
コントローラで利用できるようになった authorizeResource
メソッドです。
これはあなたのコントローラを大幅にクリーンアップする可能性があります。
では、この魔法の authorizeResource
は何をするのでしょうか?
要するに、さまざまなコントローラメソッドでの認証のためのすべての呼び出しを コンストラクタでの単一の呼び出しに置き換えます。
use App\Task;
class TaskController
{
public function __construct()
{
$this->authorizeResource(Task::class);
}
// ... all resource methods ...
}
これだけでOK! 個々のメソッドで認証するための呼び出しをすべてスキップできるようになりました。 Laravelは、任意のアクションに対して正しいアビリティを自動的にチェックするようになりました。 実際にはかなり魔法のようです。
Laravelのビルトインゲートは、アプリ内のすべての認可ロジックをハードコーディングすることで動作します。 データベース経由でユーザーのロールや権限を管理したい場合は、Laravelのビルトイン認証システムと簡単に統合できるBouncerをチェックしてみてください。
Laravelインストーラのインストール
Composerでインストールする
global
を指定しているので~/.composer/vendor
配下にインストールされる バイナリファイルは~/.composer/vendor/bin
配下にインストールされるため必要に応じてパスを通すLaravelインストーラを使うと簡単にLaravelプロジェクトを作成できる