You are here
Home > !Laravel > routesを使いこなす(5)モデルとのバインディング

routesを使いこなす(5)モデルとのバインディング

ユーザーがアクセスするURLを理解して、必要な関数にマップするのがroutes.phpの基本的な仕事です。

それらのURLには、以下のようにいろいろな形があります。


http://localhost/admin/login
http://localhost/admin/product/156
http://localhost/admin/product/156/edit

さて、上の例の156の数字は、DBテーブルのproductの主キーの値なのですが、Laravelはこの値をどのようにコントローラに取り込むのでしょう?

まず、前回のroutes.phpの設定を見てみましょう。

Route::resource('product', 'ProductController');

は、

php artisan route:list

の出力では以下のようなマップになります。

+--------+-----------+------------------------------------+------------------------+-----------------------------------------------------------------------+-----------------+
| Domain | Method    | URI                                | Name                   | Action                                                                | Middleware      |
+--------+-----------+------------------------------------+------------------------+-----------------------------------------------------------------------+-----------------+
|        | POST      | admin/product                      | admin.product.store    | App\Http\Controllers\Admin\ProductController@store                    | web             |
|        | GET|HEAD  | admin/product                      | admin.product.index    | App\Http\Controllers\Admin\ProductController@index                    | web             |
|        | GET|HEAD  | admin/product/create               | admin.product.create   | App\Http\Controllers\Admin\ProductController@create                   | web             |
|        | GET|HEAD  | admin/product/{product}            | admin.product.show     | App\Http\Controllers\Admin\ProductController@show                     | web             |
|        | DELETE    | admin/product/{product}            | admin.product.destroy  | App\Http\Controllers\Admin\ProductController@destroy                  | web             |
|        | PUT|PATCH | admin/product/{product}            | admin.product.update   | App\Http\Controllers\Admin\ProductController@update                   | web             |
|        | GET|HEAD  | admin/product/{product}/edit       | admin.product.edit     | App\Http\Controllers\Admin\ProductController@edit                     | web             |
+--------+-----------+------------------------------------+------------------------+-----------------------------------------------------------------------+-----------------+

URIの部分を見てください。

例えば、7行目の

admin/product/{product}

は、

ProductController@show

にマップされています。

そして156のIDは、この{product}の部分に対応します。

admin/product/{product}/edit

も同じことです。

対応するコントローラのメソッドを見ると、

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Models\Product;

class ProductController extends Controller
{
    /**
     * 表示画面
     *
     * @param  \App\Models\Product $product
     * @return \Illuminate\Http\Response
     */
    public function show(Product $product)
    {
        return $product->name; //画面に商品名を表示
    }
...

showのパラメータが、数字でなくモデルのProductのタイプになっていますね。
これは、Laravelが自動的に、IDの数字をもとに、

$product = Product::find(156);

のようなモデルバインディング(紐づけ)操作を行って、Eloquentのオブジェクトを生成して、メソッドの中で使用できるようにしてくれているのです。便利ですね。

さて、product_id = 156に対応するDBレコードがない場合はどうなるのでしょう?

Sorry, the page you are looking for could not be found.
2/2 NotFoundHttpException in Handler.php line 102: No query results for model [App\Product]. 

1/2 ModelNotFoundException in Builder.php line 290: No query results for model [App\Product]. 

「検索結果が空」の404エラーとなります。

今度は、上のshowの関数のパラメータ名を以下のように変えたとしたら、どうなるのでしょう?

    public function show(Product $a_product)
    {
        return $a_product_name;//画面は空
    }

この場合、$a_project_nameにはDBレコードが入らず、単にProjectの新規オブジェクトとなり、画面には何も表示されません。

つまり、パラメータ名は、{product}とまったく同じ名前である必要があるということです。ミススペルに気をつけましょう。

さて、今度は、product_idではなく、例えば、skuという商品番号の項目の値でレコードを引っ張ってきたいときはどうするのでしょう? 

つまり、

http://localhost/admin/product/ABCDE

でアクセスしたい。そこでは、ABCDがskuの値とします。

その場合は、Productのモデルの定義で、

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $table = 'product';
    protected $primaryKey = 'product_id';

    public function getRouteKeyName()
    {
        return 'sku';
    }
  ...
}

のように、getRouteKeyNameの関数を作成して、skuをリターンすれば、product_idの代わりに以下のようにオブジェクトを作成してくれます。


$product = Product::where('sku', 'ABCD')->first();

もちろん、skuは、productのDBテーブルで、主キーと同様に1つのレコードを特定するためにユニークなキーを持つ必要あります。

最後に、

http://localhost/admin/product/156?print=Y

の場合、printの値はどう取ってくるのでしょう?

これはshowメソッドを以下のように変更して、

...
    /**
     * 表示画面
     *
     * @param  \App\Models\Product $product
     * @param  \Illuminate\Http\Request
     * @return \Illuminate\Http\Response
     */
    public function show(Product $product, Request $request)
    {
        $print = $request->input('print');

        return $print;
    }
...

printの値の取得が可能です。ここ、関数のパラメータの順番はURLでの順番とは関係ありません。逆でも同じ結果となります。先に説明したように、{product}の名前と一致する変数名でLaravelは判断するからです。

Leave a Reply

Top