Introdução ao Laravel Framework – Parte 08: CRUD
✌😎 Saudações, devs! Vamos continuar nossa jornada pelo mundo Laravel.
👉 Finalmente chegou a hora de criar um CRUD, explorando todos os conceitos mostrados até aqui.
📌 Se você não possui conhecimentos básicos em Laravel, sugiro que você leia os posts anteriores para um maior aproveitamento deste tutorial (Clique aqui!)
🚀 Bora!
Pré-requisitos
🎯 Para esse tutorial, consideraremos que as ferramentas necessárias para o desenvolvimento Laravel estão devidamente instaladas e configuradas em seu computador.
📌 Caso seja necessário, visite o segundo post desta série e configure o ambiente de maneira adequada.
01 – Criando o projeto
Abra o terminal (prompt de comando ou PowerShell) na pasta que você normalmente armazena os seus projetos e digite o seguinte comando:
laravel new laravel-crud
Espere o projeto ser criado, navegue até a pasta criada e digite o seguinte comando para executá-lo:
php artisan serve
Agora que aplicação está em execução localmente: http://127.0.0.1:8000/, abra a pasta do projeto no Visual Studio Code ou em outro editor de código de sua preferência.
Let’s code! 🚀
02 – Configurando o banco de dados
Para configurar o banco de dados, abra o arquivo .env na raiz do projeto e confirme as configurações do banco de dados:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_crud
DB_USERNAME=root
DB_PASSWORD=
A chave DB_DATABASE possui o nome do banco de dados que você deve criar. Note que neste exemplo, o nome do banco de dados está configurado com o mesmo nome do projeto. Isso é opcional, ou seja, você pode usar o nome que achar mais adequado.
Caso você esteja utilizando o PostgreSQL, a chave DB_CONNECTION deve ser alterada para pgsql. Preste atenção também na porta em que o banco está “escutando”. Para mais informações sobre conexões com outros bancos de dados, consulte o arquivo config\database.php e a documentação oficial: https://laravel.com/docs/9.x/database
Inicie o serviço do Banco de Dados e crie um novo schema segundo as configurações do arquivo .env.
03 – Criando models e migrations
As migrations são responsáveis por criar e gerenciar o versionamento do schema do banco de dados conforme o projeto vai avançando. Por sua vez, objetos model representam uma ou mais linhas de uma tabela do banco de dados em memória. Objetos model também encapsulam a lógica de manipulação de dados segundo o padrão de projeto Active Record.
Dúvidas? Clique aqui para saber mais sobre models e migrations.
A ideia é criar uma tabela e um model que cadastra veículos. Para isso, digite o seguinte comando:
php artisan make:model Vehicle -m
Note que o nome das models são sempre em maiúsculo. O parâmetro -m no comando indica que será criada uma migration associada ao model criado.
Após a execução do comando foram criados 2 arquivos:
- app\Models\Vehiclephp: a classe model responsável pelas operações de CRUD;
- database\migrations\2022_08_23_112655_create_vehicles_table.php: a migration que contém o código para a criação da tabela vehicles no banco de dados.
Note que o nome da migration sempre deve possuir um prefixo com uma timestamp (etiqueta de tempo) que marca a data e a hora de sua criação. Esse detalhe é importantíssimo, pois as migrations devem ser executadas na ordem correta, a fim de construir/alterar o schema do banco de dados.
Um outro detalhe importante é que o nome da tabela criada sempre será o nome da model, em minúsculo e no plural.
Assim, o código da model deve ficar como está. Por sua vez, a migration deve possuir o seguinte código:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//..o nome da tabela é o nome do model, em minúsculo e no plural.
Schema::create('vehicles', function (Blueprint $table) {
$table->id();
$table->string("name", 50);
$table->integer("year");
$table->string("color", 30);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('vehicles');
}
};
Com as classes migrations criadas, vamos executá-la usando o seguinte comando:
php artisan migrate
Esse comando executa todas as migrations, as pré-definidas no Laravel e a que acabamos de criar. No banco de dados é possível verificar que foram criadas várias tabelas.
As tabelas migrations e failed_jobs são para controle das migrations. Vamos nos concentrar na tabela vehicles.
Se desejar testar operações de CRUD usando o Tinker, veja esse post!
É recomendável inserir alguns dados fictícios para fins de teste.
04 – Criando o controlador e rotas
Agora que criamos a estrutura necessária para manipular os dados da aplicação, vamos definir o controlador e as rotas para as operações de CRUD.
Digite o seguinte comando no terminal:
php artisan make:controller VehicleController --resource
Note que o parâmetro –resource é usado para a criação de um controller do tipo resource (recurso), ou seja, ele irá criar o corpo dos métodos necessários para que possamos codificar os métodos CRUD.
👉 ATENÇÃO! Em aplicações Web, principalmente as que aplicam o conceito de serviços (web-services), um RECURSO (resource) é a representação de uma entidade de dados. Neste caso, o recurso é um objeto do tipo VehicleModel.
📌 No Laravel Framework, os controllers ficam na pasta App\Http\Controllers. Abra o arquivo VehicleController.php e veja os métodos que foram criados:
- index(): mostra a página inicial do recurso, normalmente uma listagem de recursos cadastrados;
- create(): mostra a página que contém o formulário para a criação de um novo recurso;
- store(Request $request): persiste um novo recurso na base de dados. O parâmetro de entrada é um objeto Request, contendo os dados vindos do cliente;
- show($id): mostra um recurso mediante o $id informado;
- edit($id): mostra a página que contém o formulário para edição de um recurso recuperado mediante o parâmetro $id;
- update(Request $request, $id): atualiza um recurso informado mediante o parâmetro $id com os dados vindos do cliente, presentes no objeto Request;
- destroy($id): exclui um recurso da base dados, mediante o parâmetro $id.
Convém destacar que esses métodos receberão dados vindos do cliente, direcionados por meio do arquivo de rotas (routes/web.php). Assim, o próximo passo é associar as rotas padrão aos respectivos métodos do controlador resource.
Abra o arquivo routes/web.php e adicione o seguinte código:
Route::resource("/vehicles", VehicleController::class);
O método estático resource da classe Route recebe dois parâmetros: o endpoint das requisições, aqui definido por “/vehicles” e o controlador que irá tratar as requisições, neste caso, VehicleController.
Assim foram criadas as rotas HTTP para cada um dos métodos do controlador. Para ver as rotas criadas, digite o seguinte comando no terminal:
php artisan route:list
Observe as rotas criadas. Algumas delas já estavam predefinidas pelo framework.
Pronto! Agora vamos implementar os métodos do controlador que persistem e recuperam dados no banco de dados.
05 – Implementando os métodos do controlador
Antes de criar as views podemos implementar os métodos do controlador, considerando os dados que foram definidos na base de dados e os dados vindos dos formulários (requisições).
Lembre-se que o controlador resource já definiu os métodos específicos para cada operação de CRUD.
Assim, vamos implementar cada um dos métodos definidos, mesmo que as views ainda não foram criadas.
5.1 – Método index()
O método index retorna a view que exibe a página que lista os recursos cadastrados, neste caso, veículos. Veja:
public function index()
{
//..recuperando os veículos do banco de dados
$vehicles = Vehicle::all();
//..retorna a view index passando a variável $vehicles
return view('vehicles.index')->with('vehicles', $vehicles);
}
5.2 – Método create()
O método create() retorna a página que contém a view de cadastro de veículo. Veja:
public function create()
{
//..mostrando o formulário de cadastro
return view('vehicles.create');
}
5.3 – Método store(Request $request)
O método store recebe como parâmetro de entrada um objeto Request contendo os dados da requisição e os persiste na base de dados.
Quando a requisição vem de um formulário precisamos acessá-los usando o método input. Veja o código a seguir:
public function store(Request $request)
{
//..instancia um novo model Vehicle
$vehicle = new Vehicle();
//..pega os dados vindos do form e seta no model
$vehicle->name = $request->input('name');
$vehicle->year = $request->input('year');
$vehicle->color = $request->input('color');
//..persiste o model na base de dados
$vehicle->save();
//..retorna a view com uma variável msg que será tratada na própria view
$vehicles = Vehicle::all();
return view('vehicles.index')->with('vehicles', $vehicles)
->with('msg', 'Veículo cadastrado com sucesso!');
}
5.4 – Método show($id)
O método show mostra um recurso cadastrado, não permitindo sua edição. Note que o parâmetro $id é usado para recuperar o recurso em questão. Veja o código a seguir:
public function show($id)
{
//..recupera o veículo da base de dados
$vehicle = Vehicle::find($id);
//..se encontrar o veículo, retorna a view com o objeto correspondente
if ($vehicle) {
return view('vehicles.show')->with('vehicle', $vehicle);
} else {
//..senão, retorna a view com uma mensagem que será exibida.
return view('vehicles.show')->with('msg', 'Veículo não encontrado!');
}
}
5.5 – Método edit($id)
O método edit é bastante semelhante ao método show. A diferença é que será exibido uma view que permite a edição do recurso. Veja o código a seguir:
public function edit($id)
{
//..recupera o veículo da base de dados
$vehicle = Vehicle::find($id);
//..se encontrar o veículo, retorna a view de ediçãcom com o objeto correspondente
if ($vehicle) {
return view('vehicles.edit')->with('vehicle', $vehicle);
} else {
//..senão, retorna a view de edição com uma mensagem que será exibida.
$vehicles = Vehicle::all();
return view('vehicles.index')->with('vehicles', $vehicles)
->with('msg', 'Veículo não encontrado!');
}
}
5.6 – Método update(Request $request, $id)
O método update(…) recebe dois parâmetros de entrada: O objeto Request com os dados do objeto e o id do objeto a ser atualizado.
Logo, o seu algoritmo consiste em recuperar o objeto Vehicle usando o id, atualizá-lo com os dados do objeto Request e, finalmente, persistir os dados no banco de dados. Veja o código:
public function update(Request $request, $id)
{
//..recupera o veículo mediante o id
$vehicle = Vehicle::find($id);
//..atualiza os atributos do objeto recuperado com os dados do objeto Request
$vehicle->name = $request->input('name');
$vehicle->year = $request->input('year');
$vehicle->color = $request->input('color');
//..persite as alterações na base de dados
$vehicle->save();
//..retorna a view index com uma mensagem
$vehicles = Vehicle::all();
return view('vehicles.index')->with('vehicles', $vehicles)
->with('msg', 'Veículo atualizado com sucesso!');
}
5.7 – Método destroy($id)
O método destroy($id) exclui um objeto da base de dados mediante o id. Convém ressaltar que operações de exclusão de dados não são comuns, considerando bancos de dados relacionais. Isso se dá ao fato de que a exclusão de registros relacionados envolve exclusão em outras tabelas, pois há o conceito de integridade referencial.
O algoritmo do método consiste em recuperar o veículo a ser excluído e invocar o método delete. Veja o código a seguir:
public function destroy($id)
{
//..recupeara o recurso a ser excluído
$vehicle = Vehicle::find($id);
//..exclui o recurso
$vehicle->delete();
//..retorna à view index.
$vehicles = Vehicle::all();
return view('vehicles.index')->with('vehicles', $vehicles)
->with('msg', "Veículo excluído com sucesso!");
}
Agora que os métodos do controlador foram devidamente codificados, vamos criar as views!
06 – Criando as views
As views são as interfaces gráficas do usuário (GUI – Graphical User Interface), responsáveis por permitir aos usuários usarem os recursos do sistema.
No Laravel Framework as views são construídas mediante um motor de template chamado Blade (Veja esse post sobre views).
Uma boa prática é criar uma view base, contendo o layout “repetível” do site/sistema e reaproveitá-la mediante herança. O Blade permite isso!
Antes de criar as views, crie o arquivo public\css\app.css contendo o seguinte código:
*{
margin: 0;
padding: 0;
}
div.container{
width: 1200px;
margin: 0 auto;
}
div.container > header {
background-color: darkblue;
color: white;
padding: 10px;
text-align: center;
}
div.container > nav {
background-color: lightblue;
}
div.container > nav li {
display: inline-block;
line-height: 2rem;
font-family: Arial, Helvetica, sans-serif;
padding: 10px;
}
div.container > nav a:link,
div.container > nav a:active,
div.container > nav a:visited {
text-decoration: none;
color: darkblue;
padding: 1rem;
}
div.container > nav li:hover,
div.container > nav a:hover {
background-color: blue;
color: white;
}
div.content{
min-height: 400px;
background-color: beige;
padding: 2rem;
}
div.content h2 {
border-bottom: medium darkblue solid;
font-family: Arial;
color: darkblue;
margin-bottom: 3rem;
}
div.content p{
line-height: 1.5rem;
}
div.container > footer{
background-color: lightblue;
padding: 2rem;
display: grid;
grid-template-columns: 1fr 1fr;
text-align: center;
}
table.data-table{
border-spacing: 0;
}
table.data-table > thead{
background-color: darkblue;
color: white;
}
table.data-table td, th {
border: solid thin black;
padding: 5px;
}
table.data-table tfoot{
background-color: lightblue;
}
table.data-table > tbody > tr:hover{
background-color: lightcyan;
cursor: pointer;
}
form.form label {
display: block;
padding-bottom: 0.3rem;
}
form.form input[type=text], form.form input[type=number] {
display: block;
margin-bottom: 0.5rem;
font-size: 1.3rem;
}
form.form input[type=submit], form.form input[type=reset], button{
padding: 0.5rem;
cursor: pointer;
}
6.1 – Criando a view “base”
As views ficam na pasta resources/views. Por padrão, já temos a view welcome, que é a página de boas vindas do Laravel.
Note que as views possuem a extensão .blade.php – Sim, as views devem todas ter essa extensão!
Na pasta resources/views, crie um arquivo chamado base.blade.php. Veja o código a seguir:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
{{-- Pegando as variáveis de ambiente --}}
<title>{{ env('APP_NAME') }}</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
<div class="container">
<header>
<h1>Laravel - CRUD</h1>
</header>
<nav>
<ul>
{{-- Links para o cadastro --}}
<li><a href="/vehicles">Início</a></li>
<li><a href="/vehicles/create">Cadastro de Veículos</a></li>
</ul>
</nav>
<div class="content">
{{-- o conteúdo da view específica será injetado aqui! --}}
@yield('content')
</div>
<footer>
<div>
<p>Aprendendo Laravel Framework</p>
<p><a href="http://www.laravel.com.br" target="_blank">Laravel Site</a></p>
</div>
<div>
<p>Prof. Me. Jorge Luís Gregório</p>
<p><a href="https://www.jlgregorio.com.br" target="_blank">Meu Site Oficial</a></p>
</div>
</footer>
</div>
</body>
</html>
Lembrando que o código @yield(‘content’) cria uma área de código “injetável”, ou seja, o conteúdo específico de cada view será inserido ali.
6.2 – Criando a view “index”
A view index é a que normalmente mostra a listagem dos itens que já estão cadastrados. Nesse caso, veículos.
Vamos criar essa view herdando a view base. Note que também codificamos para verificar se há uma variável com a coleção de veículos para ser mostrada. Se essa coleção existir, será mostrada uma tabela com os dados, senão, será mostrado um elemento h3 informando que não há itens cadastrados. Veja:
{{-- herda a view 'base' --}}
@extends('base')
{{-- cria a seção content, definida na base, para injetar o código --}}
@section('content')
<h2>Veículos Cadastrados</h2>
{{-- se a variável $vehicles não existir, mostra um h3 com uma mensagem --}}
@if (!isset($vehicles))
<h3 style="color: red">Nenhum Registro Encontrado!</h3>
{{-- senão, monta a tabela com o dados --}}
@else
<table class="data-table">
<thead>
<tr>
<th>Nome</th>
<th>Ano</th>
<th>Cor</th>
<th colspan="2">Opções</th>
</tr>
</thead>
<tbody>
{{-- itera sobre a coleção de veículos --}}
@foreach ($vehicles as $v)
<tr>
<td>{{ $v->name }} </td>
<td> {{ $v->year }} </td>
<td> {{ $v->color }} </td>
{{-- vai para a rota show, passando o id como parâmetro --}}
<td> <a href="{{ route('vehicles.show', $v->id) }}">Exibir</a> </td>
<td> <a href="{{ route('vehicles.edit', $v->id) }}">Editar</a> </td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
{{-- mostra a qtde de veículos cadastrados. --}}
<td colspan="5">Veículos Cadastrados: {{ $vehicles->count() }}</td>
</tr>
</tfoot>
</table>
@endif
@if(isset($msg))
<script>
alert("{{$msg}}");
</script>
@endif
@endsection
Após inserir alguns dados diretamente via SQL e acessar a URL http://127.0.0.1:8000/vehicles teremos:
6.3 – Criando a view de cadastro (create)
Crie arquivo resources\views\vehicles\create.blade.php e considere o seguinte código comentado:
{{-- herda a view 'base' --}}
@extends('base')
{{-- cria a seção content, definida na base, para injetar o código --}}
@section('content')
<h2>Cadastrar Novo Veículo</h2>
{{-- o atributo action aponta para a rota que está direcionada ao método store do controlador --}}
<form class="form" method="POST" action="{{ route('vehicles.store') }}">
{{-- CSRF é um token de segurança para validar o formulário --}}
@csrf
<label for="Nome">Nome:</label>
<input type="text" name="name" id="name" required>
<label for="Nome">Ano:</label>
<input type="number" name="year" id="year" required>
<label for="Nome">Cor:</label>
<input type="text" name="color" id="color" required>
<input type="submit" value="Salvar">
<input type="reset" value="Limpar">
</form>
@endsection
Ao clicar no link “Cadastro de Veículos”, a aparência deve ser essa:
6.4 – Criando a view show
A view show é apenas para mostrar um recurso. Normalmente é usada nos sistemas para permitir a visualização rápida das informações.
Crie o arquivo resources\views\vehicles\show.blade.php e adicione o seguinte código:
{{-- herda a view base --}}
@extends('base')
{{-- define o conteúdo --}}
@section('content')
{{-- caso exista a variável msg, exibe uma mensagem --}}
@if (isset($msg))
<h3 style="color: red">Veículo não encontrado!</h3>
@else
{{-- senão, mostra os daddos --}}
<h2>Mostrando dados do veículo</h2>
<p><strong>Nome:</strong> {{ $vehicle->name }} </p>
<p><strong>Ano:</strong> {{ $vehicle->year }} </p>
<p><strong>Cor:</strong> {{ $vehicle->color }} </p>
<a href="{{ route('vehicles.index') }}">Voltar</a>
@endif
@endsection
Para testar, navegue até a página index e clique no link “exibir” de um dos registros da tabela.
Repare na URL acessada. Veja:
6.5 – Criando a view edit
A view edit é usada para atualizar um recurso. Neste exemplo ela também é a responsável por excluir um recurso.
Em termos de estrutura ela é parecida com a view create, mas ela possui 2 formulários: um para edição e um para exclusão. Note que os botões de comando estão fora dos formulários, sendo vinculados ao seu respectivo form por meio do atributo form.
Crie o arquivo resources\views\vehicles\edit.blade.php e adicione o seguinte código:
{{-- herda a view 'base' --}}
@extends('base')
{{-- cria a seção content, definida na base, para injetar o código --}}
@section('content')
<h2>Atualizar um Veículo</h2>
{{-- o atributo action aponta para a rota que está direcionada ao método store do controlador --}}
<form class="form" id="update-form" method="POST" action="{{ route('vehicles.update', $vehicle->id) }}">
{{-- CSRF é um token de segurança para validar o formulário --}}
@csrf
{{-- o método de atualização é o PUT --}}
@method('PUT')
<label for="Nome">Nome:</label>
<input type="text" name="name" id="name" required value="{{ $vehicle->name }}">
<label for="Nome">Ano:</label>
<input type="number" name="year" id="year" required value="{{ $vehicle->year }}">
<label for="Nome">Cor:</label>
<input type="text" name="color" id="color" required value="{{ $vehicle->color }}">
</form>
{{-- note que os botões estão fora dos forms. O atributo form indica qual form o botão pertence --}}
<button form="update-form" type="submit">Salvar</button>
<button form="delete-form" type="submit" value="Excluir" >Excluir</button>
{{-- form para exclusão --}}
<form method="POST" class="form" id="delete-form" action="{{ route('vehicles.destroy', $vehicle->id) }}">
@csrf
{{-- o método HTTP para exclusão deve ser o DELETE --}}
@method('DELETE')
</form>
@endsection
Ao clicar no link editar da lista de veículos cadastrados, temos:
Conclusão
A estrutura de uma aplicação Laravel é pensada para prover escalabilidade, manutenabilidade e, principalmente, reutilização de código.
O padrão arquitetural MVC não impede que outras camadas sejam adicionadas à arquitetura da aplicação, como por exemplo a camada Service, o padrão DTO (Data Transfer Object), entre outros.
De fato, o desenvolvimento apoiado por frameworks é a melhor abordagem, considerando aspectos de arquitetura, padronização e produtividade.
Nos próximos posts da série Laravel vamos explorar mais recursos como relacionamentos em bancos de dados, autenticação, middlewares e diretivas personalizadas do Blade.
Até breve! ✌😎