気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM等について、学んだことや備忘録をアウトプットとして残し、情報を必要としている誰かにとって「かゆいところに手が届く」ブログとなることを願いながら記事を書いています。

【Laravel5.5】超人気PHPフレームワークLaravelに入門してきました!-part2

スポンサーリンク

今日もタスクリストを確認しながら、Laravelの勉強をしてました。

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

成果物:タスクリストを確認しながら作成したソースコード

https://github.com/rinne-grid/my-laravel-quick-restart

まとめ

  • 5.1向けのドキュメント(中級者向けタスクリスト)と、5.5での表記で異なるところがあり、だいぶハマった。
  • Laravelとは全く関係ないところで、時間を費やした(仮想環境のMySQLサーバに対して、GUIクライアントから接続したかったけど諦めた)

まとめについて

  • ルートモデルバインディングの書き方が違った
<?php
// app/Providers/RouteServiceProviders.php

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

// 5.5 
Route::model('user', App\Task::class);
// ↑で駄目だった。↓はOKだった
Route::model('task', 'App\Task');
  • モデルと、モデルの認可ポリシーを関連付けるコードの書き方が違った
<?php
// app/Providers/AuthServiceProvider.php

// 5.1 
protected $policies = [
    Task::class => TaskPolicy::class,
];

// 5.5
protected $policies = [
    'App\Task' => 'App\Policies\TaskPolicy',
];

備忘録やメモ

Eloquent ORMの複数代入について

フォームの入力名とデータベースのカラム名を同じにして、 入力値をデータベース(もしくはORMがインスタンスのモデル)へ一気に代入してしまおうと言うアイデア

引用: Eloquentの複数代入のリスク https://kore1server.com/336/Eloquent%E3%81%AE%E8%A4%87%E6%95%B0%E4%BB%A3%E5%85%A5%E3%81%AE%E3%83%AA%E3%82%B9%E3%82%AF

  • 個人的なイメージ
    • $fillableはホワイトリスト(ここに登録したプロパティは複数代入できる)
    • $guardedはブラックリスト(ここに登録したプロパティには複数代入できない)

ルーティング

<?php
Route::get('/tasks', 'TaskContrller@index');
Route::post('/task', 'TaskController@store');

コントローラでのオブジェクトの作成

  • Laravelリレーションでは、createメソッドが準備されている
  • このメソッドは、属性の配列を受け取り、データベースに保存する前に、関連するモデルの外部キー値を自動的に設定する
  • 以下の場合は、$result->user()でアクセスできる現在の認証済みユーザのIDを指定したタスクのuser_idプロパティに自動的に設定する
<?php
$request->user()->tasks()->create([
  'name' => $request->name,
]);

viewやredirectでレスポンス(ビュー)を返す

  • view: resources/views直下のディレクトリを基準としたViewのパスを指定する

    • return view('tasks.index');
  • redirect: Routeに記載した、コントローラメソッドとURIの紐付けにおいて、URI部分を指定する

    • return redirect('/tasks');

タスク削除の実装

  • Routeでは、Route::delete('/task/{task}', 'TaskController@destroy')を定義した
  • しかし、削除するボタンはpostで定義している
    • HTMLフォームはPOSTとGETのみを許可しているため、フォームのDELETEリクエストを見せかける手段が必要になる
    • method_field('DELETE')関数を出力した結果は下記のようになる
<input type="hidden" name="_method" value="DELETE">

ルートモデル結合

app/Providers/RouteServiceProviders.phpのbootメソッド内に、 Route::model('task', App\Task);を追加することで 「ルートの宣言に、{task}があれば、指定されたIDに対するTaskモデルを取得する」ように指示が可能

5.1の$router->modelでは動作しなかったため調査したところ・・・

5.3からRouteで指定するように変更になった様子。

<?php
public function boot(Router $router)
{
    parent::boot($router);

    $router->model('user', 'App\User');
}
<?php
public function boot(Router $router)
{
    parent::boot($router);

    $router->model('user', 'App\User');
}
<?php
public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

しかし、5.3の表記を試しても 「Class App\Providers\App\Task does not exist」 が発生してしまったため、最終的に下記を指定した。

<?php
public function boot()
{
    parent::boot();

    Route::model('task', 'App\Task');
}

認可

認証済みユーザが任意のタスクIDを指定することで、他のユーザのタスクを削除してしまうという 悪意のあるリクエストを仕込むことができてしまう。 ルートに注入されたTaskインスタンスが実際に認証済みのユーザが所有していることを 確認するため、Laravelの認可機能を利用する。

php artisan make:policy TaskPolicy

app/PoliciesにTaskPolicy.phpファイルが作成される

  • destroyに関するポリシーを記述
<?php
    /**
     * @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プロパティに加える

書き方が変わっていたこともあり、1時間くらいハマってしまった。

<?php
    protected $policies = [
        'App\Task' => 'App\Policies\TaskPolicy',
    ];
  • コントローラのdestroyメソッド内から、authorizeメソッドを実行する
    • $this->authorize('destroy', $task);
    • 最初の引数:呼び出したいポリシー名、検証したいインスタンス
    • 現在のユーザについては、自動的にポリシーメソッドに送られてくるので、指定不要
    • アクションが非許可の場合は、403例外が投げられ、ユーザにエラーページが表示される

新しく学んだことの整理

  • モデルからデータを取得するパターン等で、Route::post('/hoges/{hoge}', 'HogeController@detail')に hogeインスタンスを割り当てたい場合
    • ルートモデルバインディングを行い、IDをモデルに渡して取得を内部的に実行してくれる
    • ルートモデルバインディングに関する設定はapp/Providers/RouteServiceProvidersのbootメソッド内に記述する
  • ユーザから悪意のあるリクエスト等、別ユーザのデータを削除することを禁止するため、認可を利用する
    • php artisan make:policy PolicyNameでポリシーの作成を行う
    • app/Policies/PolicyName.phpファイルが作成される
    • ポリシー用のメソッドを記述し、ユーザインスタンスと検証したいインスタンスを引数に指定する
      • return $user.id=== $task.user_id;などを指定。もしfalseを返すと403例外を投げる
    • AuthServiceProviderのpolicies配列の中に、モデル => モデルポリシーの形式で記述する