localdisk

PHP とか Java とか Web とか好きなことを書きます。

Laravelでプライマリキーを使った1:1関連のテーブル分割で自動採番をしないようにする

を Laravel でも同じようにできるよな。と思って書いてみたらちょっとハマったので Laravel 使っている人は気をつけましょうという話。

問題はカラム毎に charset の指定ができないので、カラム長を 255 にして utf8mb4 を default charset にするといわゆる 767bytes 問題が発生する。

参考
MySQLのPK・UNIQUEのフィールド長制限に立ち向かう - treeのメモ帳

解決方法としては

  1. DB::statement を使用して ALTER TABLE してカラムの charset を指定する。
  2. カラム長を短くする。
  3. utf8mb4 をやめる。

2 と 3 は 根本的な解決にはなりませんよね?*1 というわけで 1 の方法で解決をはかりましょう。

マイグレートファイル

users
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateUsersTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
        Schema::create('users', function(Blueprint $table) {
            $table->increments('id');
            $table->string('email')->unique();
            $table->string('password');
            $table->timestamps();
            $table->index('email');
        });
        DB::statement('ALTER TABLE users MODIFY email varchar(255) CHARACTER SET ascii COLLATE ascii_bin');
        DB::statement('ALTER TABLE users MODIFY password varchar(255) CHARACTER SET ascii COLLATE ascii_bin');
	}


	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
	    Schema::drop('users');
	}

}
profiles
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateProfilesTable extends Migration {

	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
        Schema::create('profiles', function(Blueprint $table) {
            $table->integer('id')->unsigned();
            $table->string('name')->nullable();
            $table->tinyInteger('gender')->nullable(); 
            $table->dateTime('birthday')->nullable();
            $table->timestamps();
            $table->primary('id');
        });
	}


	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
	    Schema::drop('profiles');
	}

}

で OK です。

最後に

migrate の SQL を確認したいときは pretend オプションをつけると確認できます。
上記 2 テーブルのマイグレート SQL はこんな感じです。

$php artisan migrate --pretend

CreateUsersTable: create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null, `password` varchar(255) not null, `created_at` timestamp default 0 not null, `updated_at` timestamp default 0 not null) default character set utf8 collate utf8mb4
CreateUsersTable: alter table `users` add index users_email_index(`email`)
CreateUsersTable: alter table `users` add unique users_email_unique(`email`)
CreateUsersTable: ALTER TABLE users MODIFY email varchar(255) CHARACTER SET ascii COLLATE ascii_bin
CreateUsersTable: ALTER TABLE users MODIFY password varchar(255) CHARACTER SET ascii COLLATE ascii_bin
CreateProfilesTable: create table `profiles` (`id` int unsigned not null, `name` varchar(255) null, `gender` tinyint null, `birthday` datetime null, `created_at` timestamp default 0 not null, `updated_at` timestamp default 0 not null) default character set utf8 collate utf8mb4
CreateProfilesTable: alter table `profiles` add primary key profiles_id_primary(`id`)

(追記)モデルの実装

モデルではこんな感じ。hasOne 及び belongsTo の第2引数がポイントです。

<?php
class User extends Eloquent
{
    public function profile()
    {
        return $this->hasOne('Profile', 'id');
    }
}
class Profile extends Eloquent
{
    public function user()
    {
        return $this->belongsTo('User', 'id');
    }
}

$profile = User::find(1)->profile;

*1:ぜひ、リンクを踏んでイラッとしていただきたい