Questões de precisão e consistência de dados podem levar desde pequenos inconvenientes até grandes preocupações corporativas. É fundamental construir código que armazene, altere e apague dados com segurança em seu banco de dados.

Aqui é onde entram as transações de banco de dados do Laravel.

As transações de banco de dados são uma abordagem eficaz para garantir a integridade dos dados. O Laravel simplifica essas transações em uma ampla gama de bancos de dados.

Mas o que exatamente são? Como você pode implementar as transações do banco de dados no Laravel?

Ao final deste extenso guia, você terá aprendido tudo sobre transações de banco de dados no Laravel e como usá-las de forma eficaz em seu projeto.

O que são transações de banco de dados no Laravel?

Antes de entrarmos no lado técnico das coisas, vamos primeiro entender o que são transações de banco de dados Laravel e como você pode se beneficiar com elas.

Uma transação de banco de dados é um conjunto de operações que você pode realizar com segurança dentro da estrutura do banco de dados do seu aplicativo, tais como consultas SQL para modificar dados (por exemplo, atualizações, exclusões e inserções).

A qualquer momento, você pode decidir reverter todas as consultas da transação. Além disso, qualquer consulta que você fizer será tratada como uma única ação pelo banco de dados.

Vamos analisar um exemplo disso.

Suponha que temos um aplicativo que permite aos usuários criar contas. Naturalmente, pode haver um ou muitos usuários afiliados a cada conta. Se esse aplicativo gerar simultaneamente uma conta e o primeiro usuário, você terá que lidar com o que acontece se uma conta for gerada corretamente, mas o usuário não for.

Dê uma olhada neste exemplo de código:

// Create Account
$newAcct = Account::create([
  'accountname' => Input::get('accountname'),
]);

// Create User
$newUser = User::create([
  'username' => Input::get('username'),
  'account_id' => $newAcct->id,
]);

Existem dois cenários aqui que podem causar problemas desagradáveis:

  1. A conta não é gerada
  2. Falha em criar um usuário

Vamos considerar a segunda situação.

Ter uma conta sem usuários disponíveis leva a inconsistência nos dados do banco de dados. Para resolver isso, você pode passar pelo assustador processo de codificar em torno desse problema ou economizar muito código simplesmente usando uma transação para fazer as coisas rapidamente.

 

Enquanto as transações de banco de dados estão presentes na maioria dos bancos de dados SQL, elas variam principalmente em sua implementação e eficiência. Sistemas populares como MySQL, SQLite, PostgreSQL e Oracle suportam transações, então você não deve ter problemas para implantar seu banco de dados SQL preferido.

Migrações

A migração é uma funcionalidade crucial no Laravel que permite a você construir uma tabela em seu banco de dados, fazer modificações e compartilhar o esquema do banco de dados do aplicativo. Você pode usar a migração Laravel para editar tabelas, adicionando novas colunas ou removendo as existentes.

Digamos que você está discutindo ideias com uma equipe e precisa fazer ajustes na tabela. O arquivo SQL deve ser compartilhado e importado por alguém da equipe. É possível que eles se esqueçam de importar o arquivo SQL, causando problemas no funcionamento do aplicativo.

É aí que a migração do Laravel entra em ação. Você pode adicionar uma nova coluna ao seu banco de dados ou excluir entradas sem afetar as que já estão lá.

Seeders

Os seeders são uma ferramenta fornecida pelo Laravel para os desenvolvedores facilitarem a realização de testes com diferentes tipos de dados, corrigirem bugs e otimizarem o desempenho. Você pode adicionar várias linhas de dados fictícios automaticamente à tabela do seu banco de dados através do seeder do banco de dados com um único comando.

Isso permite que você recomece com um novo banco de dados e valores de exemplo, em vez de ter que inseri-los manualmente toda vez que o banco de dados é restaurado.

Opções para transações de banco de dados no Laravel

O Laravel oferece diferentes ferramentas para gerenciar seus dados, como o Adminer. Para transações de banco de dados, existem três métodos no lado do banco de dados para iniciar manualmente uma transação e ter controle total sobre o gerenciamento da transação.

Muitos usuários acham essas opções mais flexíveis para definir exatamente quando uma transação deve ser confirmada ou desfeita:

  • Criar uma transação: Use o comando DB::beginTransaction(); para iniciar uma transação.
  • Desfazer uma transação: Use o comando DB::rollBack(); se você quiser fazer mudanças ou desfazer ações.
  • Confirmar uma transação: Se tudo correu como planejado, use o comando DB::commit();.

Lembre-se sempre de concluir cada transação aberta com uma ação de commit ou rollback, especialmente em loops. Caso contrário, esse método manual ficará fora de sincronia e seus registros não serão atualizados.

Como trabalhar com o seu banco de dados Laravel

As migrações e os seeders, como mencionado anteriormente, são soluções sofisticadas projetadas para desenvolvedores Laravel para implantar, excluir e restaurar rapidamente o banco de dados de um aplicativo, reduzindo disparidades. Isso é muito útil, especialmente quando mais de um desenvolvedor está trabalhando no mesmo aplicativo.

Esta seção irá mostrar como utilizar migrações e seeders facilmente com o banco de dados do Laravel usando comandos do artisan.

Pré-requisitos

Aqui está o que você precisa para começar:

  1. Um usuário não-root com permissões de sudo em um computador local ou servidor de desenvolvimento Ubuntu 18.04. É uma boa ideia ter um firewall ativo e configurado se você estiver usando um servidor remoto.
  2. LEMP instalado em sua máquina. Você também pode optar por instalar o Docker e o Docker Compose para executar seu aplicativo se se sentir mais confortável trabalhando com eles.

Nossa ferramenta DevKinsta é alimentada pelo Docker e usada por 60,000++ desenvolvedores e designers para desenvolver facilmente sites WordPress únicos ou múltiplos

Existem outras ferramentas de desenvolvimento web que você pode usar, dependendo de suas habilidades e necessidades de codificação.

Migrações no Laravel

Existem dois métodos em uma classe de migração: up e down. O método up é usado para criar novas tabelas, índices ou colunas em seu banco de dados. O método down deve desfazer os efeitos do método up.

Você pode usar o construtor de esquema do Laravel para construir e editar tabelas livremente em cada um desses métodos. Por exemplo, esta migração gera uma tabela de voos:

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

class CreateFlightsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/

public function up() {
  Schema::create('flights', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('name');
    $table->string('airline');
    $table->timestamps();
  });
}

/**
* Reverse the migrations.
*
* @return void
*/

public function down() {
  Schema::drop('flights');
}

Tenha em mente que os comandos make:migration precisam esclarecer o nome da tabela. Portanto, certifique-se de que o table_name corresponda ao que você quer.

Você pode usar as opções --table e --create para especificar o nome da tabela e se a migração irá criar uma nova tabela, como mostrado abaixo:

php artisan make:migration create_users_table --create=users
php artisan make:migration add_votes_to_users_table --table=users

Seu banco de dados/diretório de migrações agora incluirá a nova migração. Cada nome de arquivo de migração inclui um carimbo de data/hora, que Laravel usa para determinar a ordem de migração.

Você também tem a opção de definir um --path, que deve estar relacionado ao diretório raiz da sua instalação. Use o seguinte comando:

php artisan migrate:make foo --path=app/migrations

Executando migrações

Há alguns comandos úteis que você pode aproveitar ao executar as migrações. Vamos rever alguns deles:

  • php artisan migrate: Este comando publica todo o seu esquema no banco de dados. Ele também gera uma tabela no banco de dados.
  • php artisan migrate --path=app/foo/migrations: Este comando executa todas as migrações sob um diretório. Se você receber uma mensagem de erro “Nothing to migrate”, execute o comando php artisan migrate --path=database/migrations/foo sem o diretório do aplicativo.
  • php artisan migrate --package=vendor/package: Use este comando se você quiser executar as migrações para um pacote.

Algumas vezes você pode receber um erro de “Class not found” enquanto executa migrações. Se você conseguir, execute o comando composer dump-autoload.

Algumas migrações podem ser perigosas e podem resultar na perda dos seus dados. Portanto, Laravel irá solicitar que você confirme a execução de comandos para proteger seus dados.

Se você não quiser ser solicitado, use a flag --force flag para forçar os comandos da seguinte forma:

php artisan migrate --force

Revertendo as migrações

Utilize o comando rollback quando precisar reverter o último conjunto de migrações da seguinte forma:

php artisan migrate:rollback

Aqui estão alguns comandos adicionais de rollback que você pode usar:

  • php artisan migrate:reset: Este comando reverte todas as migrações, não apenas a última operação.
  • php artisan migrate:fresh: Use este comando quando você quiser uma nova instalação do seu banco de dados. Ele remove todas as tabelas existentes e executa o comando migration.
  • php artisan migrate:refresh: Este é um comando dois em um que executa o :rollback and migrate.
  • php artisan migrate:fresh --seed: Este executa o comando migrate:fresh antes de adicionar dados ao banco de dados através do processo de seeding. Quando você instala o aplicativo em um novo servidor, pode utilizar este comando para adicionar dados ao banco de dados.

Laravel seeding

Um seeder é uma classe que cria e insere amostras de dados (seeds) em um banco de dados. O Laravel fornece uma técnica simples para alimentar o seu banco de dados com dados de teste usando classes de seed no diretório database/seeds.

Você é livre para escolher o nome de suas classes de sementes. Mas aconselhamos que você siga um padrão claro de nomes de sua escolha, como UsersTableSeeder. Em seguida, uma classe DatabaseSeeder será criada por padrão.

Aqui está um exemplo de uma classe de seed de banco de dados no Laravel:

class DatabaseSeeder extends Seeder {
  public function run() {
    $this->call('UserTableSeeder');
    $this->command->info('User table seeded!');
  }
}
class UserTableSeeder extends Seeder {
  public function run() {
    DB::table('users')->delete();
    User::create(array('email' => '[email protected]'));
  }
}

Criando um seeder

Gerar seeders é fácil como um piscar de olhos. Você pode fazer isso de olhos fechados (mas por favor, não faça).

Execute o comando make:seeder do artisan para criar um seeder. Agora, o diretório database/seeds incluirá todos os seeders produzidos pelo framework:

php artisan make:seeder UsersTableSeeder

O método padrão de uma classe seeder é o “run”. O processo ocorre quando você aplica o comando db:seed do artisan. Você pode colocar dados no seu banco de dados da maneira que preferir usando a função “run”. Além disso, é totalmente possível usar factories de modelos Eloquent ou o Query Builder para inserir dados manualmente.

No entanto, é importante lembrar que durante o processo de seeding do banco de dados, a proteção contra atribuição em massa é desativada automaticamente.

Aqui, faremos modificações na classe básica DatabaseSeeder e adicionaremos um comando de inserção no banco de dados ao método “run”:

<?php
use IlluminateDatabaseSeeder;
use IlluminateSupportFacadesDB;
use IlluminateSupportFacadesHash;
use IlluminateSupportStr;

class DatabaseSeeder extends Seeder {
  /**
  * Run the database seeds.
  *
  * @return void
  */
  public function run() {
    DB::table('users')->insert([
      'name' => Str::random(10),
      'email' => Str::random(10).'@gmail.com',
      'password' => Hash::make('password'),
    ]);
  }
}

Se você deseja usar o (type-hint) para quaisquer dependências dentro do código do método “run”, o contêiner de serviços do Laravel irá resolvê-las automaticamente.

Além disso, você pode usar a função call para executar diferentes classes seeder a partir desta classe, permitindo que você personalize a ordem de seeding. Você pode dividir o seeding do banco de dados em arquivos diferentes, garantindo que nenhuma classe seeder se torne muito extensa.

Digite o nome da classe seeder que você deseja usar, conforme mostrado abaixo:

/**
* Run the database seeds.
*
* @return void
*/
public function run() {
  $this->call([
    UsersTableSeeder::class,
    PostsTableSeeder::class,
    CommentsTableSeeder::class,
  ]);
}

Seeders em execução

Depois de gerar seu seeder, você pode precisar usar o comando dump-autoload para recriar o autoloader do Composer:

composer dump-autoload

Em seguida, você precisa executar o comando db:seed do Artisan para preencher o seu banco de dados:

php artisan db:seed

Este comando executa a classe DatabaseSeeder por proxy, que pode ser usada para executar outras classes de seeders. No entanto, você também pode usar o parâmetro --class para executar separadamente uma classe de seeder específica, da seguinte forma:

php artisan db:seed --class=UserTableSeeder

E se você quiser recriar seu banco de dados do zero, incluindo a remoção de todas as tabelas e a execução de todas as suas migrações mais uma vez? Neste caso, use o comando migrate:fresh.

php artisan migrate:fresh --seed

Assim como acontece com as migrações, alguns processos de seeding podem levar à perda de dados ou alterações indesejadas. Por essa razão, você será solicitado a confirmar antes que os seeders sejam executados, para protegê-lo de executar comandos de seeding em seu banco de dados principal.

Se você está confiante o suficiente e não quer ser interrompido por essa etapa de segurança, use a opção --force abaixo:

php artisan db:seed --force

5 outras formas de usar consultas de banco de dados raw no Laravel

Embora o Laravel forneça ferramentas úteis como Eloquent e Query Builder, ainda é possível executar consultas de SQL usando queries raw. Reunimos cinco maneiras diferentes de fazer isso.

Mas antes de começar, é importante saber que consultas raw não são automaticamente seguras, tornando uma abordagem arriscada. Portanto, se você estiver fornecendo quaisquer parâmetros para a consulta, certifique-se de que eles estejam no formato correto e tenham os valores corretos, como um número em vez de um texto.

Cálculos de Média/Soma/Contagem (Avg/Sum/Count)

Você pode usar uma consulta raw se quiser criar um GROUP BY (), e em seguida, utilize funções agregadas do MySQL como Count(), SUM(), AVG(), MIN(), ou MAX() como mostrado no exemplo a seguir:

$users = DB::table('users')
  ->selectRaw('count(*) as user_count, status')
  ->where('status', '<>', 1)
  ->groupBy('status')
  ->get();

É possível até mesmo fazer count() quanto avg() na mesma consulta SQL:

$salaries = DB::table('salaries')
  ->selectRaw('companies.name as company_name, avg(salary) as avg_salary, count(*) as people_count')
  ->join('companies', 'salaries.company_id', '=', 'companies.id')
  ->groupBy('companies.id')
  ->orderByDesc('avg_salary')
  ->get();

Filtrando por anos

Caso você precise realizar cálculos SQL em GROUP BY ou ORDER BY, você pode usar as consultas em groupByRaw() e orderByRaw(). Após o agrupamento, você também pode utilizar a instrução where usando uma consulta SQL having com havingRaw ().

Por exemplo, este comando abaixo mostra como agrupar um campo de data/hora por ano:

$results = User::selectRaw('YEAR(birth_date) as year, COUNT(id) as amount')
  ->groupByRaw('YEAR(birth_date)')
  ->havingRaw('YEAR(birth_date) > 2000')
  ->orderByRaw('YEAR(birth_date)')
  ->get();

Calculando um campo único (Sub-Query)

Suponha que você queira calcular um campo a partir de outro e retornar o resultado em uma consulta SQL. Como você pode fazer isso?

Vamos dar uma olhada:

$products = Product::select('id', 'name')
  ->selectRaw('price - discount_price AS discount')
  ->get();

Aqui está outro exemplo de uma instrução SQL CASE:

$users = DB::table('users')
  ->select('name', 'surname')
  ->selectRaw("(CASE WHEN (gender = 1) THEN 'M' ELSE 'F' END) as gender_text")
  ->get();

Convertendo SQL mais antigo

É comum ter uma instrução SQL que precisa ser convertida para Eloquent ou Query Builder, especialmente de um projeto antigo em que você trabalhou.

Bem, você realmente não precisa fazer isso. Ao invés disso, você pode simplesmente usar a declaração DB::select(), como mostrado:

$results = DB::select('select * from users where id=?', [1]);

Executando consulta sem resultados

DB::statement pode executar uma consulta SQL, não obtendo resultados como INSERT ou UPDATE sem variáveis.

Isso é frequentemente usado na migração de banco de dados quando uma estrutura de tabela muda e os dados antigos devem ser alterados com a nova estrutura:

DB::statement('UPDATE users SET role_id = 1 WHERE role_id IS NULL AND YEAR(created_at) > 2020');

Além disso, o DB::statement() pode executar qualquer consulta SQL com esquema que não está limitado a valores ou colunas. Veja um exemplo:

DB::statement('DROP TABLE users');
DB::statement('ALTER TABLE projects AUTO_INCREMENT=123');

Resumo

Agora você deve ter um bom entendimento das transações de banco de dados no Laravel e como implementá-las. Essas transações não apenas ajudam na integridade dos dados, mas também otimizam o desempenho do Laravel e tornam o processo de desenvolvimento mais fácil.