One place for hosting & domains

      базы

      Использование триггеров базы данных MySQL в Ubuntu 18.04


      Автор выбрал фонд Apache Software Foundation для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Триггер в MySQL — это определяемая пользователем SQL-команда, которая автоматически вызывается во время операций INSERT, DELETE или UPDATE. Код триггера связан с таблицей и уничтожается после удаления таблицы. Вы можете определить время действия триггера и указать, когда его нужно активировать – до или после определенного события базы данных.

      Триггеры имеют несколько преимуществ Например, вы можете использовать их для генерации значения производного столбца во время выполнения INSERT. Еще один вариант использования триггера – обеспечение ссылочной целостности при сохранении записи в нескольких связанных таблицах. Также к преимуществам триггеров относятся регистрация действий пользователя для аудита таблиц и оперативное копирование данных в разных схемах баз данных для обеспечения избыточности и предотвращения единой точки отказа.

      Вы также можете использовать триггеры, чтобы сохранить правила проверки на уровне базы данных. Это помогает избежать нарушения бизнес-логики при совместном использовании одного источника данных несколькими приложениями. Это значительно уменьшает количество обращений к серверу базы данных, что, в свою очередь, улучшает время отклика приложений. Поскольку сервер базы данных выполняет триггеры, они могут воспользоваться улучшенными ресурсами сервера, такими как RAM и CPU.

      В этом обучающем модуле вы научитесь создавать, использовать и удалять различные типы триггеров в вашей базе данных MySQL.

      Предварительные требования

      Прежде чем начать, убедитесь в наличии следующего:

      Шаг 1 — Создание тестовой базы данных

      На этом этапе вы создадите тестовую клиентскую базу данных пользователя с несколькими таблицами для демонстрации работы триггеров MySQL.

      Более подробно о работе MySQL можно прочитать в инструкции Запросы в MySQL​​​.

      Вначале войдите на сервер MySQL как root:

      По запросу введите свой root пароль MySQL и нажмите ENTER для продолжения. Когда вы увидите mysql>, выполните следующую команду, чтобы создать базу данных test_db:

      Output

      Query OK, 1 row affected (0.00 sec)

      Далее переходите к test_db с помощью:

      Output

      Database changed

      Начинайте с создания таблицы customers. В этой таблице будут храниться записи клиентов, включая customer_id, customer_name и level. Будет два типа клиентов: BASIC и VIP.

      • Create table customers(customer_id BIGINT PRIMARY KEY, customer_name VARCHAR(50), level VARCHAR(50) ) ENGINE=INNODB;

      Output

      Query OK, 0 rows affected (0.01 sec)

      Теперь, добавьте несколько записей в таблицу customers. Для этого выполните следующие команды одну за другой:

      • Insert into customers (customer_id, customer_name, level )values('1','JOHN DOE','BASIC');
      • Insert into customers (customer_id, customer_name, level )values('2','MARY ROE','BASIC');
      • Insert into customers (customer_id, customer_name, level )values('3','JOHN DOE','VIP');

      После выполнения каждой команды INSERT вы увидите следующий вывод:

      Output

      Query OK, 1 row affected (0.01 sec)

      Чтобы убедиться, что тестовые записи были успешно вставлены, выполните команду SELECT:

      Output

      +-------------+---------------+-------+ | customer_id | customer_name | level | +-------------+---------------+-------+ | 1 | JOHN DOE | BASIC | | 2 | MARY ROE | BASIC | | 3 | JOHN DOE | VIP | +-------------+---------------+-------+ 3 rows in set (0.00 sec)

      Затем создайте другую таблицу customers для хранения соответствующей информации об учетной записи клиентов. Таблица будет содержать поля customer_id и status_notes.

      Запустите следующую команду:

      • Create table customer_status(customer_id BIGINT PRIMARY KEY, status_notes VARCHAR(50)) ENGINE=INNODB;

      Далее создайте таблицу sales. В этой таблице будут храниться данные о продажах, имеющих отношение к разным клиентам в столбце customer_id:

      • Create table sales(sales_id BIGINT PRIMARY KEY, customer_id BIGINT, sales_amount DOUBLE ) ENGINE=INNODB;

      Output

      Query OK, 0 rows affected (0.01 sec)

      Вы сможете добавить тестовые данные в колонку sales на следующих этапах во время тестирования триггеров. Далее создайте таблицу audit_log для регистрации обновлений, внесенных в таблицу sales при имплементации триггера AFTER UPDATE в шаге 5:

      • Create table audit_log(log_id BIGINT PRIMARY KEY AUTO_INCREMENT, sales_id BIGINT, previous_amount DOUBLE, new_amount DOUBLE, updated_by VARCHAR(50), updated_on DATETIME ) ENGINE=INNODB;

      Output

      Query OK, 0 rows affected (0.02 sec)

      Имея базу данных test_db и четыре таблицы, теперь вы можете перейти к работе с различными триггерами MySQL в вашей базе данных.

      Шаг 2 — Создание триггера Before Insert

      На этом этапе вы изучите синтаксис триггера MySQL перед тем, как применить эту логику для создания триггера BEFORE INSERT, который проверяет поле sales_amount перед вставкой данных в таблицу sales.

      Общий синтаксис для создания триггера MySQL показан в следующем примере:

      DELIMITER //
      CREATE TRIGGER [TRIGGER_NAME]
      [TRIGGER TIME] [TRIGGER EVENT]
      ON [TABLE]
      FOR EACH ROW
      [TRIGGER BODY]//
      DELIMITER ;
      

      Структура триггера включает:

      DELIMITER //: разделитель MySQL по умолчанию — это ;. Его нужно заменить на что-то другое, для того, чтобы MySQL рассматривал следующие строки, как одну команду, пока не достигнет пользовательского разделителя. В данном примере в качестве разделителя используется //, а стандартный разделитель ; стоит в конце.

      [TRIGGER_NAME]: триггер должен иметь имя, и вы можете указать его именно здесь.

      [TRIGGER TIME]​​​: триггер может быть вызван в разные моменты времени. MySQL позволяет определить, когда запускать триггер — до или после операции с базой данных.

      [TRIGGER EVENT]: триггеры могут быть вызваны только операциями INSERT, UPDATE и DELETE. Вы можете использовать любое из значений в зависимости от того, чего вы хотите достичь.

      [TABLE]: любой триггер, который вы создаете в своей базе данных MySQL, должен быть связан с таблицей.

      FOR EACH ROW: этот оператор позволяет MySQL выполнять код триггера для каждой строки, на которую влияет триггер.

      [TRIGGER BODY]: код, который выполняется при вызове триггера, называется trigger body​​. Это может быть один SQL-оператор или несколько команд. Обратите внимание, если вы выполняете несколько SQL-операторов в теле триггера, вы должны заключить их в блок BEGIN... END.

      Примечание: при создании тела триггера вы можете использовать ключевые слова OLD и NEW для доступа к старым и новым значениям колонки, введенным во время операции INSERT, UPDATE и DELETE. В триггере DELETE может быть использовано только ключевое слово OLD (подробнее об этом в шаге 4).

      Теперь вы можете создать свой первый триггер BEFORE INSERT. Триггер будет связан с таблицей sales и будет вызываться перед вставкой записи для проверки sales_amount. Функция триггера состоит в том, чтобы проверить, превышает ли значение sales_amount, вставляемое в таблицу продаж, величину 10000, и выдать ошибку, если это так.

      Убедитесь, что вы вошли на сервер MySQL. Затем введите следующие команды MySQL одну за другой:

      • DELIMITER //
      • CREATE TRIGGER validate_sales_amount
      • BEFORE INSERT
      • ON sales
      • FOR EACH ROW
      • IF NEW.sales_amount>10000 THEN
      • SIGNAL SQLSTATE '45000'
      • SET MESSAGE_TEXT = 'Sale has exceeded the allowed amount of 10000.';
      • END IF//
      • DELIMITER ;

      Используйте IF... THEN... END IF​​​ для оценки того, находится ли сумма, указанная в операторе INSERT, в пределах вашего диапазона. Триггер может извлечь новое значение sales_amount, используя ключевое слово NEW.

      Чтобы вызвать общее сообщение об ошибке, используются следующие строки для информирования пользователя:

      SIGNAL SQLSTATE '45000'
      SET MESSAGE_TEXT = 'Sale has exceeded the allowed amount of 10000.';
      

      Далее вставьте запись sales_amount со значением 11000 в таблицу sales, чтобы проверить, остановит ли триггер операцию:

      • Insert into sales(sales_id, customer_id, sales_amount) values('1','1','11000');

      Output

      ERROR 1644 (45000): Sale has exceeded the allowed amount of 10000.

      Эта ошибка показывает, что код триггера работает должным образом.

      Теперь попробуйте новую запись со значением 7500, чтобы проверить правильность действия команды:

      • Insert into sales(sales_id, customer_id, sales_amount) values('1','1','7500');

      Поскольку значение находится в рекомендованном диапазоне, вы увидите следующий вывод:

      Output

      Query OK, 1 row affected (0.01 sec)

      Для подтверждения вставки данных запустите следующую команду:

      Вывод подтверждает вставку данных в таблицу:

      Output

      +----------+-------------+--------------+ | sales_id | customer_id | sales_amount | +----------+-------------+--------------+ | 1 | 1 | 7500 | +----------+-------------+--------------+ 1 row in set (0.00 sec)

      На этом этапе вы протестировали способность триггеров проверять данные перед вставкой в базу данных.

      Теперь поработайте с триггером AFTER INSERT для сохранения связанной информации в разных таблицах.

      Шаг 3 — Создание триггера After Insert

      Триггеры AFTER INSERT выполняются после успешной вставки записей в таблицу. Эта функция может использоваться для автоматического запуска других бизнес-логик. Например, в банковских приложениях триггер AFTER INSERT может закрывать кредитный счет, когда клиент завершает выплату кредита. Триггер может отслеживать все платежи, внесенные в таблицу транзакций, и автоматически закрывать кредит, как только кредитный баланс будет равен нулю.

      На этом этапе вы поработаете с таблицей customer_status​​​, используя триггер AFTER INSERT для ввода связанных клиентских записей.

      Для создания триггера AFTER INSERT введите следующие команды:

      • DELIMITER //
      • CREATE TRIGGER customer_status_records
      • AFTER INSERT
      • ON customers
      • FOR EACH ROW
      • Insert into customer_status(customer_id, status_notes) VALUES(NEW.customer_id, 'ACCOUNT OPENED SUCCESSFULLY')//
      • DELIMITER ;

      Output

      Query OK, 0 rows affected (0.00 sec)

      Таким образом вы инструктируете MySQL сохранить еще одну запись в таблицу customer_status, как только происходит вставка новой клиентской записи в таблицу customers.

      Теперь вставьте новую запись в таблицу customers, чтобы убедиться, что код триггера вызывается:

      • Insert into customers (customer_id, customer_name, level )values('4','DAVID DOE','VIP');

      Output

      Query OK, 1 row affected (0.01 sec)

      После успешной вставки записи убедитесь, что запись нового статуса была добавлена в таблицу customer_status:

      • Select * from customer_status;

      Output

      +-------------+-----------------------------+ | customer_id | status_notes | +-------------+-----------------------------+ | 4 | ACCOUNT OPENED SUCCESSFULLY | +-------------+-----------------------------+ 1 row in set (0.00 sec)

      Вывод подтверждает успешную работу триггера.

      Триггер AFTER INSERT​​​ полезен для мониторинга жизненного цикла клиента. В производственной среде учетные записи клиентов могут проходить различные этапы, например открытие, приостановка и закрытие счета.

      На следующем этапе вы будете работать с триггерами UPDATE.

      Шаг 4 — Создание триггера Before Update

      Триггер BEFORE UPDATE схож с триггером BEFORE INSERT, разница заключается в том, когда они вызываются. Вы можете использовать триггер BEFORE UPDATE для проверки бизнес-логики перед обновлением записи. Для проверки используйте таблицу customers, в которую вы уже вставили некоторые данные.

      В базе данных есть два типа клиентов. В этом примере после того, как учетная запись клиента будет обновлена до уровня VIP, она не сможет быть понижена до уровня BASIC. Чтобы применить такое правило, создайте триггер BEFORE UPDATE, который будет выполняться перед оператором UPDATE, как показано ниже. Если пользователь базы данных попытается понизить клиента до уровня BASIC с уровня VIP, будет активировано определяемое пользователем исключение.

      Введите следующие команды SQL одну за другой, чтобы создать триггер BEFORE UPDATE:

      • DELIMITER //
      • CREATE TRIGGER validate_customer_level
      • BEFORE UPDATE
      • ON customers
      • FOR EACH ROW
      • IF OLD.level='VIP' THEN
      • SIGNAL SQLSTATE '45000'
      • SET MESSAGE_TEXT = 'A VIP customer can not be downgraded.';
      • END IF //
      • DELIMITER ;

      Используйте ключевое слово OLD для фиксации уровня, предоставленного пользователем при выполнении команды UPDATE. Опять же, вы используете IF... THEN... END IF, чтобы сообщить пользователю об общей ошибке.

      Далее выполните следующую SQL команду, которая попытается понизить учетную запись клиента, имеющую идентификатор customer_id, равный 3:

      • Update customers set level='BASIC' where customer_id='3';

      Вы увидите следующий вывод, предоставляющий SET MESSAGE_TEXT:

      Output

      ERROR 1644 (45000): A VIP customer can not be downgraded.

      Если вы выполните ту же команду для клиента уровня BASIC и попытаетесь повысить учетную запись до уровня VIP, команда выполнится успешно:

      • Update customers set level='VIP' where customer_id='1';

      Output

      Rows matched: 1 Changed: 1 Warnings: 0

      Вы использовали триггер BEFORE UPDATE для применения бизнес-правила. Теперь перейдем к использованию триггера AFTER UPDATE для ведения журнала аудита.

      Шаг 5 — Создание триггера After Update

      Триггер AFTER UPDATE вызывается после успешного обновления записи в базе данных. Такое поведение триггера подходит для ведения журнала аудита. В многопользовательской среде администратор с целью аудита может просмотреть историю пользователей, обновляющих записи в конкретной таблице.

      Вы создаете триггер, который регистрирует активность обновления таблицы sales. Наша таблица audit_log будет содержать информацию о пользователях MySQL, обновляющих таблицу sales, дату обновления date, а также новые new и старые old значения sales_amount.

      Для создания триггера, выполните следующие команды SQL:

      • DELIMITER //
      • CREATE TRIGGER log_sales_updates
      • AFTER UPDATE
      • ON sales
      • FOR EACH ROW
      • Insert into audit_log(sales_id, previous_amount, new_amount, updated_by, updated_on) VALUES (NEW.sales_id,OLD.sales_amount, NEW.sales_amount,(SELECT USER()), NOW() )//
      • DELIMITER ;

      Вы вставляете новую запись в таблицу audit_log. Вы используете ключевое слово NEW для получения значения sales_id и нового значения sales_amount. Также вы используете ключевое слово OLD для получения предыдущего значения sales_amount, если вы хотите зарегистрировать обе суммы для аудита.

      Команда SELECT USER() извлекает текущего пользователя, выполняющего операцию, а оператор NOW() извлекает значение текущей даты и времени с сервера MySQL.

      Теперь, если пользователь попытается обновить значение какой-либо записи в таблице sales, триггер log_sales_updates вставит новую запись в таблицу audit_log.

      Давайте создадим новую запись о продажах со случайным значением sales_id, равным 5, и попробуем обновить ее. Сначала вставьте запись о продажах:

      • Insert into sales(sales_id, customer_id, sales_amount) values('5', '2','8000');

      Output

      Query OK, 1 row affected (0.00 sec)

      Затем обновите запись:

      • Update sales set sales_amount='9000' where sales_id='5';

      Вывод должен выглядеть так:

      Output

      Rows matched: 1 Changed: 1 Warnings: 0

      Теперь выполните следующую команду, чтобы проверить, смог ли триггер AFTER UPDATE зарегистрировать новую запись в таблице audit_log:

      Триггер зарегистрировал обновление. Ваш вывод должен показать предыдущую сумму sales_amount и новую сумму new amount​​, зарегистрированную пользователем, который обновил запись:

      Output

      +--------+----------+-----------------+------------+----------------+---------------------+ | log_id | sales_id | previous_amount | new_amount | updated_by | updated_on | +--------+----------+-----------------+------------+----------------+---------------------+ | 1 | 5 | 8000 | 9000 | root@localhost | 2019-11-07 09:28:36 | +--------+----------+-----------------+------------+----------------+---------------------+ 1 row in set (0.00 sec)

      Также в таблице вы увидите дату и время, когда было выполнено обновление, что важно для аудита.

      Далее вы будете использовать триггер DELETE для обеспечения целостности ссылок на уровне базы данных.

      Шаг 2 — Создание триггера Before Delete

      Триггеры BEFORE DELETE вызываются до выполнения операции DELETE в таблице. Этот вид триггеров обычно используется для обеспечения целостности ссылок в разных связанных таблицах. Например, каждая запись в таблице sales связана с записью customer_id из таблицы customers. Если пользователь базы данных удалил из таблицы customers запись, у которой есть связанная запись в таблице sales, у вас не будет возможности узнать, какой клиент был связан с этой записью.

      Избежать подобных ситуаций и сделать логику более надежной позволит создание триггера BEFORE DELETE. Выполните следующие SQL команды одну за другой:

      • DELIMITER //
      • CREATE TRIGGER validate_related_records
      • BEFORE DELETE
      • ON customers
      • FOR EACH ROW
      • IF OLD.customer_id in (select customer_id from sales) THEN
      • SIGNAL SQLSTATE '45000'
      • SET MESSAGE_TEXT = 'The customer has a related sales record.';
      • END IF//
      • DELIMITER ;

      Теперь попробуйте удалить клиента, у которого есть связанная запись в таблице sales:

      • Delete from customers where customer_id='2';

      В результате вы получите следующий вывод:

      Output

      ERROR 1644 (45000): The customer has a related sales record.

      Триггер BEFORE DELETE может предотвратить случайное удаление связанной информации в базе данных.

      В некоторых ситуациях может потребоваться удалить из разных связанных таблиц все записи, связанные с конкретной записью. В этой ситуации возможно использовать триггер AFTER DELETE, который вы протестируете в следующем шаге.

      Шаг 5 — Создание триггера After Delete

      Триггеры AFTER DELETE активируются, когда запись была успешно удалена. Примером использования триггера AFTER DELETE является ситуация, когда скидка, которую получает конкретный клиент, определяется количеством покупок, совершенных этим клиентом в течение определенного периода. Если некоторые из записей клиента будут удалены из таблицы sales, скидка для этого клиента должна уменьшиться.

      Еще один вариант использования триггера AFTER DELETE — удаление связанной информации из других таблиц после удаления записи из базовой таблицы. Например, вы можете установить триггер, который удаляет запись о клиенте, если записи о продажах с соответствующим customer_id будут удалены из таблицы sales. Запустите следующую команду для создания триггера:

      • DELIMITER //
      • CREATE TRIGGER delete_related_info
      • AFTER DELETE
      • ON sales
      • FOR EACH ROW
      • Delete from customers where customer_id=OLD.customer_id;//
      • DELIMITER ;

      Далее запустите следующую команду, чтобы удалить все записи о продажах, связанных с customer_id, равному 2:

      • Delete from sales where customer_id='2';

      Output

      Query OK, 1 row affected (0.00 sec)

      Теперь проверьте, удалились ли записи для этого клиента из таблицы sales:

      • Select * from customers where customer_id='2';

      Вы получите вывод Empty Set, поскольку запись клиента, связанная с customer_id 2, была удалена триггером:

      Output

      Empty set (0.00 sec)

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

      Шаг 8 — Удаление триггеров

      Как и любой другой объект базы данных, вы можете удалить триггеры с помощью команды DROP. Синтакс удаления триггера следующий:

      Drop trigger [TRIGGER NAME];
      

      Например, чтобы удалить последний созданный триггер AFTER DELETE, выполните следующую команду:

      • Drop trigger delete_related_info;

      Output

      Query OK, 0 rows affected (0.00 sec)

      Необходимость удаления триггеров возникает, когда вы хотите воссоздать его структуру. В таком случае вы можете сбросить триггер и создать новый с помощью разных команд для триггеров.

      Заключение

      В этом обучающем руководстве вы научились создавать, использовать и удалять различные триггеры из базы данных MySQL. На примере клиентской базы данных вы ознакомились с применением триггеров для различных целей, таких как проверка данных, применение бизнес-логики, ведение журнала аудита и обеспечение целостности ссылок.

      Дополнительную информацию по использованию вашей базы данных MySQL можно найти здесь:



      Source link

      Использование миграции и пополнения базы данных для настройки абстрактной базы данных в Laravel


      Миграции и пополнения — это мощные утилиты для базы данных, предоставляемые PHP-фреймворком Laravel, которые позволяют разработчикам быстро инициализировать, уничтожать и воссоздавать базу данных приложения. Эти утилиты помогают свести к минимуму проблемы несогласованности базы данных, которые могут возникать, когда несколько разработчиков работают над одним приложением: новым участникам нужно будет запустить всего несколько команд artisan для настройки базы данных при новой установке.

      В этом обучающем руководстве мы выполним миграцию и пополнения для заполнения базы данных демонстрационного приложения Laravel, используя примеры данных. В конце вы сможете уничтожать и воссоздавать таблицы базы данных в любой момент времени, пользуясь только командами artisan.

      Предварительные требования

      Для выполнения этого обучающего руководства вам потребуется следующее:

      Примечание. В этом обучающем руководстве мы будем использовать контейнеризованную среду разработки под управлением 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 для распаковки архива. Если вы не использовали эту программу в последнее время, обновите локальный индекс пакетов вашего компьютера:

      Затем выполните установку пакета unzip:

      Распакуйте содержимое приложения:

      Далее необходимо переименовать директорию на travellist-demo для удобства доступа:

      • mv travellist-laravel-demo-tutorial-2.0.1 travellist-demo

      На следующем шаге мы создадим файл конфигурации .env для настройки приложения.

      Шаг 2 — Настройка файла .env приложения

      В Laravel файл .env используется для настройки зависимых от среды конфигураций, например, учетных данных и любой информации, которая может отличаться для разных развертываний. Этот файл не включен в систему контроля версий.

      Предупреждение. Файл конфигурации среды содержит важную информацию о вашем сервере, включая учетные данные базы данных и ключи безопасности. В связи с этим не следует предоставлять этот файл в открытый доступ.

      Значения в файле .env имеют приоритет по отношению к значениям в обычных файлах конфигурации, расположенных в директории config. Для каждого случая установки в новую среду требуется персонализированный файл среды, где будут определены такие настройки, как параметры подключения к базе данных, параметры отладки, URL приложения и другие параметры, в зависимости от используемой для запуска приложения среды.

      Перейдите в директорию travellist-demo:

      Теперь мы создадим новый файл .env для настройки индивидуальных параметров конфигурации для создаваемой нами среды разработки. В комплектацию Laravel входит образец файла .env, который мы скопируем для создания собственного файла:

      Откройте этот файл с помощью nano или другого текстового редактора на ваш выбор:

      Теперь ваш файл .env выглядит следующим образом:

      .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, инструментом для управления зависимостями PHP, чтобы установить зависимости приложения и обеспечить возможность выполнения команд artisan.

      Сформируйте вашу среду Docker Compose с помощью следующей команды. В результате будет создан образ travellist для службы app и добавлены дополнительные образы Docker, необходимые службам nginx и db, чтобы создать среду приложения:

      Output

      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. Она имеет следующий синтаксис: docker-compose exec service_name command.

      Примечание. Если вы предпочитаете использовать сервер LEMP для запуска демонстрационного приложения, можно проигнорировать часть docker-compose exec app для команд, представленных в рамках этого руководства. Например, вместо запуска следующей команды, как она написана, вы можете использовать следующую команду:

      Чтобы выполнить установку composer в контейнер приложения, запустите следующую команду:

      • docker-compose exec app composer install

      Output

      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

      Эта команда будет удалять все ранее существовавшие таблицы в настроенной базе данных. Если выполнение команды было завершено успешно, а приложению удалось подключиться к базе данных, вы увидите следующий результат:

      Output

      Dropped all tables successfully.

      Теперь, когда вы установили зависимости приложения с помощью Composer, вы можете использовать инструмент artisan для создания миграций и пополнений.

      Шаг 4 — Создание миграций базы данных

      Инструмент командной строки artisan, предоставляемый вместе с Laravel, содержит ряд вспомогательных команд, которые могут использоваться для управления приложением и загрузки новых классов. Чтобы сгенерировать новый класс миграции, мы можем использовать команду make:migration следующим образом:

      • docker-compose exec app php artisan make:migration create_places_table

      Laravel выводит операцию, которую необходимо выполнить (create), имя таблицы (places), а также то, будет ли эта миграция создавать новую таблицу или нет, на основе описательного имени, предоставленного команде make:migration.

      Вывод будет выглядеть следующим образом:

      Output

      Created Migration: 2020_02_03_143622_create_places_table

      В результате будет создан новый файл в директории database/migrations приложения. Отметка времени, которая включается в автоматически генерируемый файл, используется Laravel для определения того, в каком порядке следует выполнять миграции.

      Используйте предпочитаемый вами текстовый редактор для открытия сгенерированного файла миграции. Обязательно замените выделенное значение на имя вашего файла миграции:

      • nano database/migrations/2020_02_03_143622_create_places_table.php

      Сгенерированный файл миграции содержит класс CreatePlacesTable:

      database/migrations/2020_02_03_143622_create_places_table.php

      <?php
      
      use IlluminateDatabaseMigrationsMigration;
      use IlluminateDatabaseSchemaBlueprint;
      use IlluminateSupportFacadesSchema;
      
      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. Также мы зададим значение по умолчанию 0 для поля visited, так что если значение не будет передано, это будет означать, что место пока не посещено. Вот как в настоящий момент выглядит метод up:

      database/migrations/2020_02_03_143622_create_places_table.php

      …
          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 для нашей таблицы places с названием PlacesTableSeeder:

      • docker-compose exec app php artisan make:seeder PlacesTableSeeder

      Команда будет создавать новый файл с именем PlacesTableSeeder.php внутри директории database/seeds. Откройте этот файл в текстовом редакторе по выбору:

      • nano database/seeds/PlacesTableSeeder.php

      Вот как выглядит автоматически сгенерированный файл PlacesTableSeeder.php:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      
      class PlacesTableSeeder extends Seeder
      {
          /**
           * Run the database seeds.
           *
           * @return void
           */
          public function run()
          {
              //
          }
      }
      
      

      Наш новый класс seeder содержит пустой метод с именем run. Этот метод будет вызываться при выполнении команды db:seed Artisan.

      Нам нужно изменить метод run, чтобы включить инструкции по вставке примеров данных в базу данных. Мы воспользуемся конструктором запросов Laravel для организации этого процесса.

      Конструктор запросов Laravel предоставляет гибкий интерфейс для операций с базами данных, например, для вставки, обновления и восстановления данных. Также он обеспечивает защиту от внедрения SQL-кода. Конструктор запросов определяется фасадом DB, статическим прокси для классов базы данных в контейнере службы.

      Для начала мы создадим переменную статического класса, хранящую все примеры мест, которые мы хотим вставить в базу данных в форме массива. Это позволит нам использовать цикл foreach для итерации по всем значениям, вставляя все эти значения в базу данных с помощью конструктора запросов.

      Мы назовем эту переменную $places:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      
      class PlacesTableSeeder extends Seeder
      {
          static $places = [
              'Berlin',
              'Budapest',
              'Cincinnati',
              'Denver',
              'Helsinki',
              'Lisbon',
              'Moscow',
              'Nairobi',
              'Oslo',
              'Rio',
              'Tokyo'
          ];

      Далее нам потребуется включить оператор use сверху класса PlacesTableSeeder, чтобы проще ссылаться на фасад DB в коде:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      use IlluminateSupportFacadesDB;
      
      class PlacesTableSeeder extends Seeder
      …
      

      Теперь мы можем пробежаться по значениям массива $places с помощью цикла foreach и добавить их в нашу таблицу places с помощью конструктора запросов:

      database/seeds/PlacesTableSeeder.php

      …
          public function run()
          {
              foreach (self::$places as $place) {
                  DB::table('places')->insert([
                      'name' => $place,
                      'visited' => rand(0,1) == 1
                  ]);
              }
          }
      
      

      Цикл foreach проходит по каждому значению статического массива $places. В каждой итерации мы используем фасад DB для добавления новой строки в таблице places. Мы сохраним в поле name название места, которое мы только что получили из массива $places, а для поля visited будет использоваться случайное значение 0 или 1.

      Класс PlacesTableSeeder будет выглядеть следующим образом после всех обновлений:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      use IlluminateSupportFacadesDB;
      
      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
                  ]);
              }
          }
      }
      

      Сохраните и закройте файл после внесения всех изменений.

      Классы типа seeder не загружаются автоматически в приложение. Нам нужно изменить главный класс DatabaseSeeder, чтобы добавить вызов класса типа seeder, который мы только что создали.

      Откройте файл database/seeds/DatabaseSeeder.php с помощью nano или вашего любимого редактора:

      • nano database/seeds/DatabaseSeeder.php

      Класс DatabaseSeeder выглядит стандартным образом: он наследуется от класса Seeder и имеет метод run. Мы обновим этот метод, чтобы включить PlacesTableSeeder.

      Обновите текущий метод run внутри вашего класса DatabaseSeeder, удалив закомментированную строку и заменив ее на следующий выделенный код:

      database/seeds/DatabaseSeeder.php

      …
          public function run()
          {
              $this->call(PlacesTableSeeder::class);
          }
      ...
      

      Так будет выглядеть класс DatabaseSeeder после обновления:

      database/seeds/DatabaseSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      
      class DatabaseSeeder extends Seeder
      {
          /**
           * Seed the application's database.
           *
           * @return void
           */
          public function run()
          {
              $this->call(PlacesTableSeeder::class);
          }
      }
      
      
      

      Сохраните и закройте файл после внесения изменений в его содержимое.

      Мы завершили настройку миграции и наполнения для нашей таблицы places. На следующем шаге мы узнаем, как их запускать.

      Шаг 6 — Запуск миграции и наполнения базы данных

      Перед продолжением нужно убедиться, что ваше приложение готово и запущено. Мы настроим ключ шифрования приложения и получим доступ к приложению из браузера, чтобы протестировать веб-сервер.

      Чтобы сгенерировать ключ шифрования, требуемый Laravel, вы можете использовать команду artisan key:generate:

      • docker-compose exec app php artisan key:generate

      После получения ключа вы сможете получить доступ к приложению, указав в браузере имя хоста сервера или IP-адрес для порта 8000:

      http://server_host_or_ip:8000
      

      Страница будет выглядеть следующим образом:

      Ошибка MySQL

      Это означает, что приложение может подключаться к базе данных, но оно не может найти таблицу с именем places. Теперь мы создадим таблицу places, используя следующую команду migrate artisan:

      • docker-compose exec app php artisan migrate

      Вывод будет выглядеть следующим образом:

      Output

      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. Хотя мы не будем использовать эти дополнительные таблицы сейчас, они будут необходимы в будущем при расширении приложения для зарегистрированных пользователей и запланированных заданий. Пока что вы можете оставить их как есть.

      На данный момент наша таблица пустая. Нам нужно запустить команду db:seed, чтобы наполнить базу данных нашими выбранными местами:

      • docker-compose exec app php artisan db:seed

      В результате будет запущен наш класс seeder и будут добавлены образцы значений, которые мы определили в нашем классе PlacesTableSeeder. Вывод будет выглядеть следующим образом:

      Output

      Seeding: PlacesTableSeeder Seeded: PlacesTableSeeder (0.06 seconds) Database seeding completed successfully.

      Теперь перезагрузите страницу приложения в браузере. Вы увидите приблизительно следующую страницу:

      Демонстрационное приложение Laravel

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

      • docker-compose exec app php artisan db:wipe

      Output

      Dropped all tables successfully.

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

      • docker-compose exec app php artisan migrate --seed

      Output

      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. Как правило, он удаляет все таблицы, созданные в классах миграции, оставляя все остальные таблицы, которые могли быть созданы вручную. Вывод будет выглядеть следующим образом:

      Output

      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 для дальнейшей абстракции схемы базы данных вашего приложения.



      Source link