LoginSignup
13
6

More than 3 years have passed since last update.

【Laravel】テーブル名が長い時に、Laravel7から導入された外部キー制約をつけるマイグレーションエイリアスを使う方法

Posted at

久しぶりのQiitaへの投稿です。

未経験からエンジニアに転職をして、2年目に突入したバックエンドエンジニアです。
DBに外部キー制約を追加をしようと思い、Laravel7から使えるエイリアスでmigrationファイルを作成していました。
ただ、テーブル名が長い時にハマってしまい、できる人には当たり前すぎる内容かもしれませんが、振り返りの意味も込めて投稿します。

開発環境

  • 言語:PHP7.4.8
  • フレームワーク:Laravel7.28.3
  • DB:MySQL8.0.22

テーブル名が短い時

目標のテーブル構造

公式ガイドでもよく使われるような下記のようなテーブルを作り、postsテーブルのuser_idに外部キー制約をかけることを目指します。

image.png

Laravel7より前の時の記法

Laravel7より前のバージョンで外部キー制約をつけるためには、下記のようにmigrationファイルを作成をしていました。
一度user_idのカラムを作成してから別の行で外部キー制約をつけています。

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users');
        });

image.png

そんなに難しくもないですが、少しだけ面倒です。

Laravel7以降で使えるエイリアスを使用

そこでLaravel7以降では下記のようなエイリアスができ、foreignIdconstrained()を使えば一行でも外部キー制約をかけることができるようになりました。
短くて楽ですね。

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained();
            $table->timestamps();
        });

image.png

テーブル名が長い時

目標のテーブル構造

問題のパターンです。下記のようなテーブルを想定して試してみます。
名前は適当に僕が好きなスポーツからとってきましたが、とりあえず2つのテーブル名が長く文字数の合計が一定数を超えると同じ現象になります。

image.png

Laravel7以降で使えるエイリアスを使用

せっかく新しい記法があるということなので、foreignIdを使った新しいエイリアスを使ってやってみます。

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('national_basketball_association_team_id')->constrained();
            $table->timestamps();
        });

キー名が長すぎるようで下記のようなエラーが出てしまいました。

  SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name 'national_basketball_association_players_national_basketball_association_team_id_foreign' is too long (SQL: alter table `national_basketball_association_players` add constraint `national_basketball_association_players_national_basketball_association_team_id_foreign` foreign key (`national_basketball_association_team_id`) references `national_basketball_association_teams` (`id`))

通常であれば、Laravelのスキームビルダの制約命名規則に従い、自動的に外部キー制約の名前は変換されるため、外部キー制約名は下記のようになります。

(接続元テーブル名)_(外部キー名)_foreign

しかし、今回この命名規則に当てはめようとすると、national_basketball_association_players_national_basketball_association_team_id_foreignという名前になりますが、これは文字数が長すぎるということでエラーになってしまいました。

Laravel7以降で使えるエイリアスを使用(nameでキー名変更)

同じようなことが他の制約を作る時でも起こっていたのを思い出します。
nameメソッドを使えば直せたような気がするのでnameメソッドを使って試してみました。
外部キー制約名は省略した、basketball_players_team_id_foreignを設定します。

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('national_basketball_association_team_id')->constrained()->name('basketball_players_team_id_foreign');
            $table->timestamps();
        });

先程と違い、今度はエラーも起こらず、migrationファイルが無事に通りました。
念のためにdatabaseを確認してみます。

image.png

外部キー制約がついていません。。。
多分routeを作成する時に使うnameメソッドと勘違いしていたのですが、ここで結構ハマってしまいました。

migration関連でnameメソッドは用意されていなさそうでした。

エイリアスメソッドのコードを確認

改めて今回のエイリアスのforeignIdメソッドを確認してみます。

    public function foreignId($column)
    {
        $this->columns[] = $column = new ForeignIdColumnDefinition($this, [
            'type' => 'bigInteger',
            'name' => $column,
            'autoIncrement' => false,
            'unsigned' => true,
        ]);

        return $column;
    }

引数がカラム名のみで1つしか受け付けていないので、キー名を短くして渡そうとしても無理そうです。

また、foreignIdの後に使っているconstrainedも確認してみます。

    public function constrained($table = null, $column = 'id')
    {
        return $this->references($column)->on($table ?? Str::plural(Str::beforeLast($this->name, '_'.$column)));
    }

こちらも外部キー制約名を渡す引数がなく、無理そうです。

結果:Laravel7以降で使えるエイリアスを使用(indexでキー名変更)

下記のようにindexメソッドを使って、外部キー名を指定するとうまくいきました。

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->foreignId('national_basketball_association_team_id')->constrained()->index('basketball_players_team_id_foreign');
            $table->timestamps();
        });

公式ガイドを見てみるとindexメソッドは「基本的なインデックス追加」とだけ書いてありましたが、外部キー制約の名前を作成をする際にも使えるようです。
uniqueキー作るときは、第2引数にインデックスキー名指定できるのに対して、外部キー制約の場合はindexメソッドを使わないといけなさそうなので書き方が結構違いますね。

image.png

※何か誤解していたり、他にいい方法があれば教えていただけると助かります!!!

Laravel7より前の時の記法でやる方法

もちろん元々使っていた記法を使って、foreignメソッドの中の第2引数に外部キー制約名を指定してもうまくいきます。
このやり方は元々やったことあったのですが、ハマるくらいなら最初からこっち使っておけばよかったなと思いました、、、

        Schema::create('national_basketball_association_players', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->unsignedBigInteger('national_basketball_association_team_id');
            $table->foreign('national_basketball_association_team_id', 'basketball_players_team_id_foreign')->references('id')->on('national_basketball_association_teams');
            $table->timestamps();
        });

image.png

感想

チュートリアルで使うようなusersとかpostsとかのテーブルと違い、実務ではテーブル名が長くなるケースは命名規則によりけりだと思いますが、結構発生しそうなイメージなので意外と困ることもあるかなと思います。

また、今回少し深く調べてみて、フレームワークのコードをより深くみるようにしたのはよかったなと思いました。

それにしてもnameメソッドを使ったときに何故かmigrationファイル通るのに外部キー制約がついていないのは結構たちが悪かったです。。。

13
6
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
6