以前に、Laravel 5.3 コントローラのコンストラクタの重要な変更として、コントローラで定義するメソッド間で共有するコードをコンストラクタに入れることが可能なことを説明しました。今度は同じコンストラクタ内で、コントローラのメソッドに渡される引数の取り出しかたを説明します。何を言っているかというと、まずは準備から。
準備
例として、routes/web.phpにおいて、以下のようなrouteを定義します。
...
Route::resource('product', 'ProductController');
...
これは、以下のようなURIを生成します。
+--------+-----------+-------------------------------+-----------------------+------------------------------------------------------------------------+----------------------------------------------+
| Domain | Method    | URI                           | Name                  | Action                                                                 | Middleware                                   |
+--------+-----------+-------------------------------+-----------------------+------------------------------------------------------------------------+----------------------------------------------+
|        | GET|HEAD  | product                       | product.index         | App\Http\Controllers\ProductController@index                           | web                                          |
|        | POST      | product                       | product.store         | App\Http\Controllers\ProductController@store                           | web                                          |
|        | GET|HEAD  | product/create                | product.create        | App\Http\Controllers\ProductController@create                          | web                                          |
|        | GET|HEAD  | product/{product}             | product.show          | App\Http\Controllers\ProductController@show                            | web                                          |
|        | PUT|PATCH | product/{product}             | product.update        | App\Http\Controllers\ProductController@update                          | web                                          |
|        | DELETE    | product/{product}             | product.destroy       | App\Http\Controllers\ProductController@destroy                         | web                                          |
|        | GET|HEAD  | product/{product}/edit        | product.edit          | App\Http\Controllers\ProductController@edit                            | web                                          |
+--------+-----------+-------------------------------+-----------------------+------------------------------------------------------------------------+----------------------------------------------+
そして、ProductController.phpを以下のように定義します。
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Product;
class ProductController extends Controller
{
   /**
     * 情報画面
     *
     * @param  \App\Models\Product  $product
     * @return \Illuminate\View\View
     */
    public function show(Product $product)
    {
     //
    }
   /**
     * 編集画面
     *
     * @param  \App\Models\Product  $product
     * @return \Illuminate\View\View
     */
    public function edit(Product $product)
    {
      //
    }
   /**
     * 編集保存
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Product  $product
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(Request $request, Product $product)
    {
      //
    }
}
コンストラクタで引数を取り出す
用意ができたところで、
例えば、
product/456
のGETのアクセスでは、id = 456に対応するProductのEloquentオブジェクトがshow()のメソッドの引数$productの値として渡されます。
そして、
product/456/edit
のGETは、id = 456に対応するProductのオブジェクトがedit()のメソッドの引数$productの値として渡されます。
さて、このid = 456のProductのオブジェクト、それぞれのメソッドの引数と渡される前に、コンストラクタで取り出すには?
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Product;
class ProductController extends Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->middleware(function ($request, $next) {
            $product = $request->product; //id = 456のProductのオブジェクトを取得
            debug(get_class($product), $product->id);
            return $next($request);
        });
    }
...
と、middleware()の関数を使えば、その匿名関数の中で簡単に取り出せますね!
product/456
のURIにブラウザでアクセスすれば、Debugbarでは、
debug App\Models\Product debug 456
とデバッグ文を表示します。
product/456/edit
でも
product/456/update
でも、同じようにそれぞれのメソッドを実行する前に、引数にアクセスすることが可能となります。
ひとつここで疑うのは、Productのオブジェクトを取得するために以下のようなSQL文が、middleware()で1回、そしてさらにshow()のようなメソッドの引数でもう1回、計2回実行されることになるか、です。
select * from product where id = 456
これもDebugbarのQueryの出力で1回しか実行されていないことを確認しました。一度作成したオブジェクトを同じコントローラ内で2度作成することはなりません。
取り出して、それからどうする?
ここが大事なのですが、例えば、この商品(product)がEコマースで販売される商品とします。商品の登録や編集は、裏側の管理画面で商品の製造元の店舗がログインして行います。しかし、複数の店舗が存在するので、店舗Aが店舗Bの商品を編集できては困ります。しかし、URIには商品のIDが表示されるので、IDの数字を変えて他の店舗の商品を閲覧(公開されていない情報がある)や編集することが可能となってしまいます。それを防ぐためには、
...
    public function __construct()
    {
        parent::__construct();
        $this->middleware(function ($request, $next) {
            $product = $request->product; //id = 456のProductのオブジェクトを取得 
            $user = auth('shop')->user(); // 現在ログインしている店舗のユーザー
            if ($product->shop_id != $user->shop_id) { //この商品の店舗ではない!
               abort(404);
            }
            return $next($request);
        });
    }
...
のように設定すれば、いちいちshow()やedit()やupdate()などで、同様なコードを繰り返さずに済みプログラムの管理性が高まる、ということです。
