O Laravel 4, assim como qualquer programa de computador, só é capaz de lembrar o que o usuário fez enquanto está sendo executado. Por isso precisamos de uma forma de persistir dados inseridos por nossos usuários. Para isso podem ser utilizados arquivos texto ou um sistema de banco de dados. Entre as diversas opções uma das mais comuns é o MySQL. Neste artigo vou mostrar o básico sobre a interação do Laravel com o MySQL.
Para ser bem objetivo e fácil de entender, vamos construir um CMS (Content Management System – Sistema de Gestão de Conteúdo). Neste sistema vamos ser capazes de cadastrar posts e os usuários serão capazes de comentar estes posts. No artigo de hoje vou apenas explicar a parte do banco de dados, criando a tabela e o modelo dos artigos, sem relacionamentos, rotas, controller ou design. Estas coisas ficarão para outros artigos.
Se você não acompanhou o último artigo da série, você pode baixar o código que vamos utilizar no Github. Lembre-se de fazer o composer install para obter as dependências (mais informações).
Migrações
Vamos começar falando sobre as migrações. Quando trabalhamos com bancos de dados precisamos definir a estrutura do nosso banco de dados através de scripts SQL. Podemos criar estes scripts através de ferramentas como o MySQL Workbench e depois compartilhar com nossos colegas de equipe. Isso cria um problema que é organizar a ordem com que estes scripts devem ser executados. A cada nova instalação do aplicativo, todos os scripts tem que ser executados corretamente.
Para solucionar este problema, foi criado o conceito de migrações. Basicamente as migrações são arquivos PHP que executam os scrips de banco corretamente. No caso do Laravel, cada migração recebe a data/hora do sistema, garantindo assim a ordem dos scripts.
Antes de criarmos nossa migração, você deve instalar a tabela de migrações do Laravel. Esta tabela vai controlar qual migração foi executada. O nome da tabela você pode definir em app/config/database.php na chave migrations.
1 2 3 4 5 6 7 8 9 10 11 12 |
/* |-------------------------------------------------------------------------- | Migration Repository Table |-------------------------------------------------------------------------- | | This table keeps track of all the migrations that have already run for | your application. Using this information, we can determine which of | the migrations on disk haven't actually been run in the database. | */ 'migrations' => 'migrations', |
Nós vamos manter esta tabela mesmo. Para instalar a tabela execute o comando:
1 |
php artisan migrate:install |
Criando uma Migration
Para criar nossa primeira “migration”, vamos usar o artisan. Se você olhar na raiz do nosso projeto, vai encontrar este arquivo. Ele é uma aplicação PHP que executa diversas funções, entre elas criar as migrações.
1 |
php artisan migrate:make criar_artigos |
Este comando vai criar um arquivo de migração no diretório app/database/migrations com o nome 2014_03_09_212855_criar_artigos.php (depende da data/hora que você executar o comando). Abrindo o arquivo, você verá o seguinte conteúdo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CriarArtigos extends Migration { /** * Run the migrations. * * @return void */ public function up() { // } /** * Reverse the migrations. * * @return void */ public function down() { // } } |
Existem dois métodos nesta classe: up() e down().
- O método up() será executado quando você fizer esta migração (comando php artisan migrate);
- O método down() será executado quando você desfizer esta migração (comando php artisan migrate:rollback)
Você também pode usar php artisan migrate:reset para dar um rollback em todas as migrações e php artisan migrate:refresh para dar um rollback e executar todas as migrações novamente. Agora vamos finalizar nossa migração utilizando o Schema Builder.
Schema Builder
Além disso o Laravel possui um Schema Builder (construtor de esquema), que permite definir as tabelas e campos do banco de dados através de um código limpo e claro, utilizando métodos PHP. Primeiro vamos definir como seria a nossa tabela.
Vamos chamar a tabela de artigos, seguindo a nomenclatura que já usamos ao criar a migração. Os campos que precisamos são:
- id: chave para identificar o artigo
- titulo: string de título
- conteudo: texto do artigo
- created_at: data/hora de criação
- updated_at: data/hora de atualização
Para este post é apenas isso, mas não se preocupe que mais adiante vamos aprimorar. Agora vamos ver como ficará nosso arquivo de migração 2014_03_09_212855_criar_artigos.php. Depois do código vou explicar cada parte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CriarArtigos extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('artigos', function($table) { $table->increments('id'); $table->string('titulo', 100); $table->text('conteudo'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('artigos'); } } |
No método up() estamos criando nossa tabela. Utilizamos o método Schema::create para informar o Laravel que vamos criar a tabela artigos. Como o segundo parâmetro desta função, passamos uma função com o seguintes comandos:
- $table->increments(‘id’): este comando define um campo id do tipo inteiro com o auto-increment definido, que também será nossa chave primária. O Laravel faz tudo isto apenas neste comando.
- $table->string(‘titulo’, 100): agora definimos um campo string (no caso do MySQL será um VARCHAR) com tamanho de 100 caracteres.
- $table->text(‘conteudo’): campo texto para colocarmos o conteúdo do nosso artigo.
- $table->timestamps(): este comando cria dois campos data/hora, created_at e updated_at.
Como você deve ter imaginado, existem diversos métodos para criar campos, definir índices, modificar a estrutura da tabela, etc. Eu não vou reproduzir todos os comandos aqui, então você pode se aprofundar vendo a documentação oficial aqui. Ao longo desta série de artigos vamos usar outros métodos e vou tentar explicar sempre que for relevante.
No método down() temos apenas uma linha. Com o método Schema::drop, vamos remover a tabela. Portanto se fizermos a migração vamos criar a tabela. Se quisermos voltar atrás, vamos remover a mesma tabela. Finalizando esta parte, execute o comando:
1 |
php artisan migrate |
Você verá a seguinte mensagem de sucesso:
1 2 |
Migration table created successfully. Migrated: 2014_03_09_212855_criar_artigos |
Se você conectar ao banco usando um aplicativo como o MySQL Workbench, verá que a tabela foi criada de acordo com a imagem abaixo:
Eloquent ORM
Agora que temos nossa tabela criada, vamos ao nosso modelo. Para isto vamos utilizar o Eloquent ORM. ORM significa Object Relational Model, que em português significa Modelo Objeto Relacional. Basicamente é um mapeamento entre o registro no banco de dados relacional e o objeto definido através de uma classe.
Vou dar um exemplo para explicar melhor. Se você tem no seu código uma classe desse tipo:
1 2 3 4 5 6 7 |
<?php class Pessoa { public $nome; public $telefone; } |
Você consegue instanciar este objeto e trabalhar com ele em memória, criando um novo objeto pessoa ou alterando um objeto existente. Mas como você salva isso no banco? Normalmente você veria as pessoas escreverem no PHP uma string com uma query para ser executado no banco:
1 2 3 4 |
<?php $strSQL = "INSERT INTO pessoa(nome,telefone) VALUES('$nome','$telefone')"; mysql_query($strSQL) or die(mysql_error()); |
Não seria melhor termos um método save(), que persistisse os dados no banco para nós? Você pode criar um método assim diretamente na classe. Mas seria necessário replicar o código para todas as tabelas do seu banco de dados. Neste momento que entra o Eloquent ORM. O Eloquent é uma implementação do pattern Active Record. Basicamente é uma classe que possui diversos métodos para interação com o banco, sendo necessário apenas estender a classe Eloquent.
1 2 3 4 5 6 |
<?php class Artigo extends Eloquent { } |
Pronto, este é o nosso model para a tabela artigos. É só isso mesmo. Isso já vai nos permitir trabalhar com nosso banco de dados. Salve este arquivo no diretório app/models/Artigo.php.
É claro que existem uma série de coisas que podem ser necessárias dependendo do caso. Por exemplo, como o Eloquent sabe qual o nome da nossa tabela se não está definido em nenhum lugar? Simplesmente adotando alguns padrões. O nome da tabela será o nome da classe, mais um ‘S’. No nosso caso artigos. Se você precisa definir um nome diferente adicione o parâmetro:
1 2 3 4 5 6 |
<?php class Outro extends Eloquent { protected $table = 'outros_artigos'; } |
Se você não criou campos created_at e updated_at na sua tabela, deve adicionar mais um parâmetro: protected $timestamps = false;. Desta maneira o Laravel saberá que não deve atualizar estes campos. Nós não precisamos disso pois utilizamos estes campos.
Testando
Nesta última parte do artigo, vamos criar o código necessário para visualizar os posts cadastrados e criar registros. Vamos fazer diretamente nas rotas. As rotas são a primeira coisa que o Laravel vai procurar quando um usuário acessar nosso sistema. Basicamente é a descrição do que vai acontecer quando o usuário acessar uma URL. Essas rotas são definidas no arquivo app/routes.php. Atualmente temos a seguinte rota definida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to | and give it the Closure to execute when that URI is requested. | */ Route::get('/', function() { return View::make('hello'); }); |
Para testar nosso model e a interação com o banco de dados vamos trabalhar diretamente neste arquivo (não faça isso em um aplicativo real!). Adicione as seguintes rotas no final do arquivo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Listar todos os artigos Route::get('/artigos/', function() { $artigos = Artigo::get(); return View::make('artigos', compact('artigos')); }); // Criar um novo artigo Route::get('/artigos/inserir', function() { return View::make('artigos_inserir'); }); Route::post('/artigos/inserir', function() { $artigo = new Artigo(); $artigo->titulo = Input::get('titulo'); $artigo->conteudo = Input::get('conteudo'); $artigo->save(); return Redirect::to('/artigos/'); }); |
Na primeira rota estamos buscando todos os acessos feitos na URL /artigos/, buscando os artigos utilizando nosso model Artigo e o método get(), e enviando todos os artigos encontrados para a view artigos. Esta view deve ser ciada dentro de app/views/artigos.php com o conteúdo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Lista de Artigos</title> </head> <body> <h1>Artigos</h1> <?php if($artigos->count()) : ?> <?php foreach ($artigos as $artigo) : ?> <h2><?php echo $artigo->titulo; ?></h2> <p><?php echo $artigo->conteudo; ?></p> <?php endforeach; ?> <?php else : ?> <h2>Nenhum artigo encontrado.</h2> <?php endif; ?> </body> </html> |
Se você acessar a URL http://desenvolvendo_com_laravel.localhost/artigos (substitua pelo endereço que você usou), você verá o título Artigos, com a mensagem Nenhum artigo encontrado.
Continuando nas outras duas rotas, uma delas é utilizada quando acontece o GET da URL /artigos/inserir/. Neste caso será apenas exibido a view artigos_inserir.php, que contém o form de inserção:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Inserir artigo</title> </head> <body> <h1>Inserir artigo</h1> <form method="post"> <div> <input name="titulo" type="text" placeholder="Título"> </div> <div> <textarea name="conteudo"></textarea> </div> <input type="submit" value="Salvar"> </form> </body> </html> |
Quando você acessar o endereço http://<seu-localhost>/artigos/inserir, você verá o form de inserção como aparece a seguir:
Preenchendo estes campos e clicando em salvar, executará um POST no mesmo endereço. Sabe qual rota será executada? Será a rota Route::post(‘/artigos/inserir’. É exatamente a mesma URL, a diferença está que uma rota funciona com o método get(), atendendo pedidos GET, e a outra usa o método post(), atendendo pedidos POST.
Na rota do POST vamos instanciar um novo objeto do nosso modelo Artigo. Depois vamos atribuir os valores enviados do nosso form. O legal é que o nosso model não tem nenhuma propriedade, mesmo assim conseguimos acessar os campos do banco como se fossem atributos do nosso objeto, sem complicação. Logo depois salvamos o registro no banco de dados com o comando $artigo->save(). Depois do save redirecionamos a página para a lista de artigos, e você poderá ver que agora existe um registro salvo. Executando algumas vezes você terá algo assim:
Conclusão
Neste artigo vimos como criar uma migração e interagir com o banco de dados através do Eloquent ORM. Eu conto com a paciência de vocês nos pontos onde fui mais superficial. No decorrer dos artigos vamos aprimorar esta aplicação e vou explicando um pouco mais sobre cada coisa. Se eu fosse explicando tudo acabaria com um artigo interminável, pois um assunto conecta com o outro e assim por diante. De qualquer maneira, se você tem alguma dúvida sobre migrações ou sobre o Eloquent ORM, mesmo que não tenha sido comentado neste artigo, pode perguntar que eu farei o meu melhor para responder.
Dúvida:
Já tenho um banco de dados pronto, preciso necessariamente criar as migrations ou posso partir direto para as ORMs?
Tem alguma ferramenta que gere as ORMs com base no banco de dados?
Procurei no Google mas nenhuma me pareceu solida o bastante, embora o JeffreyWay tenha me chamado a atenção.
Se você já tem o banco de dados pode ir direto para as ORMs/models.
Com relação a ferramente para gerar eu não conheço nenhuma que gere a partir de um banco pronto. Os geradores do Jeffrey Way são bem legais para acelerar todo o processo, criando a as migrations, models, controllers, etc usando o generate:resource. Mas ele não vai ler o teu banco e buscar as tabelas, campos e relacionamentos.
Se você quer apenas montar os models, pode fazer manualmente mesmo pois eles são bem simples de construir. O que acaba levando mais tempo é a construção da lógica e views.
Oscar,
Lamentavelmente não consegui atingir o objetivo da aula. “Empaquei” no momento em que tento visualizar a http://meu_laravel.localhost/artigos e ele retorna um Erro 404.
Se acesso http://meu_laravel.localhost, aparece o It Works!
Sei que é muito difícil imaginar o que pode estar acontecendo, mas você tem alguma ideia?
Saudações,
Alexandre Bonfá
Já descobri: faltou o ‘/public’ no vhosts… Obrigado, anyway…
Oscar,
Neste artigo vai trabalhar com MySQL, mas ele trabalharia com o Oracle? Estou tentando conexao com o yajra/laravel-oci8, mas mesmo seguindo o manual existente tenho ‘ERRO DE CONEXAO’
Giovani,
Eu ainda não usei o Laravel com o Oracle, mas pela rápida pesquisa que fiz o caminho seria usar essa biblioteca que você comentou. Você já tentou usar o starter kit (https://github.com/yajra/laravel-4.1-starter-kit) que eles oferecem?
Resolvido… era só um problema com minha instalação do instantclient, foi só reinstala-lo e pronto.
Bom, esse exemplo você demostrou como utilizar com tabela. Gostaria de saber a respeito de procedures/functions do PostgreSQL como eu deveria trabalhar no laravel de uma forma correta. Gostaria também de saber a opinião de você se o ideal é utilizar querys ou procedures no laravel, pois estou habituado a usar procedures.
Eu ainda não usei PostgreSQL com o Laravel, mas acredito que as chamadas para stored procedures devem seguir o mesmo estilo de chamadas SQL puras, ou seja:
$result = DB::select('select sua_funcao(?)', array($sua_var));
Com relação ao que seria a melhor prática, acho que depende :). Se você está acostumado a trabalhar com procedures, se já tem uma prática em planejar o banco com as procedures, recomendo que mantenha elas. Até pela performance melhor.
Acho que fazer as queries direto no Laravel vale a pena se você tem um ambiente muito dinâmico, sempre alterando tabelas e modificando o sistema. É uma coisa a menos para sua equipe modificar, as alterações ficam todas no Laravel, migrações, queries, etc.
Olha eu tive um problema já no começo com o install do migrate me dava o erro 1045 dizendo que meu usuário e senha estavam errados, ai acabei pesquisando e achei, tal vez sirva de dica pra outras pessoas, mais dentro da pasta app\config\local encontra-se outro arquivo database.php editem ele também já que as vezes ele não muda sozinho.
Por sinal estou amando teu tutorial muito obrigado pelas ensinanças!!
Ponto muito importante! Vou incluir no tutorial.
Excelente o tutorial, consegui concluir o exemplo.
Olá Oscar!
Gostaria de compartilhar algumas dificuldades que tive, pois estou usando o laravel 5.2.
Demorei muito para me achar com as rotas, pois a estrutura no laravel 5.2 é diferente.
Nesta versão as rotas estão no diretório app/http.
Algo que me ajudou a entender, foi o comando para listar as rotas.
php artisan route:list
Também tive dificuldade como caminho da view e model.
O que me ajudou a entender, foi criá-las através de linha de comando.
php artisan make:model artigo
Parabéns pelos posts, estou iniciando com Laravel e tem ajudado muito!
Valeu pelas dicas Cleber. Eu não tenho conseguido escrever, por isso ainda não atualizei essas questões da versão. Fiz um artigo de migração, mas os posts antigos ainda fazem referência a versão antiga do Laravel.
Bom dia Sr. Oscar Dias!
estou iniciando estudo de programação e agora com a laravel 5. só que quando entro com a URL ( artigo ) me retorna que a class Artigo não foi encontrada, o formulario estar abrindo direitinho mais quando preencho e executo também traz a mesma resposta. te envio o modelo do model se vc poder me da uma dica te agradeço muito, desde ja obrigado…..
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Artigo extends Model
{
class Artigo extends Eloquent
{
}
}
Parece que você fez uma declaração de classe dentro da outra… Você deve fazer apenas uma:
< ?php namespace App; use Illuminate\Database\Eloquent\Model; class Artigo extends Model { // código do model }
Outra coisa que você tem que cuidar é com o namespace e o nome do arquivo. Se o namespace não estiver correto, ou o nome do arquivo for diferente, também pode dar esse erro.
obrigado meu amigo Deus te abençoe… Muito bom estes tutorial, muito mesmo…..
Ótimo artigo Oscar!
Me ajudou bastante, precisei realizar alguns ajustes na versão 5.3 do Laravel, mas ainda assim ele foi muito útil e explicativo! 😀
Muito obrigado! Sucesso!