Laravel5.5でphp artisan migrate
した時に下記のエラーが発生することがあります。
$ php artisan migrate
[Illuminate\Database\QueryException]
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long;
max key length is 767 bytes
(SQL: alter table `users` add unique `users_email_unique`(`email`))
[PDOException]
SQLSTATE[42000]: Syntax error or access violation:
1071 Specified key was too long; max key length is 767 bytes
エラーの発生する理由
Laravelでは、バージョン5.4以降で、MySQLのデフォルトキャラクタセットがutf8mb4に変更されました。Laravelがデフォルトのキャラクタセットをutf8mb4に変更した理由は、UTF8の絵文字などをDBに正しくストア出来るようにしたためです。
utf8mb4のキャラクタセットだとデータを格納する際、1文字当たり4byteの領域が必要です。また、Laravelのマイグレーションではstring型のカラムを作ったときに、varchar(255)としてデータ型を指定します。utf8mb4のcharasetで255文字を格納する列を作った場合、データ長は最大255*4=1020byteになります。
一方で、MySQLのVer5.7.7以前の場合、テーブルのIndexに指定できるデータ長は最大767byteです。
このため、Ver5.7.7以前のMySQLに対してテーブルの作成を行うと、MySQLが許容するIndexのデータ長767Byteを超えてしまい、エラーmax key length is 767 bytes
が発生します。
対応方法
対処法としては、"MySQLのバージョンを5.7.7以降にアップデートしたり、DBのcharasetをutf8mb4にしない"という選択肢もありますが、文字列型の列を作る時にデフォルトのデータ長を変更してしまうのが最も簡単です。
Laravelのマイグレーションで、create tableする時に、varcharのデータ長を191文字になるよう変更します。191文字にすると、191*4=764byteなのでindexの最大長である767byte以内に収まります
変更の対象ファイルですが、app\Providers\AppServiceProvider.phpをエディタで開いてください。
AppServiceProvider.phpを見ると、以下のようにboot()関数が空になっているので...
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
...
ここに、Schema::defaultStringLength(191);
の1行を追加すればよいです。
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
\Illuminate\Support\Facades\Schema::defaultStringLength(191);
}
...
再実行しても、Table 'users' already existsエラーが出るときは...
一度"Specified key was too long"エラーが出た後、前述の対処を行い、再度php artisan migrate
を実行すると、引き続き下記のエラーが出る場合があります。
[Illuminate\Database\QueryException]
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' already exists
(SQL: create table `users` (`id` int unsigned not null auto_increment primary key,
`name` varchar(191) not null, `email` varchar(191) not null, `password` varchar (191) not null,
`remember_token` varchar(100) null, `created_at` timestamp null, `updated_at` timestamp null)
default character set utf8mb4 collate utf8mb4_unicode_ci)
これは、前回の実行でcreate tableによるテーブル作成だけ行われindexの作成に失敗したためで、再度php artisan migrate
を実行したとき、既にテーブルが存在しているためです。
上記の例だとusers
テーブルが問題になっているので、下記のSQLを実行してテーブルを手作業で削除してあげる必要があります。
drop table users;
テーブルをdropした後、再度php artisan migrate
を実行すれば、今度こそは大丈夫なはずです。
とても助かりました。
Thank you!!