「PHPフレームワーク Laravel入門」の感想・備忘録2

スポンサーリンク
「PHPフレームワーク Laravel入門」の感想・忘備録1の続き

全体

サービスプロバイダ

サービスプロバイダーはサービスコンテナへサービス(インスタンス)を登録するためのもの。。
config/app.phpに使用するサービスプロバイダーが定義されている。
(登録はvendor/Illuminate\Foundation\Application.phpのregisterConfiguredProvidersメソッドで行われている)

サービスプロバイダーはphp artisan make:provider XxxServiceProviderで作成することができる。
registerメソッドに$this->app->singleton(\Xxx\Yyy::class);のようにサービス登録処理を記述し、bootメソッドにすべてのサービスプロバイダーが読み込まれたあとに実行したい処理を記述する。
ビューコンポーザはbootメソッドでView::composer()を実行することで、全てのサービスが実行された後、レンダリングの前に処理を実行させる仕組み。

  • サービスコンテナへのサービスの登録
    app()->bind(\Xxx\Yyy::class);
    app()->singleton(\Xxx\Yyy::class);
  • サービスコンテナからサービスの取り出し
    app()->make(\Xxx\Yyy::class);

ミドルウェア

指定したルートのアクションメソッドの前後に処理を実行させるための仕組み。
全アクションで実行させる場合はapp/Http/Kernel.phpに設定するが、それ以外はルート情報で設定する。

ミドルウェアを作成

php artisan make:middleware HelloMiddleware

handleメソッドに処理を追加

// アクション実行前
$request->merge(['msgFromHelloMiddleware' => 'hello from HelloMiddleware']);

// アクション実行
$response = $next($request);

// アクション実行後
$response->setContent(str_replace('置換されます', '置換されました', $response->content()));
return $response;

ルート情報にミドルウェアを追加する

// 個別のルートに追加する場合
Route::get('/', 'IndexController@index')->middleware(\App\Http\Middleware\HelloMiddleware::class);

// ルート情報のgroupメソッドの第1引数で指定することもできる。
Route::group(['middleware' => ['Hello']], ]function() {
  Route::get('hoge', 'HogeController@index');
});

// 全アクションで実行させる場合はグローバルミドルウェアとして登録する
// app/Http/Kernel.phpの$middleware配列に追加する。

// ミドルウェアグループとして登録する場合
Route::get('/', 'IndexController@index')->middleware('hello');
// app/Http/Kernel.phpの$middlewareGroups連想配列に追加しておく必要がある
// 'hello' => [
//    \App\Http\Middleware\HelloMiddleware::class,
// ],

バリデーション

バリデーションはコントローラ・モデル・フォームリクエストに定義することができる。

コントローラに定義する場合

コントローラのvalidateメソッドを使う
$this->validate($request, ['name' => 'required', 'max:10']);

validateメソッドはControllerクラスに組み込まれているValidatesRequestsというトレイト(メンバーをまとめてクラスに追加する仕組み)により提供されているメソッド。
※ エラーがあった場合は自動的に前の画面にリダイレクトされる

Viewでの表示
@foreach ($errors as $error)
    <p class="text-danger">{{ $error }}</p>
@endforeach

@if ($errors->has('msg'))
    <p class="text-danger">{{ $errors->first('msg') }}</p>
@endif

モデルに定義する場合

モデルにfillableとrulesプロパティを定義
protected $fillable = ['name'];
public static $rules = [
  'name' => 'required|string|max:10'
];

fillableによるホワイトリスト指定ではなくguardedによるブラックリスト指定も可能だが、fillableを使う方が無難。

  • fillableの場合
    allメソッドでフォーム変数を全てfillメソッドに渡すことができる。
    $item->fill($request->all())->save();
  • guardedの場合
    onlyメソッドやexceptメソッドで必要な項目を絞り込む必要がある。
    $item->fill($request->except('_token'))->save();
    $item->fill($request->only(['name', 'password']))->save();

フォームリクエストに定義する場合

コントローラのvalidateメソッドは自分で呼び出さなければならない。
フォームリクエストの場合、呼び出しは不要。
エラーがあった場合に自動的に前の画面にリダイレクトされる点や、Viewでの表示方法はvalidateメソッドと同じ。

フォームリクエストを作成

php artisan make:request HelloRequest

作成されたHelloRequestクラスにはauthorizeメソッドとruleメソッドが用意されている。
authorizeメソッドは認証をチェックするためのもので、特にやることがない場合はreturn true;でOK。

HelloRequest.phpのruleメソッドでルール配列を返却
public function rules()
{
    return ['msg' => ['required', 'max:10']];
}
messagesメソッドをオーバーライドすることでエラーメッセージ を変更することができる
public function messages()
{
    return [
        'msg2.required' =>'メッセージ2は必須です。'
    ];
}

独自バリデータ

「エラー時にリダイレクトさせたくない」「フォームに値以外をチェックしたい」場合などは、Validator::make()で独自バリデータを生成して使用する。

$validator = Validator::make($request->all(), ['name' => 'required'], ['name.required' => '名前が入力されていません']);
if ($validator->fails()) {
    return redirect('/hello')->withErrors($validator)->withInput();
}

ユーザー認証

Laravel 5.8 までは、php artisan make:authだけでログイン機能を実装できたが、 Laravel 6.0 から手順が変わった。

php artisan migrateでusersテーブル、password_resetsテーブルが作成される。
※ Laravelインストール時にマイグレーションファイル、コントローラ(App\Http\Controllers\Auth)は作成されている。

composer require laravel/ui:^1.0 --dev
php artisan ui bootstrap --authまたはphp artisan ui vue --auth
※ laravel/uiのバージョンはLaravelのバージョンで異なる。
https://laravel.com/docs/6.x/frontend#introduction
https://laravel.com/docs/7.x/frontend#introduction

データベース

クエリビルダ

DB::table('テーブル名');でBuilderインスタンスを取得することができる

  • DB::table('users')->get();
    戻り値はstdClassのCollection
  • DB::table('users')->first();
    戻り値はstdClass

Builderインスタンスのwhere, orWhere, whereRaw, orderBy, offset, limitなどのメソッドでSQLを組み立てる。
get, first, insert, update,deleteなどのメソッドでSQLを実行する。

モデル(Eloquent)

Task::orderBy('id', 'desc')のようにモデルのクラスメソッドを使ってBuilderインスタンスを取得する。
モデルはクエリビルダのインスタンスメソッドに相当する(同じメソッド名の)クラスメソッドを持っている。

DBクラスから取得したクエリビルダはIlluminate\Database\Query\Builderだが、モデルから取得したクエリビルダはIlluminate\Database\Eloquent\Builderである。
そのため、モデルから取得したBuilderインスタンスの場合、取得したデータの型はモデルとなる。
(DBクラスの場合はstdClass)

  • Task::get();
    戻り値はTaskのCollection
  • Task::first();
    戻り値はTask

データの取得

クラスメソッドall, findを使う、またはwhereやorderByなどのクラスメソッドでBuilderインスタンスを取得してからgetまたはfirstメソッドを実行する。

  • Task::all();
  • Task::where('name', 'hoge')->get();

データの登録・更新・削除

登録・更新はモデルのsaveメソッド、削除はdeleteメソッドを使う。

// 登録
$task = new Task();
$task->name = 'hoge';
$task->save();

// 更新
$task = Task::find(1);
$task->name = 'hoge';
$task->save();

// 削除
$task = Task::find(1);
$task->delete();

hasメソッド, dosentHaveメソッド

hasOne, hasMany, belongsToでリレーションを定義した場合、hasメソッド, dosentHaveメソッドで関連レコードが存在する(しない)データだけを取得することができる。

whereHas, orWhereHasメソッドで条件を指定することもでき、joinメソッドを使うよりも簡単な記述が可能となる。

User::has('tasks')->get();

User::dosentHave('tasks')->get();

User::whereHas('tasks', function (Builder $query) {
    $query->where('title', 'like', '%hoge%');
})->get();

Eagerロード

N+1問題を回避することができる。
例えば、usersテーブルとtasksテーブルがhasManyの関係である場合、User::all()とすると、まずUserが全て取得され、その後Postが1つずつ取得される。
よって、postsが100件あった場合は101回クエリが実行されてしまう。 User::with('tasks')->get();
とすると内部でWHERE INが使われるためクエリは2回しか実行されない。

ページネーション

クエリビルダインスタンスのpagenateメソッド、simplePagenateメソッドを使う。
$tasks = Task::where('id', '>', 100)->pagenate(10);

ビューでは{{ $tasks->links() }}のようにする。
パラメータを引き継ぐ場合はappends()メソッドを使って{{ $tasks->appends(['name'=>$name])->links() }}のようにする。

コメント