がんばるぞ

がんばります

route()の第二引数にはEloquentを渡そう

route('foo.bar', $eloquent->id)

みたいなことをしている人は

route('foo.bar', $eloquent)

といった感じで、Eloquentごと渡した方が良いですよ、という話。

なぜか

<?php
// vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php

    public function formatParameters($parameters)
    {
        $parameters = Arr::wrap($parameters);

        foreach ($parameters as $key => $parameter) {
            if ($parameter instanceof UrlRoutable) {
                $parameters[$key] = $parameter->getRouteKey();
            }
        }

        return $parameters;
    }

これは、 route() でURLを生成する処理の中で、URLパラメータを取得している部分である。

<?php
if ($parameter instanceof UrlRoutable) {
    $parameters[$key] = $parameter->getRouteKey();
}

ここで、$parameterがUrlRoutableである場合は getRouteKey とかいうメソッドが呼ばれているのがわかる。

これは Illuminate\Contracts\Routing\UrlRoutable というインターフェースで、Eloquentクラスを見てみると

<?php
// vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
    ...
}

ばっちり実装されているのがわかる。

つまり、Eloquentを渡すと getRouteKey という関数からURLパラメーターを返せるようになる

何が嬉しいのか

例えば、今まではURLのパラメータにautoincrementsなidを使用していたが
何らかの理由でuuidに変更したくなったとする。

そうするとEloquentの getRouteKeyName というメソッドをオーバーライドするだけで、全ての route() に修正が適用されることになる。 ※getRouteKeyメソッドではない

つまり、route() メソッドを使用してる場所を全検索などをせずに済む

これが嬉しい。

試しにやってみる

php artisan tinker で軽く実験

まずはデフォルトの状態。

>>> use App\User;
>>> $user = new User;
=> App\User {#2908}
>>> $user->id = 1;
=> 1
>>> $user->uuid = 'foo-bar';
=> "foo-bar"
>>> route('user.edit', $user);
=> "http://localhost/user/1"

次に、RouteKeyNameをオーバーライドする

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function getRouteKeyName()
    {
        return 'uuid';
    }
}

もっかい。php artisan tinker を実行

>>> use App\User;
>>> $user = new User;
=> App\User {#2908}
>>> $user->id = 1;
=> 1
>>> $user->uuid = 'foo-bar';
=> "foo-bar"
>>> route('user.edit', $user);
=> "http://localhost/user/foo-bar"

おっけー!