13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel の Route Model Binding で主キーをハッシュ化したものを使う

Posted at

はじめに

Laravel でアプリケーションを作っていく際に、何らかの理由で URL に主キーをそのまま表示させたくない場合どうしたらいいか、そこらへんを書いていきます。

例:
https://example.com/posts/1 ではなく
https://example.com/posts/Lk6-W7gbKmv にしたい場合。

環境

Laravel 6.x

インストールと設定

Laravel のインストール:

composer create-project --prefer-dist laravel/laravel foo

プロジェクトルートに移動:

cd $_

主キーをハッシュ化するのに便利なライブラリをインストール:

composer require vinkla/hashids

Laravel Hashids の設定ファイルを作成:

php artisan vendor:publish

リストが表示されるので Vinkla\Hashids\HashidsServiceProvider の番号を入力。これで config 以下に hashids.php が作成される。

.env ファイルに hashids で使う salt を追加:

HASHIDS_SALT=GkQDUgz4c8UmSY2P6nozchOz0fOxg5sk

php artisan tinker を使って Str::random(32); とかで値を生成する。APP_KEY と同様に絶対に公開してはいけない。

hashids の設定:

config/hashids.php
    'connections' => [
        'main' => [
            // ハッシュ化するときの魔法の粉
            'salt' => env('HASHIDS_SALT'),
            // ハッシュ値で使いたい文字列
           'alphabet'=> 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-',
            // ハッシュ値の長さ
            'length' => 11,
        ],
        // ...
    ],

tinker で確認してみる:

php artisan tinker
>>> Hashids::encode(1);
=> "Lk6-W7gbKmv"
>>> Hashids::decode('Lk6-W7gbKmv')[0];
=> 1

実際に使ってみる

MySQL を起動:

mysql.server start

データベースの作成:

mysql --login-path=local
mysql> create database laravel;

posts テーブル用のマイグレーションファイルを生成:

php artisan make:migration create_posts_table

生成されたマイグレーションファイルの編集:

database/migrations/xxx_creates_posts_table.php
class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('content');
            $table->timestamps();
        });
    }
}

テーブルの作成:

php artisan migrate

モデルとコントローラの作成:

php artisan make:controller PostController --resource --model=Post

ルートの登録:

routes/web.php
Route::resource('posts', 'PostController');

データを 1 件挿入:

php artisan tinker
>>> App\Post::forceCreate(['content' => 'foo']);

Route Model Binding に Hashids を適用:

app/Post.php
use Illuminate\Database\Eloquent\Model;
use Vinkla\Hashids\Facades\Hashids;

class Post extends Model
{
    // ...
    public function getRouteKey(): string
    {
        return Hashids::encode($this->getKey());
    }

    public function resolveRouteBinding($value): ?Model
    {
        $value = Hashids::decode($value)[0] ?? null;

        return $this->where($this->getRouteKeyName(), $value)->first();
    }
}

tinker で確認してみる:

php artisan tinker
>>> route('posts.show', App\Post::find(1));
=> "http://localhost/posts/Lk6-W7gbKmv"

これでコントローラーやビューで主キーのエンコードやデコード処理をやらずとも、Route Model Binding を通して良い感じにやってくれる。

まとめ

Route Model Binding 自体便利だけど、コードの見通しが悪くなるのであまり好きではないが、ここまで簡単にできてしまう感じが「おーなんか Laravel らしいね」と思ってついつい記事にしてみました。

備考

上記の実装の場合、各テーブルの主キーのハッシュ値が同じになるので、テーブル毎にハッシュ値を設定したい場合はコネクションを複数用意して使うこともできる:

config/hashdis.php
    'default' => 'foo',
    'connections' => [
        'foo' => [
            'salt' => env('HASHIDS_SALT_FOO'),
            'alphabet' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-',
            'length' => 11,
        ],
        'bar' => [
            'salt' => env('HASHIDS_SALT_BAR'),
            'alphabet' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-',
            'length' => 11,
        ],

    ],
php artisan tinker
>>> Hashids::connection('foo')->encode(1);
=> "Lk6-W7gbKmv"
>>> Hashids::connection('bar')->encode(1);
=> "BnP-1Ll3wLA"

リンク

Hashids
Laravel Hashids
Route Model Binding - Laravel

13
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?