Миграции и сидеры – производительные утилиты для работы с БД, предоставляемые PHP-фреймворком Laravel. Благодаря миграциям и сидерам разработчики могут быстро инициализировать, удалить и восстановить базу данных своего приложения. Эти утилиты помогают минимизировать несогласованности и конфликты данных внутри БД (они возникают в базах, с которыми одновременно работают несколько разработчиков ). Если к разработке приложения присоединяются новые участники, им нужно запустить всего несколько команд artisan, чтобы получить текущую базу данных.
В этом мануале вы научитесь использовать миграции и сидеры для заполнения базы данных простого тестового приложения Laravel. Вы узнаете, как с помощью этих утилит можно удалять и восстанавливать таблицы БД.
Требования
- Локальная машина или сервер разработки Ubuntu 18.04, настроенные по этому мануалу (включая пользователя с доступом к sudo). Если вы используете удаленный сервер, рекомендуем включить брандмауэр.
- Установка Docker (читайте разделы 1 и 2 мануала Установка и использование Docker в Ubuntu 18.04).
- Установка Docker Compose (раздел 1 мануала Установка Docker Compose в Ubuntu 18.04).
Примечание: Этот мануал протестирован в контейнеризованной среде разработки приложения на основе Docker Compose, но также это можно сделать на стеке LEMP. Если этот вариант вам больше подходит, выполните мануал Установка и настройка Laravel на стеке LEMP в Ubuntu 18.04.
1: Подготовка тестового приложения
Давайте для начала загрузим тестовое приложение Laravel из этого GitHub репозитория. Вам понадобится ветка tutorial-02, которая включает установку Docker Compose, благодаря чему вы сможете запустить приложение в контейнерах. Мы будем использовать для работы свой домашний каталог, но вы можете выбрать любой другой.
cd ~
curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-2.0.1.zip -o travellist.zip
Мы загрузили код приложения в формате .zip. Теперь его нужно распаковать с помощью команды unzip. Если вы еще не обновили индекс пакетов, сделайте это сейчас:
sudo apt update
Установите пакет unzip:
sudo apt install unzip
Распакуйте архив:
unzip travellist.zip
Переименуйте полученный каталог в travellist-demo, чтобы его было проще найти:
mv travellist-laravel-demo-tutorial-2.0.1 travellist-demo
Теперь у вас есть тестовое приложение. Можно приступать к настройке.
2: Создание файла .env для приложения
В Laravel файл .env позволяет создавать зависимую от среды конфигурацию (он хранит учетные данные и любую другую информацию, которая может отличаться в зависимости от развертывания). Этот файл не включается в контроль версий.
Важно! В конфигурационном файле среды хранится важная информация о сервере, в том числе учетные данные БД и ключи безопасности. Не рекомендуем выкладывать этот файл в открытый доступ.
Все значения из файла .env имеют более высокий приоритет, чем значения из обычных конфигурационных файлов в каталоге config. Каждая установка в новой среде требует индивидуальный файл среды, в котором определяются следующие настройки: параметры подключения к БД и отладки, URL приложения и другие параметры (это зависит от среды, которую вы используете для запуска приложения).
Перейдите в каталог travellist-demo:
cd travellist-demo
Сейчас нужно создать новый файл .env для хранения индивидуальных параметров нашей будущей среды разработки. Laravel предоставляет образец файла .env, его можно скопировать и использовать в качестве шаблона для нашего собственного файла:
cp .env.example .env
Откройте файл с помощью nano:
nano .env
Сейчас он выглядит так:
APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
…
В данный момент файл .env нашего тестового приложения travellist содержит параметры для работы в контейнеризованной среде (в этой статье мы рассказывали, как создать ее с помощью Docker Compose). Вам не обязательно менять эти значения, но вы можете изменить DB_DATABASE, DB_USERNAME и DB_PASSWORD, если захотите (эти значения автоматически вставляются из файла docker-compose.yml). Переменную DB_HOST менять не нужно – она задает имя сервиса базы данных в среде Docker Compose.
Внеся все изменения в файл, сохраните и закройте его (Ctrl+X, Y, а затем Enter).
Примечание: Если вы хотите запустить приложение на стеке LEMP, вам нужно заменить выделенные значения вашими параметрами БД, включая переменную DB_HOST.
3: Установка зависимостей с помощью Composer
С помощью инструмента для управления зависимостями Composer можно установить все зависимости нашего приложения и включить поддержку команд artisan.
Запустите среду Docker Compose при помощи следующей команды. Это создаст образ travellist для сервиса app и добавит другие дополнительные образы Docker (они нужны сервисам nginx и db, чтобы создать среду).
docker-compose up -d
Creating network “travellist-demo_travellist” with driver “bridge”
Building app
Step 1/11 : FROM php:7.4-fpm
---> fa37bd6db22a
Step 2/11 : ARG user
---> Running in 9259bb2ac034
…
Creating travellist-app ... done
Creating travellist-nginx ... done
Creating travellist-db ... done
Эта операция может длиться несколько минут. После ее выполнения можно запустить Composer для установки зависимостей приложения.
Чтобы иметь возможность выполнять команды composer и другие команды в контейнере сервиса app, используйте docker-compose exec. Команда exec позволяет выполнять в контейнерах Docker Compose любые необходимые команды. exec использует следующий синтаксис:
docker-compose exec service_name command
Примечание: Если вы работаете на стеке LEMP, вы можете пропустить в командах часть «docker-compose exec app» в рамках этого мануала. То есть вместо следующей команды:
docker-compose exec app composer install
вы можете использовать команду:
composer install
Чтобы выполнить команду composer install в контейнере приложения, запустите:
docker-compose exec app composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 85 installs, 0 updates, 0 removals
- Installing doctrine/inflector (1.3.1): Downloading (100%)
- Installing doctrine/lexer (1.2.0): Downloading (100%)
- Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%)
…
Когда Composer завершит установку зависимостей, вы получите доступ к командам artisan. Чтобы подтвердить, что приложение подключается к базе данных, выполните следующую команду (она чистит все существующие таблицы):
docker-compose exec app php artisan db:wipe
Эта команда удалит все существовавшие таблицы в вашей БД. Если команда выполнена успешно, а приложение смогло подключиться к БД, вы увидите такой результат:
Dropped all tables successfully.
4: Создание миграций
Инструмент командной строки artisan поставляется вместе с Laravel. Он предлагает ряд вспомогательных команд, при помощи которых можно управлять приложением и загружать новые классы. Чтобы создать новый класс миграции, используйте команду make:migration:
docker-compose exec app php artisan make:migration create_places_table
Laravel определяет операцию, которую необходимо выполнить (create), имя таблицы (places), а также запрашивает, должна ли данная миграция создать новую таблицу. Все эти данные включены в описательное имя в команде make:migration. Вывод будет выглядеть так:
Created Migration: 2020_02_03_143622_create_places_table
После этого в каталоге приложения database/migrations появится новый файл. С помощью метки времени, которая включается в этот сгенерированный файл, Laravel определяет, в каком порядке нужно выполнять миграции.
Откройте этот файл миграции. Обязательно укажите в этой команде имя вашего файла:
nano database/migrations/2020_02_03_143622_create_places_table.php
В файле находится класс по имени CreatePlacesTable:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePlacesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('places', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('places');
}
}
Этот класс включает два метода: up и down. Они содержат код загрузки, который можно расширить (например, с его помощью вы можете определить, что будет происходить при выполнении этой миграции или в случае отката изменений).
Давайте изменим метод up, чтобы таблица places отражала такую структуру:
- id: первичный ключ.
- name: название города.
- visited: посещен этот город или нет.
Конструктор Laravel предлагает методы для создания, обновления и удаления таблиц в БД. Класс Blueprint позволяет определить структуру таблицы и поддерживает несколько методов, с помощью которых можно абстрагировать определения каждого поля в таблице.
Сгенерированный автоматически код определяет поле первичного ключа id. Метод timestamps создает два поля datetime (они автоматически обновляются классами соответствующей БД при помещении или обновлении данных в таблице). Также нам нужно добавить поля name и visited.
Поле name будет иметь тип string, а visited – тип boolean. Полю visited мы зададим значение по умолчанию 0 (и если этому полю не будет передано значение, это будет означать, что данный город еще не посещен). В итоге метод up выглядит так:
…
public function up()
{
Schema::create('places', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 100);
$table->boolean('visited')->default(0);
$table->timestamps();
});
}
…
Примечание: Полный список доступных типов можно найти в документации Laravel.
Добавив две выделенные красным строки в сценарий миграции, сохраните и закройте файл.
Теперь вы можете выполнить свою миграцию с помощью artisan migrate, но это создаст пустую таблицу, а нам нужно иметь возможность добавлять в нее данные. Далее мы покажем, как сделать это с помощью сидеров.
5: Создание сидеров
Сидер (seeder) — это специальный класс, который позволяет генерировать данные и вставлять их в БД. Это очень важная для разработки утилита, поскольку она позволяет воссоздать базу данных приложения (без нее при каждом воссоздании пришлось бы вставлять данные в БД вручную).
Используйте команду artisan, которая сгенерирует новый класс seeder по имени PlacesTableSeeder для таблицы places:
docker-compose exec app php artisan make:seeder PlacesTableSeeder
Команда создаст новый файл PlacesTableSeeder.php в каталоге database/seeds. Откройте его в редакторе:
nano database/seeds/PlacesTableSeeder.php
Автоматически сгенерированный файл PlacesTableSeeder.php выглядит так:
<?php
use Illuminate\Database\Seeder;
class PlacesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}
Новый сидер содержит пустой метод run. Он будет вызываться при выполнении команды db:seed.
Теперь нужно отредактировать метод run и добавить в него инструкции для вставки данных в БД. Для этого мы используем конструктор запросов Laravel.
С помощью этого конструктора Laravel предоставляет гибкий и удобный интерфейс для работы с БД (в том числе для вставки, обновления и восстановления данных). Также конструктор запросов защищает приложение от инъекций SQL-кода. Конструктор запросов определяется фасадом DB (это статический прокси для классов БД в контейнере сервиса).
Давайте создадим переменную статического класса, которая в форме массива будет хранить названия всех городов, которые мы хотим вставить в БД. Благодаря этому мы сможем использовать цикл foreach для итерации по всем значениям и вставить все эти значения в БД.
Давайте назовем эту переменную $places:
<?php
use Illuminate\Database\Seeder;
class PlacesTableSeeder extends Seeder
{
static $places = [
'Berlin',
'Budapest',
'Cincinnati',
'Denver',
'Helsinki',
'Lisbon',
'Moscow',
'Nairobi',
'Oslo',
'Rio',
'Tokyo'
];
…
После этого нужно включить в начало класса PlacesTableSeeder оператор use, чтобы упростить ссылку на фасад DB в коде:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class PlacesTableSeeder extends Seeder
…
Теперь можно итерировать массив $places с помощью цикла foreach и вставить все значения из массива в таблицу places при помощи конструктора запросов.
…
public function run()
{
foreach (self::$places as $place) {
DB::table('places')->insert([
'name' => $place,
'visited' => rand(0,1) == 1
]);
}
}
Цикл foreach переберет все значения статического массива $places. При каждой итерации фасад DB используется для добавления новой строки в таблицу places. Название города, которое мы только что извлекли из массива $places, вставляется в поле name, а в поле visited вставляется случайное значение 0 или 1.
Учитывая все изменения, класс PlacesTableSeeder будет выглядеть так:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class PlacesTableSeeder extends Seeder
{
static $places = [
'Berlin',
'Budapest',
'Cincinnati',
'Denver',
'Helsinki',
'Lisbon',
'Moscow',
'Nairobi',
'Oslo',
'Rio',
'Tokyo'
];
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
foreach (self::$places as $place) {
DB::table('places')->insert([
'name' => $place,
'visited' => rand(0,1) == 1
]);
}
}
}
Сохраните и закройте файл.
Классы сидеров не загружаются в приложение автоматически. Для этого нам нужно самостоятельно отредактировать главный класс DatabaseSeeder и включить в него новый сидер.
Откройте database/seeds/DatabaseSeeder.php:
nano database/seeds/DatabaseSeeder.php
Класс DatabaseSeeder выглядит так же, как любой стандартный сидер: он расширяется из класса Seeder и включает метод run. Давайте обновим этот метод, включив в него PlacesTableSeeder.
Обновите метод run в классе DatabaseSeeder. Для этого удалите закомментированную строку и замените ее следующей:
…
public function run()
{
$this->call(PlacesTableSeeder::class);
}
...
Теперь ваш класс DatabaseSeeder выглядит так:
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(PlacesTableSeeder::class);
}
}
Сохраните и закройте файл.
Теперь у нас есть сидер, который будет заполнять данными таблицу places.
6: Запуск миграций и сидеров БД
Сейчас нам нужно убедиться, что приложение запущено и работает. Мы настроим ключ шифрования приложения и откроем его в браузере, чтобы проверить работу веб-сервера.
Чтобы создать ключ шифрования для Laravel, используйте команду:
docker-compose exec app php artisan key:generate
Создав ключ, вы сможете открыть приложение в браузере, указав имя хоста или IP-адрес и порт 8000:
http://server_host_or_ip:8000
На странице будет фраза:
A table was not found
You might have forgotten to run your migrations […]
Это значит, что приложение подключается к БД, но не находит таблицу по имени places. Давайте создадим таблицу places с помощью этой команды:
docker-compose exec app php artisan migrate
Вывод будет выглядеть так:
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.06 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.06 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (0.03 seconds)
Migrating: 2020_02_10_144134_create_places_table
Migrated: 2020_02_10_144134_create_places_table (0.03 seconds)
Обратите внимание: вместе с настроенной нами миграцией create_places_table было выполнено несколько других миграций. Они генерируются автоматически, если Laravel установлен. Сейчас мы не будем использовать эти дополнительные таблицы, но они пригодятся в будущем при расширении функционала приложения (например, при настройке аутентификации пользователей и планирования заданий). Пока что вы можете оставить эти таблицы без изменений.
На данный момент таблица places пуста. Запустите следующую команду, чтобы заполнить БД данными:
docker-compose exec app php artisan db:seed
Эта команда запустит сидер, а он добавит в таблицу значения, которые мы определили в классе PlacesTableSeeder. Вывод будет выглядеть так:
Seeding: PlacesTableSeeder
Seeded: PlacesTableSeeder (0.06 seconds)
Database seeding completed successfully.
Перезагрузите страницу в браузере. Вы увидите список городов.
Если вам нужно начать все сначала, вы можете очистить все таблицы вашей БД с помощью команды:
docker-compose exec app php artisan db:wipe
Dropped all tables successfully.
Чтобы запустить миграцию и заполнить таблицы данными с помощью одной команды, используйте эту команду:
docker-compose exec app php artisan migrate --seed
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.06 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.07 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (0.03 seconds)
Migrating: 2020_02_10_144134_create_places_table
Migrated: 2020_02_10_144134_create_places_table (0.03 seconds)
Seeding: PlacesTableSeeder
Seeded: PlacesTableSeeder (0.06 seconds)
Database seeding completed successfully.
Чтобы откатить миграцию, введите:
docker-compose exec app php artisan migrate:rollback
Это запустит метод down для всех классов миграции внутри каталога migrations. Как правило, этот метод удаляет все таблицы, созданные в классах миграции, оставляя те таблицы, которые могли быть созданы вручную. Вы увидите такой вывод:
Rolling back: 2020_02_10_144134_create_places_table
Rolled back: 2020_02_10_144134_create_places_table (0.02 seconds)
Rolling back: 2019_08_19_000000_create_failed_jobs_table
Rolled back: 2019_08_19_000000_create_failed_jobs_table (0.02 seconds)
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_100000_create_password_resets_table (0.02 seconds)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back: 2014_10_12_000000_create_users_table (0.02 seconds)
Функция отката особенно полезна, если вы вносите изменения в модели и не можете использовать команду db:wipe (например, если одна БД используется несколькими системами).
Заключение
В этом мануале вы научились работать с миграциями и сидерами. Эти утилиты способны упростить настройку БД в среде разработки и тестирования приложения Laravel 6.
Рекомендуем вам ознакомиться с документацией Laravel, где можно найти много полезной информации (например, о том, как использовать конструктор запросов или модели Eloquent).