Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 25.06.2017 09:43:51

Миграция БД: Не могу ни создать, ни откатить

У меня есть две миграции по умолчанию (

2014_10_12_000000_create_users_table

и

2014_10_12_100000_create_password_resets_table

)  и ещё одна собственная:

class CreatePagesTable extends Migration {
    public function up() {
        Schema::create('pages', function (Blueprint $table) {   
            $table->increments('id');   
            $table->string('name')->unique();
            $table->string('title');
            $table->text('metadesc');
            $table->text('keywords');         
        });
    }

    public function down() {
        Schema::dropIfExists('pages');
    }
}

Когда я попытался сделать миграцию в первый раз, в консоли было:

> php artisan migrate
Migration table created successfully.

  [Illuminate\Database\QueryException]
  SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max ke
  y 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 ke
  y length is 767 bytes

Таблицы pages при этом в БД не появилось. Эксперимента ради я решил попробовать ещё раз сделать миграцию, и тогда получил

  [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` varc
  har(191) not null, `email` varchar(191) not null, `password` varchar(191) not null, `remem
  ber_token` varchar(100) null, `created_at` timestamp null, `updated_at` timestamp null) de
  fault character set utf8mb4 collate utf8mb4_unicode_ci)

  [PDOException]
  SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' already exists

Однако, когда я попробовал сделать откат, то получил

> php artisan migrate:rollback
Nothing to rollback.

> php artisan migrate:reset
Nothing to rollback.

Я пробовал это решение, безрезультатно:

public function boot()
{
    Schema::defaultStringLength(191);
}

Я хочу понять причину происходящего.

Данные моих инструментов:

  • Laravel Framework 5.4.22

  • Apache PHP-7

  • MySQL - 5.5

Не в сети

#2 26.06.2017 07:26:35

Re: Миграция БД: Не могу ни создать, ни откатить

Вот, мне помогло в свое время установка сравнения как `utf_general_ci`, если тебе нужна именно `utf8mb4` в ссылке решение.

Не в сети

#3 26.06.2017 11:02:53

Re: Миграция БД: Не могу ни создать, ни откатить

Благодарю Вас за ответ!
В результате экспериментов я-таки все миграции сделал, но причину предыдущих неудач не установил, потому пока не могу сказать, помогло ли Ваше решение. Если появится новая информация, я снова отпишусь в данной теме.

Не в сети

#4 26.06.2017 11:48:32

Re: Миграция БД: Не могу ни создать, ни откатить

utf_general_ci — такой кодировки в MySQL не существует. Есть utf8_general_ci, utf16_general_ci, utf32_general_ci и обычно используется первая. Однако если MySQL нормальной версии, а не раритет — лучше во всех случаях вместо них использовать utf8mb4_..., так как только она полностью поддерживает Юникод (в частности, emoji будут работать только там).

Кроме того, кодировка влияет на размер строки и ключа. В MySQL и то, и другое ограничено 767 байтами (о чём и указано в ошибке).

Поле переменной длины (VARCHAR/VARBINARY) в данном случае по смыслу равно полю фиксированной длины (CHAR/BINARY). Это значит, что его размер задаёт максимальный размер поля, то есть MySQL предполагает, что значения действительно будут достигать этой длины. Это имеет несколько последствий, два самых важных из которых:

  1. При создании ключа, MySQL считает, превысит ли ключ лимит на основе максимальных длин всех полей в ключе.
  2. При выполнении запросов MySQL выделяет буферы памяти соответственно максимальным длинам полей (вот почему не стоит указывать длину поля в 10 раз больше, чем реальные значения или использовать TEXT, где хватает короткого VARCHAR).

В случае Gleb2708, видимо, имеет место быть первая проблема (хотя я не вижу кода CREATE TABLE users, который выдал ошибку в первый раз). utf8_general_ci это урезанная («облегченная») версия Юникода, где символ может занимать 1-3 байта. Соответственно, если у нас поле с максимальной длиной 255 символа (не байт!), то:

255 * 3 = 765 + 2 = 767

Здесь 3 — максимальная длина каждого символа (по верхней планке), 2 — длина значения-строки в байтах (т.к. поле с переменной длиной, длина значения для каждой строки данных хранится в виде двух байт). Итого получаем, что 255 при utf8_general_ci есть максимальная длина, при которой такое строковое поле может использоваться в виде ключа.

Если ключ составной (состоит из нескольких полей), то аналогичным образом складываются длины всех полей в нём — они не должны быть больше 767 байт.

utf8mb4_general_ci — это реализация UCS-4 (UTF-32), где каждый символ занимает 1-4 байта. Соответственно, длина такого поля ещё меньше:

191 * 4 = 764 + 2 = 766

Длина поля также влияет на длину строки данных в таблице, но там расчёт другой. Общая идея та же — указанная длина должна быть максимально близкой к длине настоящих данных, это лучше во многих отношениях.


Тем не менее, максимальная длина поля не обязательно напрямую зависит от того, используется оно в ключе или нет. При создании ключа можно указать длину, которая будет индексироваться и она может быть меньше полной длины поля (в этом случае индекс будет работать, но менее эффективно для строк, которые длиннее длины поля, указанной для ключа). Расчёт для максимальной длины индекса тот же, что и выше.

Таким образом, если ты создаёшь поле email varchar(N) collate utf8mb4_unicode_ci и потом создаёшь ключ на нём без указания длины, то MySQL берёт длину поля (N) и при N >191 ты получишь ошибку (причём не при создании таблицы, а при добавлении туда ключа). Для полей TEXT/BLOB при создании ключа обязательно нужно указывать длину, так как максимальная длина таких полей заведомо очень большая (за исключением TINYTEXT/TINYBLOB она начинается от 65 Кб).

Вариантов это решить два, даже три:

  1. Уменьшить длину email — много ли существует адресов с длиной 191 символ?
  2. Использовать latin1_general_ci — хотя в теории в адресе можно использовать любые символы Юникода, я ни разу таких адресов не видел. ИМХО, лишняя возможность создать проблемы.
  3. Указывать длину 191 или короче для ключа по email.

Не в сети

#5 26.06.2017 13:19:13

Re: Миграция БД: Не могу ни создать, ни откатить

Ну, простите, очепятался.

Не в сети

Подвал раздела