{{Laracast Laravel 5 Fundamentals, 7, Migrations, 14.01.2015, 30.05.2016, https://laracasts.com/series/laravel-5-fundamentals/episodes/7}} %%(hvlraw) %% (0:00) Миграции БД - одна из самых мощных особенностей Laravel. Сначала миграция может показаться слегка устрашающей, если вы не знакомы с термином. Вот что я об этом думаю - это как контроль версий для вашей БД. Вспомните, если вам нужна была раньше некая таблица, допустим для статей. Как вы её создавали? И могу поспорить, вот что вы делали... Наверняка открывали какую-нибудь GUI-программу вроде Sequel Pro или похожую на неё. (0:30) И затем вы вручную создавали таблицу и определяли столбцы. Хорошо, с этим нет проблем. Но допустим вам нужно добавить второго члена в команду - как им получить ту же настройку вашей БД? Думаю вы экспортируете схему, отправите её им, и затем они импортируют её. Но теперь, что если вы сделали изменение в БД? Может быть вы изменили имя столбца. У нас появились проблемы, ведь все разработчики в вашей команде, особенно если они работают удалённо, должны убедиться, что их состояние БД точно такое же, как у всех. (1:00) Так что, как вы можете представить, это может быть довольно сложно. Не говоря уже о факте что ваша производственная БД должна быть также синхронизирована. Что если вместо всего этого мы могли бы предоставить все те же наборы инструкций нашей БД как класс PHP? Какие у этого преимущества? Я вам покажу. Тут есть парочка по умолчанию. Идём в %%(t)database%%, %%(t)migrations%%... и вы увидите пару включённых здесь. (1:30) Одну для создания таблицы %%(t)users%%, и одну для %%(t)password_resets%%. Теперь запомните, если в вашем приложении нет пользователей, то вы можете их удалить. Они тут не обязательны. Однако в большинстве приложений есть таблица %%(t)users%%, так что это может быть очень полезно. Теперь, если вы слегка ошеломлены и думаете: "Я не хочу создавать класс каждый раз как делаю изменения в БД. Это слишком долго." На самом деле - нет. Laravel содержит несколько генераторов, которые сильно ускоряют всё это дело. (2:00) Учитывая это, давайте посмотрим. У нас есть два метода, %%up()%% и %%down()%%. Так, значит для нашего метода %%up()%%, какие действия нам нужно предпринять? В нашем случае, мы создаём новую таблицу %%(t)users%%. И мы планируем эту таблицу так: ID c автоматическим увеличением (autoincrementing), имя - строка, email - уникальная строка, пароль - 60 символов, %%rememberToken()%% - он немного особенный, но пока не думайте о нём. (2:30) А также несколько временных штампов - когда этот пользователь был создан, когда последний раз обновлён. Но теперь, как насчёт метода %%down()%%? Вот одна из отличных вещей насчёт миграции. Допустим вы запустили миграцию для создания этой таблицы %%(t)users%%. Но затем, через 20 минут, вы осознали: "Ой, это должно быть %%(t)username%%, а не %%(t)name%%!" Что же, это довольно легко исправить. Всё что необходимо сделать - это откатить миграцию назад. (3:00) Я покажу вам как это сделать. Делаете изменение, перезапускаете миграцию, и готово! Больше ничего не нужно делать. Даже лучше, такие изменения и модификации являются частью вашей системы контроля версий, что очень даже круто. Но давайте оставим здесь просто %%(t)name%%. Теперь, у нас есть миграция, но значит ли это, что у нас уже есть таблица %%(t)users%%? Нет. Мы создали класс, но ещё не "мигрировали" БД. Давайте попробуем сделать это: %%(sh) php artisan migrate %% (3:30) И готово. Мы создали таблицы %%(t)users%% и %%(t)password_resets%%. Теперь давайте копнём глубже и посмотрим как это выглядит. Позволю себе предположить что на вашей машине уже установлен SQLite. Скорее всего он у вас есть. Если же нет, то вы можете проверить, запустив %%(t)sqlite3%%, и у вас должен выскочить запрос типа такого. Иначе, если вы Мак-пользователь, обратите внимание на Homebrew. Он позволит вам сделать: %%(sh) homebrew install sqlite %% (4:00) Очень полезно, и это же применимо для многих прочих мелких инструментов и компонентов, которые вы можете подтянуть. И конечно же, если вы повторяете все действия, но по какой-то причине не можете установить что-либо на своём компьютере - оставьте комментарий ниже и мы все постараемся помочь вам. Хорошо, давайте попробуем: %%(sh) sqlite3 storage/database.sqlite %% (я укажу путь к файлу). Хорошо, теперь посмотрим на таблицы: %%(t) .tables %% И вы увидите что у нас есть таблица %%(t)migrations%%. Её никогда не нужно трогать. Фреймворк Laravel использует её, чтобы решить есть ли новые классы миграции, которые нужно запустить, что требуется откатить, вещи типа того. (4:30) Но дальше у нас есть две созданные таблицы. Давайте посмотрим их структуру: %%(t) .schema %% И вы увидите обычный SQL-текст: %%(sql) CREATE TABLE "users"… %% и все эти поля были сгенерированы Laravel, основываясь на созданном нами классе миграции. Теперь вернёмся к тому примеру где мы говорили: "Что если мы дали название %%(t)name%%, а должно быть %%(t)username%%? Что нам сделать чтобы исправить это? " Я открою новую вкладку терминала и скажу: %%(sh) php artisan %% (5:00) Сначала дайте я вам покажу. Если я запущу эту строку, то вы увидите все возможные команды, которые можете запустить. В основном вы будете использовать вот эти три (%%(t)migrate:refresh%%, %%(t)migrate:reset%%, %%(t)migrate:rollback%%). Итак, я хочу откатить назад последнюю миграцию: %%(sh) php artisan migrate:rollback %% Считайте что это работает как "undo" (вернуться на шаг назад). Я только что запустил миграцию, но допустил ошибку и собираюсь откатить назад, поправить её и затем перезапустить миграцию. Теперь поправим ошибку. Исправим тут на %%(t)username%%: %% $table->string('username'); %% (5:30) Вернёмся и запустим: %%(sh) php artisan migrate %% И мы закончили. БД обновилась. Чтобы доказать вам: %%(sh) sqlite3 storage/database.sqlite %% %%(t) .schema %% Мы можем снова посмотреть на структуру. И теперь вы видите как изменения отражены здесь. Довольно круто, правда? Теперь, когда вы понимаете как запускать миграции и откатывать их, как насчёт создания новых? ОК, давайте продолжим с той простой идеей о статье. Мы хотим таблицу, чтобы хранить там все статьи вашего блога. (6:00) Очень просто. Давайте скажем: %%(sh) php artisan make:migration %% (я хочу создать новую миграцию) Не забывайте, что если вдруг забудете какие аргументы писать или в каком порядке, то просто перед этим добавьте %%(t)help%%: %%(sh) php artisan help make:migration %% Важный момент вот здесь - имя миграции. Считайте что это идентификатор для описания того, что эта миграция делает. Так, если я создаю таблицу %%(t)articles%%, то имя может быть: %%(t)create_articles_table%%. (6:30) Далее, вы увидите тут пару флагов, и они просто регулируют шаблон, который будет применяться при генерации вашего файла. Давайте попробуем: %%(sh) php artisan make:migration create_articles_table %% И раз я создаю таблицу, то передаю: %%(t)--create="articles"%%. Хорошо, давайте запустим это. И вот, я создал эту новую миграцию. Давайте рассмотрим её. В боковой панели вы увидите, что вот она, новая, здесь. И даже лучше, для нас был создан хороший шаблон. (7:00) Он предполагает, что вы хотите ID с авто приращением (довольно типично для таблиц). Также временные штампы %%(t)created_at%% и %%(t)updated_at%%, которые тоже будут полезны. Обычно лучше включать их, если только нет какой-то хорошей причины, по которой они будут не нужны. Итак, давайте подумаем об этом. У стандартной статьи будет заголовок и тело. Как насчёт этого? ОК, в таблице у нас строка, которая будет заголовком: %% $table->string('title'); %% И затем ещё одну, но не строку, а целое текстовое поле для тела статьи: %% $table->text('body'); %% (7:30) И кто знает, может вы захотите что-то ещё, например временной штамп %%(t)published_at%%. OК: %% $table->timestamp('published_at'); %% Таким образом, логика здесь диктует: "Я могу запланировать тут статью, но если %%(t)published_at%% не равна сегодняшнему дню или моменту в прошлом, то не нужно отражать эту статью". ОК, вот так мы создаём таблицу. Для метода %%down()%%, мы таким образом отменяем создание. (8:00) В этом случае мы удалим таблицу %%(t)articles%%. Но в других случаях вы должны откатить изменения, изменив таблицу. И скоро я покажу вам, как это выглядит. Сейчас же, наша миграция хорошо выглядит, так давайте мигрируем БД: %%(sh) php artisan migrate %% И всё готово. У вас тут новая таблица. Дальше, если вам нужно сделать изменение, и особенно если вы ещё не внесли ваши изменения в производственную версию, то я советую просто откатиться, сделать модификации и перезапуститься. (8:30) Но давайте представим, что вы уже это сделали и пути назад нет. И теперь мы решили, что нам нужно добавить в эту таблицу новый столбец. Мы можем создать новую миграцию. Вот так: %%(sh) php artisan make:migration %% И опять, ID здесь (или имя миграции), вам нужно чётко описать, что именно вы делаете. И это хороший принцип, которого стоит придерживаться. (9:00) Почему бы нам не сказать тут, что для вашей статьи вы также хотите добавить новый столбец? Возможно что-то типа отрывка из статьи (excerpt). Итак, %%(t)excerpt%% будет отображаться на основной странице, и когда вы кликнете на статью, то перейдёте к полному тексту. Хорошо, мы хотим добавить отрывок к таблице %%(t)articles%%: %%(t) add_excerpt_to_articles_table %% Сделано. Давайте посмотрим, как она теперь выглядит. У нас есть новая миграция, но она немного другая. (9:30) И заметьте, в этом случае, у нас не так много шаблонного кода. Да, можно было бы написать всё это вручную, и это бы не заняло много времени. Но если мы можем попросить Laravel сделать это за нас, то почему бы и нет? Давайте запустим команду ещё раз, но сейчас я скажу Laravel с какой таблицей я работаю: %%(t) --table="articles" %% ОК, сейчас, поскольку мы это добавили, мы получили более расширенный шаблон. Хорошо, сделаем наше изменение. Я хочу добавить новый текстовый столбец для отрывка: %% $table->text('excerpt'); %% (10:00) И теперь, если я хочу отменить это, то мы удалим столбец: %% $table->dropColumn('excerpt'); %% И это, так сказать, формула для того, как отменять наши действия. ОК, я думаю мы готовы к запуску. Ещё один момент тут... будет ли хорошо для нашей таблицы, если у нас будет столбец %%(t)excerpt%%, но в нём - ничего? Или, другими словами, при создании новой статьи, можно ли сделать %%(t)excerpt%% равным %%(t)null%%? (10:30) И вы возможно решите, что да, это было бы допустимо. В этом случае вам стоит добавить %%nullable()%%: %% $table->text('excerpt')->nullable(); %% Дальше: %%(sh) php artisan migrate %% И мы всё сделали. Теперь у нас есть наш столбец %%(t)excerpt%% в нашей таблице %%(t)articles%%. Давайте убедимся. Мы запускаем это, рассматриваем структуру, и вот тут внизу наш "отрывок". Но тут есть один моментик, о котором я хочу вас предупредить. (11:00) Давайте откатим миграцию просто ради шутки и посмотрим что произойдёт: %%(sh) php artisan migrate:rollback %% И, блин, всё сломалось. Если мы прокрутим сюда наверх, то увидим, что Laravel пытался сослаться на этот класс (%%Doctrine\DBAL\Driver\PDOSqlite\Driver%%), но не смог его найти. И это потому, что если вы хотите удалить столбец, то нужен специальный пакет. И документация Laravel расскажет вам всё что нужно. Достаточно лишь выполнить: %%(sh) composer require doctrine/dbal %% Так давайте подтянем его и дадим секунду на установку. (11:30) Готово. Пробуем снова. Откатываем миграцию, и сейчас всё работает. Так что помните об этом. И теперь опять если мы посмотрим структуру (schema), то увидим, что изменения отображены. Нашего %%(t)excerpt%% больше нет. Хорошо, теперь, когда вы понимаете основы миграций, в следующем эпизоде, я познакомлю вас с Eloquent, который вы полюбите.