Tutorial

Como usar o Migrations de banco de dados e o Seeders para abstrair a configuração do banco de dados em Laravel

PHPPHP FrameworksDatabasesLaravel

Migrations [Migrações] e seeders [Propagadores] são utilitários de banco de dados eficazes, fornecidos pela framework do Laravel para PHP, que permite que os desenvolvedores façam a inicialização, destruição e recriação do banco de dados de um aplicativo rapidamente. Esses utilitários ajudam a minimizar problemas de inconsistência de banco de dados que podem surgir com vários desenvolvedores trabalhando no mesmo aplicativo: novos colaboradores precisam apenas executar alguns comandos do artisan para definir o banco de dados em uma nova instalação.

Neste guia, vamos criar migrations (migrações) e seeders (propagadores) para preencher o banco de dados de um aplicativo de demonstração do Laravel com dados de amostra. No final, você poderá destruir e recriar as tabelas de seu banco de dados quantas vezes quiser, usando apenas comandos do artisan.

Pré-requisitos

Para seguir este guia, você vai precisar do seguinte:

Nota: neste guia, utilizaremos um ambiente de desenvolvimento em contêiner, gerenciado pelo Docker Compose para executar o aplicativo, mas você também pode optar por executar o aplicativo em um servidor LEMP. Para configurar isso, siga nosso guia sobre Como instalar e configurar o Laravel com LEMP no Ubuntu 18.04.

Passo 1 — Obtendo o aplicativo Demo

Para começar, iremos buscar o aplicativo de demonstração do Laravel a partir de seu repositório do GitHub. Estamos interessados na ramificação do tutorial-02, que inclui uma Configuração do Docker Compose para executar o aplicativo em contêineres. Neste exemplo, baixaremos o aplicativo para nossa pasta base, mas também é possível usar qualquer diretório de sua escolha:

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

Como baixamos o código do aplicativo como um arquivo .zip, precisaremos do comando unzip para desempacotá-lo. Se não tiver feito isso recentemente, atualize o índice de pacotes local de sua máquina:

  • sudo apt update

Depois, instale o pacote unzip:

  • sudo apt install unzip

Em seguida, descompacte o conteúdo do aplicativo:

  • unzip travellist.zip

Em seguida, renomeie o diretório descompactado como travellist-demo para facilitar o acesso:

  • mv travellist-laravel-demo-tutorial-2.0.1 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

No Laravel, o arquivo .env é usado para configurar as configurações dependentes de 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 um novo ambiente exige um arquivo de ambiente personalizado para definir coisas como as configurações de conexão com o banco de dados, as opções de depuração, o URL do aplicativo, entre outros itens que possam variar, dependendo do ambiente em que o aplicativo esteja funcionando.

Navegue até o diretório travellist-demo:

  • cd travellist-demo

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:

  • cp .env.example .env

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

  • nano .env

Esta é a aparência do seu arquivo .env agora:

.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

O arquivo .env atual do aplicativo de demonstração travellist contém configurações para usar o ambiente em contêiner que criamos com o Docker Compose na última parte desta série. Você não precisa alterar nenhum desses valores, mas fique à vontade para modificar o DB_DATABASE, o DB_USERNAME e o DB_PASSWORD se quiser, uma vez que eles são puxados automaticamente pelo nosso arquivo docker-compose.yml para configurar o banco de dados de desenvolvimento. Certifique-se apenas de que a variável DB_HOST permaneça inalterada, uma vez que ela faz referência ao nome do nosso serviço de banco de dados dentro do ambiente do Docker Compose.

Caso faça quaisquer alterações no arquivo, certifique-se de salvar e fechar ele pressionando CTRL + X, Y e, então, ENTER.

Nota: caso tenha optado por executar o aplicativo em um servidor LEMP, será necessário alterar os valores destacados para refletir as configurações do seu próprio banco de dados, incluindo a variável DB_HOST.

Passo 3 — Instalando as dependências do aplicativo com o Composer

Agora, usaremos o Composer, uma ferramenta de gerenciamento de dependências do PHP, para instalar as dependências do aplicativo e garantir que possamos executar os comandos do artisan.

Abra seu ambiente do Docker Compose com o comando a seguir. Isso compilará a imagem do travellist para o serviço do app e extrairá imagens adicionais do Docker - exigidas pelos serviços nginx e db, para criar o ambiente do aplicativo:

  • docker-compose up -d
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

Esta operação pode levar alguns minutos para completar. Assim que o processo terminar, podemos executar o Composer para instalar as dependências do aplicativo.

Para executar o composer e outros comandos no contêiner de serviço do app, utilizaremos o docker-compose exec. O comando exec permite que executemos qualquer comando de nossa escolha em contêineres gerenciados pelo Docker Compose. Ele usa a seguinte sintaxe: docker-compose exec service_name command.

Nota: caso tenha optado por usar um servidor LEMP para executar o aplicativo de demonstração, ignore a parte do docker-compose exec app dos comandos listados durante todo este guia. Por exemplo, em vez de executar o seguinte comando como ele está escrito, você apenas executaria:

  • composer install

Para executar o composer install no contêiner do app, execute:

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

Quando o Composer terminar de instalar as dependências do aplicativo, você poderá executar os comandos do artisan. Para testar se o aplicativo consegue se conectar ao banco de dados, execute o seguinte comando que limpará quaisquer tabelas pré-existentes:

  • docker-compose exec app php artisan db:wipe

Esse comando removerá quaisquer tabelas pré-existentes do banco de dados configurado. Se ele foi executado com sucesso e o aplicativo conseguiu se conectar ao banco de dados, você verá um resultado parecido com este:

Output
Dropped all tables successfully.

Agora que você tem as dependências do aplicativo instaladas com o Composer, você pode utilizar a ferramenta artisan para criar migrations e seeders.

Passo 4 — Criando migrations de banco de dados

A ferramenta de linha de comando do artisan que vem com o Laravel contém uma série de comandos auxiliares que podem ser usados para gerenciar o aplicativo e inicializar novas classes. Para gerar uma nova classe de migration, podemos usar o comando make:migration como segue:

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

O Laravel deduz a operação a ser executada (create), o nome da tabela (places) e se essa migration criará uma nova tabela ou não, com base no nome descritivo fornecido para o comando make:migration.

Você verá um resultado semelhante a este:

Output
Created Migration: 2020_02_03_143622_create_places_table

Isso gerará um novo arquivo no diretório database/migrations do aplicativo. O carimbo de data/hora no arquivo gerado automaticamente é utilizado pelo Laravel para determinar em qual ordem as migrations devem ser executadas.

Utilize seu editor de texto preferido para abrir o arquivo migration gerado. Lembre-se de substituir o valor destacado pelo nome de arquivo de migration de sua escolha:

  • nano database/migrations/2020_02_03_143622_create_places_table.php

O arquivo de migration gerado contém uma classe chamada CreatePlacesTable:

database/migrations/2020_02_03_143622_create_places_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePlacesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('places', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('places');
    }
}

Essa classe tem dois métodos: up e down. Ambos os métodos contêm o código de inicialização que podem ser estendidos para personalizar o que acontece quando essa migration for executada e também o que acontece quando ela for revertida.

Vamos modificar o método up, de modo que a tabela places reflita a estrutura que já estamos usando na versão atual do aplicativo:

  • id: campo de chave primária.
  • name: nome do lugar.
  • visited: se este lugar já foi visitado ou não.

O compilador de esquemas do Laravel expõe métodos para a criação, atualização e exclusão de tabelas em um banco de dados. A classe Blueprint define a estrutura da tabela e ela proporciona vários métodos para abstrair a definição de cada campo da tabela.

O código gerado automaticamente define um campo de id primário chamado id. O método timestamps cria dois campos de datetime que são atualizados automaticamente pelas classes de banco de dados subjacentes, quando os dados são inseridos ou atualizados dentro dessa tabela. Além disso, precisaremos incluir um campo name e um visited.

Nosso campo name será do tipo string e nosso campo visited será definido com o tipo boolean. Também vamos definir um valor padrão de 0 para o campo visited, de modo que se nenhum valor for transferido, significa que o lugar ainda não foi visitado. É assim que o método up se parecerá agora:

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

Nota: você pode encontrar a lista completa dos tipos de coluna disponíveis na documentação do Laravel.

Após incluir as duas linhas destacadas em seu próprio script de migration, salve e feche o arquivo.

Agora, sua migration está pronta para ser executada através do artisan migrate. No entanto, isso criaria apenas uma tabela vazia; também precisamos conseguir inserir dados da amostra para o desenvolvimento e teste. No próximo passo, veremos como fazer isso usando os seeders de banco de dados.

Passo 5 — Criando os Seeders de banco de dados

Um seeder é uma classe especial utilizada para gerar e inserir dados de amostra (seeds) em um banco de dados. Essa é uma característica importante em ambientes de desenvolvimento, uma vez que permite que você recrie o aplicativo com um banco de dados novo, usando valores de amostra que, de qualquer forma você teria que inserir manualmente toda vez que o banco de dados fosse recriado.

Agora, utilizaremos o comando artisan para gerar uma nova classe de seeders para nossa tabela de places chamada PlacesTableSeeder:

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

O comando criará um novo arquivo chamado PlacesTableSeeder.php dentro do diretório database/seeds. Abra aquele arquivo usando seu editor de texto preferido:

  • nano database/seeds/PlacesTableSeeder.php

É assim que o arquivo PlacesTableSeeder.php, gerado automaticamente, se parece:

database/seeds/PlacesTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class PlacesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //
    }
}

Nossa nova classe de seeder contém um método vazio chamado run. Esse método será chamado quando o comando db:seed do Artisan for executado.

Precisamos editar o método run para incluir instruções para inserir dados de amostra no banco de dados. Vamos utilizar o compilador de consultas do Laravel para simplificar esse processo.

O compilador de consultas do Laravel oferece uma interface fluente para as operações de banco de dados como a inserção, atualização e recuperação de dados. Ele também introduz proteções contra ataques de injeção de SQL. O compilador de consulta é exposto pela facade do DB - um proxy estático para as classes de banco de dados subjacentes no contêiner de serviço.

Para começar, criaremos uma variável de classe estática para reter todos os locais das amostras que desejamos inserir no banco de dados como uma matriz. Isso nos permitirá usar um loop foreach para iterar em todos os valores, inserindo cada um no banco de dados utilizando o compilador de consulta.

Chamaremos essa variável de $places:

database/seeds/PlacesTableSeeder.php
<?php

use Illuminate\Database\Seeder;

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

Em seguida, precisaremos incluir uma instrução use no topo de nossa classe PlacesTableSeeder para facilitar a referência da facade DB em todo o código:

database/seeds/PlacesTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class PlacesTableSeeder extends Seeder
…

Agora, podemos iterar através dos valores da matriz $places, usando um loop foreach e inserir cada um deles em nossa tabela places com o compilador de consultas:

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

O loop foreach itera através de cada valor da matriz estática $places. Em cada iteração, usamos a facade DB para inserir uma nova linha na tabela places. Definimos o campo name para o nome do lugar que acabamos de obter da matriz $places e definimos o campo visited para um valor aleatório de 0 ou 1.

É assim que a classe PlacesTableSeeder completa se parecerá após todas as atualizações:

database/seeds/PlacesTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

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

    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        foreach (self::$places as $place) {
            DB::table('places')->insert([
                'name' => $place,
                'visited' => rand(0,1) == 1
            ]);
        }
    }
}

Salve e feche o arquivo quando terminar de fazer essas alterações.

As classes do seeder não são carregadas automaticamente no aplicativo. Precisamos editar a classe principal, DatabaseSeeder, para incluir uma chamada para o seeder que acabamos de criar.

Abra o arquivo database/seeds/DatabaseSeeder.php com o nano ou com o seu editor favorito:

  • nano database/seeds/DatabaseSeeder.php

A classe DatabaseSeeder se parece com qualquer outro seeder: ela se estende a partir da classe Seeder e tem um método run. Atualizaremos esse método para incluir uma chamada para o PlacesTableSeeder.

Atualize o método run atual, dentro da classe DatabaseSeeder, excluindo a linha comentada e substituindo-a pelo código destacado a seguir:

database/seeds/DatabaseSeeder.php
…
    public function run()
    {
        $this->call(PlacesTableSeeder::class);
    }
...

É assim que a classe DatabaseSeeder completa se parecerá após a atualização:

database/seeds/DatabaseSeeder.php
<?php

use Illuminate\Database\Seeder;

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


Salve e feche o arquivo quando terminar de atualizar seu conteúdo.

Agora, terminamos a configuração da migration e de um seeder para nossa tabela places. No próximo passo, veremos como executá-las.

Passo 6 — Executando as migrations e os seeders do banco de dados

Antes de prosseguir, precisamos garantir que seu aplicativo esteja em funcionamento. Vamos configurar a chave de criptografia do aplicativo e, depois, acessar o aplicativo a partir de um navegador para testar o servidor Web.

Para gerar a chave de criptografia que o Laravel exige, utilize o comando artisan key:generate:

  • docker-compose exec app php artisan key:generate

Assim que a chave tiver sido gerada, você poderá acessar o aplicativo, apontando seu navegador para o nome do host ou endereço IP do servidor na porta 8000:

http://server_host_or_ip:8000

Você verá uma página como esta:

Erro do MySQL

Isso significa que o aplicativo conseguiu conectar-se ao banco de dados, mas que não encontrou uma tabela chamada places. Vamos criar a tabela places agora, utilizando o comando do artisan migrate:

  • docker-compose exec app php artisan migrate

Você obterá um resultado parecido com este:

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)

Você verá que algumas outras migrations foram executadas junto com a migration create_places_table que configuramos. Essas migrations são geradas automaticamente quando o Laravel é instalado. Embora não estejamos usando essas tabelas adicionais agora, elas serão necessárias no futuro quando expandirmos o aplicativo para ele ter usuários registrados e trabalhos agendados. Por enquanto, deixe-as como estão.

Neste ponto, nossa tabela ainda está vazia. Precisamos executar o comando db:seed para propagar o banco de dados com nossas amostras de locais:

  • docker-compose exec app php artisan db:seed

Isso executará nosso seeder e irá inserir os valores da amostra que definimos dentro da nossa classe PlacesTableSeeder. Você verá um resultado semelhante a este:

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

Agora, recarregue a página do aplicativo no seu navegador. Você verá uma página parecida com esta:

Demo do aplicativo Laravel

Sempre que precisar começar do zero, você poderá remover todas as tabelas de banco de dados com:

  • docker-compose exec app php artisan db:wipe
Output
Dropped all tables successfully.

Para executar o aplicativo migrations e propagar as tabelas em um único comando, utilize:

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

Se quiser reverter uma migration, você pode executar:

  • docker-compose exec app php artisan migrate:rollback

Isso acionará o método down para cada classe de migration dentro da pasta migrations. Normalmente, ele removerá todas as tabelas que foram criadas através das classes de migration, deixando sozinha quaisquer outras tabelas que possam ter sido criadas manualmente. Você verá um resultado como este:

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)

O comando de reversão é especialmente útil quando estiver fazendo alterações nos modelos do aplicativo e um comando db:wipe não puder ser usado - por exemplo, caso vários sistemas dependerem de um mesmo banco de dados.

Conclusão

Neste guia, vimos como usar as migrations e os seeders de banco de dados para facilitar a configuração de bancos de dados para desenvolvimento e teste para um aplicativo Laravel 6.

Como um passo seguinte, talvez você queira conferir a documentação do Laravel para obter mais informações sobre como usar o compilador de consultas e como usar os Modelos eloquentes para se concentrar ainda mais no esquema do banco de dados do seu aplicativo.

Creative Commons License