One place for hosting & domains

      Laravel

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

      Conteinerizando um aplicativo Laravel 6 para o desenvolvimento com o Docker Compose no Ubuntu 18.04


      Introdução

      Conteinerizar um aplicativo se refere ao processo de adaptar um aplicativo e seus componentes com a finalidade de conseguir executá-lo em ambientes leves, conhecidos como contêineres. Esses ambientes são isolados e descartáveis e podem ser aproveitados para o desenvolver, testar e implantar aplicativos para a produção.

      Neste guia, usaremos o Docker Compose para conteinerizar um aplicativo Laravel 6 para o desenvolvimento. Quando você tiver terminado, terá um aplicativo de demonstração Laravel funcionando em três contêineres de serviço separados:

      • Um serviço de app executando o PHP7.4-FPM;
      • Um serviço de db executando o MySQL 5.7;
      • Um serviço de nginx que usa o serviço app para analisar o código PHP antes de entregar o aplicativo Laravel para o usuário final.

      Para permitir um processo de desenvolvimento simplificado e facilitar a depuração do aplicativo, manteremos os arquivos do aplicativo em sincronia, usando volumes compartilhados. Também veremos como usar os comandos docker-compose exec para executar o Composer e o Artisan no contêiner do app.

      Pré-requisitos

      Passo 1 — Obtendo o aplicativo Demo

      Para começar, vamos buscar o aplicativo demo do Laravel do seu repositório do Github. Estamos interessados na ramificação do tutorial-01, que contém o aplicativo básico do Laravel que criamos no primeiro guia desta série.

      Para obter o código do aplicativo compatível com este tutorial, baixe a versão tutorial-1.0.1 para seu diretório home com:

      • cd ~
      • curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

      Vamos precisar do comando unzip para descompactar o código do aplicativo. Caso não tenha instalado esse pacote antes, faça isso agora com:

      • sudo apt update
      • sudo apt install unzip

      Agora, descompacte os conteúdos do aplicativo e renomeie o diretório desempacotado para ter um acesso mais fácil:

      • unzip travellist.zip
      • mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

      Navegue até o diretório travellist-demo:

      No próximo passo, vamos criar um arquivo de configuração .env para configurar o aplicativo.

      Passo 2 — Configurando o arquivo .env do aplicativo

      Os arquivos de configuração do Laravel estão localizados em um diretório chamado config, dentro do diretório raiz do aplicativo. Além disso, um arquivo .env é usado para preparar a configuração dependente do ambiente, como credenciais e quaisquer informações que possam variar entre as implantações. Esse arquivo não está incluído no controle de revisão.

      Aviso: o arquivo de configuração de ambiente contém informações confidenciais sobre o seu servidor, incluindo credenciais para o banco de dados e chaves de segurança. Por esse motivo, nunca compartilhe esse arquivo publicamente.

      Os valores contidos no arquivo .env terão precedência sobre os valores definidos nos arquivos de configuração regulares, localizados no diretório config. Cada instalação em novo ambiente exige um arquivo de ambiente personalizado para definir coisas como as configurações da conexão com o banco de dados, as opções de depuração, a URL do aplicativo, entre outros itens que possam variar, dependendo do ambiente em que o aplicativo esteja funcionando.

      Agora, vamos criar um novo arquivo .env para personalizar as opções de configuração do ambiente de desenvolvimento que estamos preparando. O Laravel vem com um exemplo de arquivo do .env que podemos copiar para criar o nosso:

      Abra este arquivo usando o nano ou outro editor de texto de sua escolha:

      O arquivo .env atual do aplicativo demo travellist contém as configurações para usar um banco de dados local do MySQL, com o 127.0.0.1 como o host do banco de dados. Precisamos atualizar a variável DB_HOST para que ela aponte para o serviço de banco de dados que vamos criar em nosso ambiente do Docker. Neste guia, vamos chamar nosso serviço de banco de dados de db. Vá em frente e substitua o valor listado de DB_HOST pelo nome do serviço de banco de dados:

      .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
      ...
      

      Caso queira, sinta-se à vontade para alterar o nome do bando de dados, nome de usuário e senha. Essas variáveis serão aproveitadas em um próximo passo, onde iremos definir o arquivo docker-compose.yml para configurar nossos serviços.

      Salve o arquivo quando terminar de editar. Se você usou o nano, você pode fazer isso pressionando Ctrl+x, depois Y e Enter para confirmar.

      Passo 3 — Configurando o Dockerfile do aplicativo

      Embora ambos os nossos serviços do MySQL e do Nginx se baseiem em imagens padronizadas, obtidas a partir do Docker Hub,ainda precisaremos compilar uma imagem personalizada para o contêiner do aplicativo. Criaremos um novo Dockerfile para isso.

      Nossa imagem do travellist irá basear-se na imagem oficial do PHP, php:7.4-fpm​​​ do Docker Hub. Além desse ambiente básico do PHP-FPM, instalaremos alguns módulos adicionais do PHP e a ferramenta de gerenciamento de dependência do Composer.

      Também iremos criar um novo usuário do sistema; isso é necessário para executar comandos do artisan e do composer durante o desenvolvimento do aplicativo. A configuração do uid garante que o usuário dentro do contêiner tenha o mesmo uid que o usuário do sistema no seu computador host, onde você estiver executando o Docker. Dessa forma, quaisquer arquivos criados por esses comandos são replicados no host com as permissões corretas. Isso também significa que você poderá usar o editor de códigos de sua escolha no computador host para desenvolver o aplicativo que está executando dentro dos contêineres.

      Crie um novo Dockerfile com:

      Copie o seguinte conteúdo para o seu Dockerfile:

      Dockerfile

      FROM php:7.4-fpm
      
      # Arguments defined in docker-compose.yml
      ARG user
      ARG uid
      
      # Install system dependencies
      RUN apt-get update && apt-get install -y 
          git 
          curl 
          libpng-dev 
          libonig-dev 
          libxml2-dev 
          zip 
          unzip
      
      # Clear cache
      RUN apt-get clean && rm -rf /var/lib/apt/lists/*
      
      # Install PHP extensions
      RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
      
      # Get latest Composer
      COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
      
      # Create system user to run Composer and Artisan Commands
      RUN useradd -G www-data,root -u $uid -d /home/$user $user
      RUN mkdir -p /home/$user/.composer && 
          chown -R $user:$user /home/$user
      
      # Set working directory
      WORKDIR /var/www
      
      USER $user
      
      

      Não se esqueça de salvar o arquivo quando terminar.

      Nosso Dockerfile começa pela definição da imagem base que estamos usando: php:7.4-fpm.

      Após instalar os pacotes do sistema e as extensões do PHP, instalaremos o Composer, copiando o executáve composer de sua última imagem oficial para nossa imagem do aplicativo.

      Então, um novo usuário do sistema é criado e configurado usando os argumentos user e uid que foram declarados no início do Dockerfile. Esses valores serão injetados pelo Docker Compose no momento da compilação.

      Por fim, definimos o dir padrão em funcionamento como /var/www e trocamos para o usuário recém-criado. Isso garantirá que, ao executar os comandos do composer e do artisan no contêiner do aplicativo, você esteja se conectando como um usuário comum e que esteja no diretório correto.

      Passo 4 — Configurando os arquivos de configuração do Nginx e os arquivos de despejo do banco de dados

      Ao criar ambientes de desenvolvimento com o Docker Compose, com frequência, é necessário compartilhar os arquivos de configuração ou de inicialização com os contêineres de serviço, para assim configurar ou inicializar esses serviços. Essa prática facilita fazer alterações nos arquivos de configuração para ajustar seu ambiente enquanto estiver desenvolvendo o aplicativo.

      Agora, vamos definir uma pasta com os arquivos que serão usados para configurar e inicializar nossos contêineres de serviço.

      Para configurar o Nginx, compartilharemos um arquivo travellist.conf que configurará como o aplicativo será atendido. Crie a pasta docker-compose/nginx com:

      • mkdir -p docker-compose/nginx

      Abra um novo arquivo chamado de travellist.conf dentro desse diretório:

      • nano docker-compose/nginx/travellist.conf

      Copie a seguinte configuração do Nginx para aquele arquivo:

      docker-compose/nginx/travellist.conf

      
      server {
          listen 80;
          index index.php index.html;
          error_log  /var/log/nginx/error.log;
          access_log /var/log/nginx/access.log;
          root /var/www/public;
          location ~ .php$ {
              try_files $uri =404;
              fastcgi_split_path_info ^(.+.php)(/.+)$;
              fastcgi_pass app:9000;
              fastcgi_index index.php;
              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_param PATH_INFO $fastcgi_path_info;
          }
          location / {
              try_files $uri $uri/ /index.php?$query_string;
              gzip_static on;
          }
      }
      

      Esse arquivo irá configurar o Nginx para escutar na porta 80 e usar o index.php como a página de índice padrão. Ele definirá o diretório base como /var/www/public e, em seguida, configurará o Nginx para usar o serviço app na porta 9000 para processar os arquivos *.php.

      Salve e feche o arquivo quando terminar de editar.

      Para configurar o banco de dados do MySQL, compartilharemos um banco de dados de despejo que será importado quando o contêiner for inicializado. Esta é uma característica fornecida pela imagem do MySQL 5.7, que usaremos naquele contêiner.

      Crie uma nova pasta para seus arquivos de inicialização do MySQL dentro da pasta docker-compose:

      • mkdir docker-compose/mysql

      Abra um novo arquivo .sql:

      • nano docker-compose/mysql/init_db.sql

      O arquivo de despejo do MySQL se baseia no banco de dados que configuramos em nosso guia do Laravel em LEMP. Ele criará uma nova tabela chamada places. Então, ele preencherá a tabela com um conjunto de exemplos de localidade.

      Adicione o código a seguir ao arquivo:

      docker-compose/mysql/db_init.sql

      DROP TABLE IF EXISTS `places`;
      
      CREATE TABLE `places` (
        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
        `visited` tinyint(1) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
      
      INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);
      

      A tabela places contém três campos: id, name e visited. O campo visited é um sinalizador utilizado para identificar os lugares que ainda não foram visitados. Sinta-se à vontade para modificar os exemplos de localidade ou incluir novos locais. Salve e feche o arquivo quando terminar.

      Terminamos de configurar o Dockerfile do aplicativo e os arquivos de configuração de serviço. Em seguida, configuraremos o Docker Compose para usar esses arquivos ao criar nossos serviços.

      O Docker Compose permite a criação de ambientes multicontêiner para os aplicativos em execução no Docker. Ele usa as definições de serviço para compilar ambientes totalmente personalizáveis com vários contêineres que podem compartilhar redes e volumes de dados. Isso permite uma integração perfeita entre os componentes do aplicativo.

      Para configurar nossas definições de serviço, criaremos um novo arquivo chamado docker-compose.yml. Este arquivo fica normalmente localizado na raiz da pasta do aplicativo. Ele define seu ambiente em contêiner, incluindo as imagens base que você usará para compilar seus contêineres e como seus serviços irão interagir.

      Vamos definir três serviços diferentes em nosso arquivo docker-compose.yml: app, db e nginx.

      O serviço app compilará uma imagem chamada travellist, com base no Dockerfile que criamos anteriormente. O contêiner definido por esse serviço executará um servidor php-fpm para analisar o código PHP e enviará os resultados de volta para o serviço do nginx, o qual estará em execução em um contêiner separado. O serviço mysql define um contêiner que executa um servidor do MySQL 5.7. Nosso serviços irão compartilhar uma conexão de rede de ponte chamada de travellist.

      Os arquivos do aplicativo serão sincronizados nos serviços do app e do nginx através de montagens associadas. As montagens bind são úteis em ambientes de desenvolvimento porque elas levam em conta um desempenho sincronizado bidirecional entre o computador host e os contêineres.

      Crie um novo arquivo docker-compose.yml na raiz da pasta do aplicativo:

      Um arquivo típico do docker-compose.yml começa com uma definição de versão, seguido de um nó de services, sob o qual todos os serviços são definidos. As redes compartilhadas são geralmente definidas no final desse arquivo.

      Para começar, copie este código boilerplate para o seu arquivo docker-compose.yml:

      docker-compose.yml

      version: "3.7"
      services:
      
      
      networks:
        travellist:
          driver: bridge
      

      Agora, vamos editar o nó de services para incluir os serviços app, db e nginx.

      O serviço app

      O serviço app configurará um contêiner chamado travellist-app. Ele compila uma nova imagem do Docker com base em um Dockerfile - localizado no mesmo caminho que o arquivo docker-compose.yml. A nova imagem será salva localmente sob o nome travellist.

      Embora o diretório base que está sendo atendido como o aplicativo esteja localizado no contêiner do nginx, também precisamos dos arquivos do aplicativo em algum lugar dentro do contêiner de app, para que possamos executar tarefas de linha de comando com a ferramenta Laravel Artisan.

      Copie a seguinte definição de serviço sob seu nó de services, dentro do arquivo docker-compose.yml:

      docker-compose.yml

        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: travellist
          container_name: travellist-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - travellist
      

      Essas configurações fazem o seguinte:

      • build: esta configuração diz ao Docker Compose para compilar uma imagem local para o serviço app, usando o caminho especificado (contexto) e o Dockerfile para obter instruções. Os argumentos user e uid são injetados no Dockerfile para personalizar os comandos de criação de usuário no momento da compilação.
      • image: o nome que será usado para a imagem que está sendo criada.
      • container_name: define o nome do contêiner desse serviço.
      • restart: sempre reinicia, a menos que o serviço seja interrompido.
      • working_dir: define o diretório padrão para esse serviço como /var/www.
      • volumes: cria um volume compartilhado que sincronizará o conteúdo do diretório atual para o /var/www, dentro do contêiner. Note que esse não é o seu diretório raiz, uma vez que ele ficará no contêiner nginx.
      • networks: define esse serviço para usar uma rede chamada travellist.

      O serviço db

      O serviço db usa uma imagem de MySQL 5.7 pré-compilada do Docker Hub. Como o Docker Compose carrega automaticamente os arquivos da variável .env, localizados no mesmo diretório que o arquivo docker-compose.yml, podemos obter nossas configurações de banco de dados do arquivo .env do Laravel que criamos em um passo anterior.

      Inclua a seguinte definição de serviço no nó de seus services, logo após o serviço app:

      docker-compose.yml

        db:
          image: mysql:5.7
          container_name: travellist-db
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            SERVICE_TAGS: dev
            SERVICE_NAME: mysql
          volumes:
            - ./docker-compose/mysql:/docker-entrypoint-initdb.d
          networks:
            - travellist
      

      Essas configurações fazem o seguinte:

      • image: define a imagem do Docker que deve ser usada para este contêiner. Neste caso, estamos usando uma imagem MySQL 5.7 do Docker Hub.
      • container_name: define o nome do contêiner para este serviço: travellist-db​​​.
      • restart: sempre reinicia este serviço, a menos que ele seja explicitamente interrompido.
      • environment: define as variáveis de ambiente no novo contêiner. Estamos usando valores obtidos a partir do arquivo .env do Laravel para configurar nosso serviço do MySQL, o qual irá criar - automaticamente - um novo banco de dados e um usuário, com base nas variáveis de ambiente fornecidas.
      • volumes: cria um volume para compartilhar um arquivo de despejo de banco de dados .sql que será usado para inicializar o banco de dados do aplicativo. A imagem do MySQL importará automaticamente os arquivos .sql colocados no diretório /docker-entrypoint-initdb.d dentro do contêiner.
      • networks: define esse serviço para usar uma rede chamada travellist.

      O serviço nginx

      O serviço nginx usa uma imagem do Nginx pré-compilada sobre o Alpine, uma distribuição de Linux leve. Ela cria um contêiner chamado travellist-nginx e usa a definição de ports para criar um redirecionamento da porta 8000 - no sistema host - para a porta 80 - dentro do contêiner.

      Inclua a seguinte definição de serviço no nó de seus services, logo após o serviço db:

      docker-compose.yml

        nginx:
          image: nginx:1.17-alpine
          container_name: travellist-nginx
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d
          networks:
            - travellist
      

      Essas configurações fazem o seguinte:

      • image: define a imagem do Docker que deve ser usada para este contêiner. Neste caso, estamos usando a imagem do Alpine Nginx 1.17.
      • container_name: define o nome do contêiner para este serviço: travellist-nginx​​​.
      • restart: sempre reinicia este serviço, a menos que ele seja explicitamente interrompido.
      • ports: define um redirecionamento de porta que permitirá o acesso externo através da porta 8000 para o servidor Web em execução na porta 80, dentro do contêiner.
      • volumes: cria dois volumes compartilhados. O primeiro sincronizará o conteúdo do diretório atual para o /var/www, dentro do contêiner. Dessa forma, quando fizer alterações locais nos arquivos do aplicativo, elas serão refletidas rapidamente no aplicativo que está sendo atendido pelo Nginx, dentro do contêiner. O segundo volume garantirá que o nosso arquivo de configuração do Nginx, localizado em docker-compose/nginx/travellist.conf, seja copiado para a pasta de configuração do Nginx do contêiner.
      • networks: define esse serviço para usar uma rede chamada travellist.

      Arquivo docker-compose.yml finalizado

      Nosso arquivo docker-compose.yml finalizado ficará com esta aparência:

      docker-compose.yml

      version: "3.7"
      services:
        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: travellist
          container_name: travellist-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - travellist
      
        db:
          image: mysql:5.7
          container_name: travellist-db
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            SERVICE_TAGS: dev
            SERVICE_NAME: mysql
          volumes:
            - ./docker-compose/mysql:/docker-entrypoint-initdb.d
          networks:
            - travellist
      
        nginx:
          image: nginx:alpine
          container_name: travellist-nginx
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d/
          networks:
            - travellist
      
      networks:
        travellist:
          driver: bridge
      

      Certifique-se de salvar o arquivo quando terminar.

      Agora, usaremos os comandos do docker-compose para compilar a imagem do aplicativo e executar os serviços que especificamos em nossa configuração.

      Compile a imagem do app com o seguinte comando:

      Esse comando pode levar alguns minutos para completar. Você verá um resultado similar a este:

      Output

      Building app Step 1/11 : FROM php:7.4-fpm ---> fa37bd6db22a Step 2/11 : ARG user ---> Running in f71eb33b7459 Removing intermediate container f71eb33b7459 ---> 533c30216f34 Step 3/11 : ARG uid ---> Running in 60d2d2a84cda Removing intermediate container 60d2d2a84cda ---> 497fbf904605 Step 4/11 : RUN apt-get update && apt-get install -y git curl libpng-dev libonig-dev ... Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer ---> e499f74896e3 Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user ---> Running in 232ef9c7dbd1 Removing intermediate container 232ef9c7dbd1 ---> 870fa3220ffa Step 9/11 : RUN mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user ---> Running in 7ca8c0cb7f09 Removing intermediate container 7ca8c0cb7f09 ---> 3d2ef9519a8e Step 10/11 : WORKDIR /var/www ---> Running in 4a964f91edfa Removing intermediate container 4a964f91edfa ---> 00ada639da21 Step 11/11 : USER $user ---> Running in 9f8e874fede9 Removing intermediate container 9f8e874fede9 ---> fe176ff4702b Successfully built fe176ff4702b Successfully tagged travellist:latest

      Quando a compilação estiver concluída, você poderá executar o ambiente em segundo plano com:

      Output

      Creating travellist-db ... done Creating travellist-app ... done Creating travellist-nginx ... done

      Isso executará seus contêineres em segundo plano. Para exibir informações sobre o estado de seus serviços ativos, execute:

      Você verá um resultado como esse:

      Output

      Name Command State Ports ------------------------------------------------------------------------------- travellist-app docker-php-entrypoint php-fpm Up 9000/tcp travellist-db docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp travellist-nginx nginx -g daemon off; Up 0.0.0.0:8000->80/tcp

      Agora, seu ambiente está funcionando! Porém, ainda precisaremos executar alguns comandos para concluir a configuração do aplicativo. Você pode usar o comando docker-compose exec para executar comandos nos contêineres de serviço, como um ls -l para exibir informações detalhadas sobre os arquivos no diretório do aplicativo:

      • docker-compose exec app ls -l

      Output

      total 256 -rw-rw-r-- 1 sammy 1001 738 Jan 15 16:46 Dockerfile -rw-rw-r-- 1 sammy 1001 101 Jan 7 08:05 README.md drwxrwxr-x 6 sammy 1001 4096 Jan 7 08:05 app -rwxr-xr-x 1 sammy 1001 1686 Jan 7 08:05 artisan drwxrwxr-x 3 sammy 1001 4096 Jan 7 08:05 bootstrap -rw-rw-r-- 1 sammy 1001 1501 Jan 7 08:05 composer.json -rw-rw-r-- 1 sammy 1001 179071 Jan 7 08:05 composer.lock drwxrwxr-x 2 sammy 1001 4096 Jan 7 08:05 config drwxrwxr-x 5 sammy 1001 4096 Jan 7 08:05 database drwxrwxr-x 4 sammy 1001 4096 Jan 15 16:46 docker-compose -rw-rw-r-- 1 sammy 1001 1015 Jan 15 16:45 docker-compose.yml -rw-rw-r-- 1 sammy 1001 1013 Jan 7 08:05 package.json -rw-rw-r-- 1 sammy 1001 1405 Jan 7 08:05 phpunit.xml drwxrwxr-x 2 sammy 1001 4096 Jan 7 08:05 public -rw-rw-r-- 1 sammy 1001 273 Jan 7 08:05 readme.md drwxrwxr-x 6 sammy 1001 4096 Jan 7 08:05 resources drwxrwxr-x 2 sammy 1001 4096 Jan 7 08:05 routes -rw-rw-r-- 1 sammy 1001 563 Jan 7 08:05 server.php drwxrwxr-x 5 sammy 1001 4096 Jan 7 08:05 storage drwxrwxr-x 4 sammy 1001 4096 Jan 7 08:05 tests -rw-rw-r-- 1 sammy 1001 538 Jan 7 08:05 webpack.mix.js

      Agora, vamos executar o composer install para instalar as dependências do aplicativo:

      • docker-compose exec app composer install

      Você verá um resultado como esse:

      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%) - Installing erusev/parsedown (1.7.4): Downloading (100%) - Installing symfony/polyfill-ctype (v1.13.1): Downloading (100%) - Installing phpoption/phpoption (1.7.2): Downloading (100%) - Installing vlucas/phpdotenv (v3.6.0): Downloading (100%) - Installing symfony/css-selector (v5.0.2): Downloading (100%) … Generating optimized autoload files > IlluminateFoundationComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully.

      A última coisa que precisamos fazer - antes de testar o aplicativo - é gerar uma chave única para o aplicativo com a artisan, a ferramenta de linha de comando do Laravel. Esta chave é usada para criptografar sessões de usuário e outros dados confidenciais:

      • docker-compose exec app php artisan key:generate

      Output

      Application key set successfully.

      Agora, vá até seu navegador e acesse o nome de domínio ou endereço IP do seu servidor na porta 8000:

      http://server_domain_or_IP:8000
      

      Você verá uma página como esta:

      Demo do aplicativo Laravel

      Você pode usar o comando logs para verificar os registros gerados por seus serviços:

      • docker-compose logs nginx
      Attaching to travellist-nginx
      travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:25 +0000] "GET / HTTP/1.1" 200 626 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
      travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:26 +0000] "GET /favicon.ico HTTP/1.1" 200 0 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
      travellist-nginx | 192.168.160.1 - - [23/Jan/2020:13:57:42 +0000] "GET / HTTP/1.1" 200 626 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
      …
      

      Se quiser pausar seu ambiente do Docker Compose ao mesmo tempo que mantém o estado de todos seus serviços, execute:

      Output

      Pausing travellist-db ... done Pausing travellist-nginx ... done Pausing travellist-app ... done

      Em seguida, você poderá retomar os seus serviços com:

      Output

      Unpausing travellist-app ... done Unpausing travellist-nginx ... done Unpausing travellist-db ... done

      Para fechar seu ambiente do Docker Compose e remover todos os seus contêineres, redes e volumes, execute:

      Output

      Stopping travellist-nginx ... done Stopping travellist-db ... done Stopping travellist-app ... done Removing travellist-nginx ... done Removing travellist-db ... done Removing travellist-app ... done Removing network travellist-laravel-demo_travellist

      Para obter um panorama de todos os comandos do Docker Compose, verifique a Referência da linha de comando do Docker Compose.

      Conclusão

      Neste guia, configuramos um ambiente do Docker com três contêineres, usando o Docker Compose para definir nossa infraestrutura em um arquivo YAML.

      A partir deste ponto, você poderá trabalhar no seu aplicativo do Laravel sem a necessidade de instalar e configurar um servidor Web local para desenvolvimentos e testes. Além disso, você estará trabalhando com um ambiente descartável e que pode ser facilmente replicado e distribuído, o que pode ser útil ao desenvolver seu aplicativo e ao passar para um ambiente de produção.



      Source link

      How To Use Database Migrations and Seeders to Abstract Database Setup in Laravel


      Migrations and seeders are powerful database utilities provided by the Laravel PHP framework to allow developers to quickly bootstrap, destroy and recreate an application’s database. These utilities help to minimize database inconsistency problems that can arise with multiple developers working on the same application: new contributors need only to run a couple artisan commands to set the database up on a fresh install.

      In this guide, we’ll create migrations and seeders to populate a Laravel demo application’s database with sample data. At the end, you will be able to destroy and recreate your database tables as many times as you want, using only artisan commands.

      Prerequisites

      In order to follow this guide, you’ll need:

      Note: In this guide, we’ll use a containerized development environment managed by Docker Compose to run the application, but you may also opt to run the application on a LEMP server. To set this up, you can follow our guide on How to Install and Configure Laravel with LEMP on Ubuntu 18.04.

      Step 1 — Obtaining the Demo Application

      To get started, we’ll fetch the demo Laravel application from its GitHub repository. We’re interested in the tutorial-02 branch, which includes a Docker Compose setup to run the application on containers. In this example, we’ll download the application to our home folder, but you can use any directory of your choice:

      • cd ~
      • curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-2.0.1.zip -o travellist.zip

      Because we downloaded the application code as a .zip file, we’ll need the unzip command to unpack it. If you haven’t done so recently, update your machine’s local package index:

      Then install the unzip package:

      Following that, unzip the contents of the application:

      Then rename the unpacked directory to travellist-demo for easier access:

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

      In the next step, we’ll create a .env configuration file to set up the application.

      Step 2 — Setting Up the Application’s .env File

      In Laravel, a .env file is used to set up environment-dependent configurations, such as credentials and any information that might vary between deployments. This file is not included in revision control.

      Warning: The environment configuration file contains sensitive information about your server, including database credentials and security keys. For that reason, you should never share this file publicly.

      The values contained in the .env file will take precedence over the values set in regular configuration files located in the config directory. Each installation on a new environment requires a tailored environment file to define things such as database connection settings, debug options, and the application URL, among other items that may vary depending on which environment the application is running.

      Navigate to the travellist-demo directory:

      We’ll now create a new .env file to customize the configuration options for the development environment we’re setting up. Laravel comes with an example.env file that we can copy to create our own:

      Open this file using nano or your text editor of choice:

      This is how your .env file looks like now:

      .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

      The current .env file from the travellist demo application contains settings to use the containerized environment we’ve created with Docker Compose in the last part of this series. You don’t need to change any of these values, but you are free to modify the DB_DATABASE, DB_USERNAME and DB_PASSWORD if you wish, since these are pulled by our docker-compose.yml file automatically to set up the development database. Just make sure the DB_HOST variable remains unchanged, since it references the name of our database service within the Docker Compose environment.

      If you make any changes to the file, make sure to save and close it by pressing CTRL + X, Y, then ENTER.

      Note: If you have opted to run the application on a LEMP server, you’ll need to change the highlighted values to reflect your own database settings, including the DB_HOST variable.

      Step 3 — Installing Application Dependencies with Composer

      We’ll now use Composer, PHP’s dependency management tool, to install the application’s dependencies and make sure we’re able to execute artisan commands.

      Bring up your Docker Compose environment with the following command.
      This will build the travellist image for the app service and pull in the additional Docker images required by the nginx and db services, in order to create the application environment:

      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

      This operation might take a few minutes to complete. Once the process is finished, we can run Composer to install the application’s dependencies.

      To execute composer and other commands in the app service container, we’ll use docker-compose exec. The exec command allows us to execute any command of our choice on containers managed by Docker Compose. It uses the following syntax: docker-compose exec service_name command.

      Note: In case you have opted to use a LEMP server to run the demo application, you should ignore the docker-compose exec app portion of the commands listed throughout this guide. For example, instead of running the following command as it’s written, you would just run:

      To execute composer install in the app container, run:

      • 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%) …

      When Composer is finished installing the application’s dependencies, you’ll be able to execute artisan commands. To test that the application is able to connect to the database, run the following command which will clean up any pre-existing tables:

      • docker-compose exec app php artisan db:wipe

      This command will drop any pre-existing tables on the configured database. If it ran successfully and the application is able to connect to the database, you’ll see output like this:

      Output

      Dropped all tables successfully.

      Now that you have installed the application dependencies with Composer, you can use the artisan tool to create migrations and seeders.

      Step 4 — Creating Database Migrations

      The artisan command line tool that ships with Laravel contains a series of helper commands that can be used to manage the application and bootstrap new classes. To generate a new migration class, we can use the make:migration command as follows:

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

      Laravel infers the operation to be executed (create), the name of the table (places), and whether this migration will create a new table or not, based on the descriptive name provided to the make:migration command.

      You’ll see output similar to this:

      Output

      Created Migration: 2020_02_03_143622_create_places_table

      This will generate a new file in the application’s database/migrations directory. The timestamp included in the auto-generated file is used by Laravel to determine in which order migrations should be executed.

      Use your text editor of choice to open the generated migration file. Remember to replace the highlighted value with your own migration file name:

      • nano database/migrations/2020_02_03_143622_create_places_table.php

      The generated migration file contains a class called 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');
          }
      }
      
      

      This class has two methods: up and down. Both methods contain bootstrap code that you can extend to customize what happens when that migration is executed and also what happens when it is rolled back.

      We’ll modify the up method so that the places table reflects the structure we’re already using in the current application’s version:

      • id: primary key field.
      • name: name of the place.
      • visited: whether or not this place was already visited.

      The Laravel schema builder exposes methods for creating, updating and deleting tables in a database. The Blueprint class defines the table’s structure and it provides several methods to abstract the definition of each table field.

      The auto-generated code sets up a primary id field called id. The timestamps method creates two datetime fields that are automatically updated by the underlying database classes when data is inserted or updated within that table. In addition to these, we’ll need to include a name and a visited field.

      Our name field will be of type string, and our visited field will be set with the boolean type. We’ll also set a default value of 0 for the visited field, so that if no value is passed, it means the place was not visited yet. This is how the up method will look like now:

      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();
              });
          }
      …
      

      Note: You can find the full list of available column types in the Laravel documentation.

      After including the two highlighted lines on your own migration script, save and close the file.

      Your migration is now ready to be executed via artisan migrate. However, that would only create an empty table; we also need to be able to insert sample data for development and testing. In the next step, we’ll see how to do that using database seeders.

      Step 5 — Creating Database Seeders

      A seeder is a special class used to generate and insert sample data (seeds) in a database. This is an important feature in development environments, since it allows you to recreate the application with a fresh database, using sample values that you’d otherwise have to manually insert each time the database is recreated.

      We’ll now use the artisan command to generate a new seeder class for our places table called PlacesTableSeeder:

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

      The command will create a new file called PlacesTableSeeder.php inside the database/seeds directory. Open that file using your text editor of choice:

      • nano database/seeds/PlacesTableSeeder.php

      This is what the auto-generated PlacesTableSeeder.php file looks like:

      database/seeds/PlacesTableSeeder.php

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

      Our new seeder class contains an empty method named run. This method will be called when the db:seed Artisan command is executed.

      We need to edit the run method in order to include instructions to insert sample data in the database. We’ll use the Laravel query builder to streamline this process.

      The Laravel query builder offers a fluent interface for database operations such as inserting, updating, deleting, and retrieving data. It also introduces safeguards against SQL injection attacks. The query builder is exposed by the DB facade - a static proxy to underlying database classes in the service container.

      To get started, we’ll create a static class variable to hold all the sample places we want to insert into the database as an array. This will allow us to use a foreach loop to iterate through all values, inserting each one in the database using the query builder.

      We’ll call this variable $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'
          ];

      Next, we’ll need to include a use statement at the top of our PlacesTableSeeder class to facilitate referencing the DB facade throughout the code:

      database/seeds/PlacesTableSeeder.php

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

      We can now iterate through the $places array values using a foreach loop, and insert each one in our places table using the query builder:

      database/seeds/PlacesTableSeeder.php

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

      The foreach loop iterates through each value of the $places static array. At each iteration, we use the DB facade to insert a new row at the places table. We set the name field to the name of the place we just obtained from the $places array, and we set the visited field to a random value of either 0 or 1.

      This is what the full PlacesTableSeeder class will look like after all the updates:

      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
                  ]);
              }
          }
      }
      

      Save and close the file when you’re done making these changes.

      Seeder classes aren’t automatically loaded in the application. We need to edit the main DatabaseSeeder class to include a call to the seeder we’ve just created.

      Open the database/seeds/DatabaseSeeder.php file using nano or your favorite editor:

      • nano database/seeds/DatabaseSeeder.php

      The DatabaseSeeder class looks like any other seeder: it extends from the Seeder class and has a run method. We’ll update this method to include a call to PlacesTableSeeder.

      Update the current run method inside your DatabaseSeeder class by deleting the commented-out line and replacing it with the following highlighted code:

      database/seeds/DatabaseSeeder.php

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

      This is how the full DatabaseSeeder class will look like after the update:

      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);
          }
      }
      
      
      

      Save and close the file when you’re done updating its content.

      We have now finished setting up both a migration and a seeder for our places table. In the next step, we’ll see how to execute them.

      Step 6 — Running Database Migrations and Seeders

      Before proceeding, we need to make sure your application is up and running. We’ll set up the application encryption key and then access the application from a browser to test the web server.

      To generate the encryption key required by Laravel, you can use the artisan key:generate command:

      • docker-compose exec app php artisan key:generate

      Once the key has been generated, you’ll be able to access the application by pointing your browser to your server hostname or IP address on port 8000:

      http://server_host_or_ip:8000
      

      You’ll see a page like this:

      MySQL error

      That means the application is able to connect to the database, but it couldn’t find a table called places. We’ll create the places table now, using the following migrate artisan command:

      • docker-compose exec app php artisan migrate

      You’ll get output similar to this:

      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)

      You’ll notice that a few other migrations were executed along with the create_places_table migration we’ve set up. These migrations are auto generated when Laravel is installed. Although we won’t be using these additional tables now, they will be needed in the future when we expand the application to have registered users and scheduled jobs. For now, you can just leave them as is.

      At this point our table is still empty. We need to run the db:seed command to seed the database with our sample places:

      • docker-compose exec app php artisan db:seed

      This will run our seeder and insert the sample values we defined within our PlacesTableSeeder class. You’ll see output similar to this:

      Output

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

      Now, reload the application page on your browser. You’ll see a page similar to this:

      Demo Laravel Application

      Whenever you need to start from scratch, you can drop all your database tables with:

      • docker-compose exec app php artisan db:wipe

      Output

      Dropped all tables successfully.

      To run the app migrations and seed the tables in a single command, you can use:

      • 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.

      If you want to roll back a migration, you can run:

      • docker-compose exec app php artisan migrate:rollback

      This will trigger the down method for each migration class inside the migrations folder. Typically, it will remove all the tables that were created through migration classes, leaving alone any other tables that might have been manually created. You’ll see output like this:

      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)

      The rollback command is especially useful when you’re making changes to application models and a db:wipe command can’t be used - for instance, if multiple systems depend on the same database.

      Conclusion

      In this guide, we’ve seen how to use database migrations and seeders to facilitate setting up development and testing databases for a Laravel 6 application.

      As a next step, you might want to check the Laravel documentation for more details on how to use the query builder, and how to use Eloquent models to abstract your application’s database schema even further.



      Source link