Continuando com a série de artigos sobre o Laravel, vou explicar como funcionam as rotas e complementar o nosso cadastro utilizando controllers. As rotas são os caminhos por onde nossas URLs passam. Os controllers são as classes onde definimos o comportamento do nosso aplicativo. Para acompanhar o desenvolvimento do nosso “CMS”, o código atual da nossa aplicação pode ser baixado neste link.
Rotas
As rotas, como já mencionado, são definições de qual controller será executado a partir da URL acessada. O arquivo onde são feitas estas definições é o app/routes.php. Nós já usamos este arquivo no post passado, mas usamos ele de uma maneira errada. O que nós fizemos foi colocar diretamente neste arquivo a nossa lógica. Vou explicar as alternativas que temos para trabalhar com rotas e depois vamos corrigir o nosso arquivo.
Comandos Básicos
Primeiro os comandos mais simples que podemos usar nas nossas rotas. Podemos executar uma ação especifica de acordo com nosso pedido HTTP utilizando os seguintes comandos:
1 2 3 4 5 |
Route::get(); Route::post(); Route::put(); Route::delete(); Route::any(); |
Todos utilizam os mesmos parâmetros, sendo que o método Route::any() pode ser utilizado para qualquer tipo de pedido. Não é recomendado a sua utilização por não ser tão transparente quanto usar o método correto para a situação correta. Dois parâmetros são necessários:
1 2 3 4 5 6 |
// Listar todos os artigos Route::get('/artigos/', function() { $artigos = Artigo::get(); return View::make('artigos', compact('artigos')); }); |
O primeiro é a URL que vai “ativar” esta rota. Neste parâmetro você pode utilizar as URLs completas:
- /artigos;
- /artigos/inserir;
- /artigos/editar;
- /artigos/ler/um/artigo;
Além de poder utilizar parâmetros dentro da string da URL:
- /artigos/{acao};
- /artigos/ler/{id};
Estes parâmetros podem ser passados para a função utilizando o mesmo como uma variável:
1 2 3 4 5 |
Route::get('/artigos/ler/{id}', function($id) { $artigo = Artigo::find($id); return View::make('artigos_ler', compact('artigo')); }); |
O segundo parâmetro pode ser uma função anonima (uma Closure, em português – Clausura) como o exemplo acima, ou um método de um controller:
1 |
Route::get('/artigos', 'ArtigoController@showIndex'); |
Neste caso ao acessar o endereço <dominio>/artigos o Laravel executaria o método showIndex() do controller ArtigoController. Dentro da função anonima ou do método do controller você pode definir uma série de respostas para aquela rota, vamos ver as respostas possíveis a seguir.
Repostas
A resposta de um pedido HTTP é basicamente o retorno da nossa função ou método. A maneira mais simples é retornar uma string:
1 2 3 4 |
Route::get('/', function() { return 'Meu retorno'; }); |
Nada de interessante aí. Aprimorando isso você poderia retornar uma string com um HTML dentro do tipo:
1 2 3 4 5 6 7 8 9 10 11 |
Route::get('/', function() { return '<html> <head> <title>Resposta!</title> </head> <body> <p>Resposta em HTML!</p> </body> </html>'; }); |
Neste ponto chegamos as views. Ao invés de codificar o HTML diretamente dentro das nossas rotas e controllers, utilizamos views (ou visões). As views são criadas no diretório app/views e podem ser enviadas como resposta com o seguinte código:
1 2 3 4 |
Route::get('/', function() { return View::make('hello'); }); |
Além de retornar HTML, podemos definir outros tipos de retorno dentro de nossas rotas e controllers:
Redirecionamentos
Você pode redirecionar o usuário utilizando o seguinte código:
1 2 3 4 |
Route::get('/', function() { return Redirect::to('sobre'); }); |
Download de arquivos
Você pode definir a resposta como um download, para indicar para o browser que um arquivo deve ser baixado:
1 2 3 4 5 |
Route::get('baixar/relatorio', function() { $file = 'relatorio.pdf'; return Response::download($file); }); |
JSON
Você pode também enviar um JSON como resposta, especialmente útil para APIs e chamadas AJAX:
1 2 3 4 5 |
Route::get('busca/paises', function() { $data = array('espanha', 'italia', 'brasil'); return Response::json($data); }); |
Filtros
Outra funcionalidade muito útil das rotas são os filtros. No arquivo app/filters.php é possível definir filtros que deve ser executados ao acessar determinadas rotas. Por exemplo, se você tem uma área do seu sistema que deve ser acessada somente por um usuário logado, basta criar um filtro verificando se o usuário está logado ou não.
Exemplo de filtro de login (já vem implementado por padrão):
1 2 3 4 |
Route::filter('auth', function() { if (Auth::guest()) return Redirect::guest('login'); }); |
Para utilizar este filtro você deve utilizar o seguinte código:
1 2 3 4 5 6 7 |
Route::get('admin', array( 'before' => 'auth', function() { return View::make('admin'); } )); |
Você pode também agrupar diversas rotas para que o mesmo filtro seja executado sempre que o usuário tentar acessar uma dessas rotas:
1 2 3 4 5 6 7 8 9 10 11 12 |
Route::group(array('before' => 'auth'), function() { Route::get('admin', function() { return 'Area administrativa'; }); Route::get('usuarios', function() { return 'Cadastro de usuários'; }); Route::get('financeiro', function() { return 'Gestão financeira'; }); }); |
Comandos Avançados
Finalizando essa longa explicação das rotas, mais alguns comandos que permitem um maior controle e flexibilidade nas rotas.
Nomes nas Rotas
1 2 3 4 5 6 |
Route::get('/login', array( 'as' => 'login', function() { return View::make('login'); } )); |
Quando você dá um nome para a rota, pode utilizá-lo ao criar links no seu sistema (route(‘login’)) e para redirecionar utilizando Redirect::route(‘login’).
Rotas HTTPS
1 2 3 4 5 6 7 8 |
Route::get('/artigos/', array( 'https', function() { $artigos = Artigo::get(); return View::make('artigos', compact('artigos')); } )); |
Filtro dos Parâmetros
1 2 3 4 5 6 |
Route::get('/artigo/{formato}/{id}', function($formato, $id) { $artigo = Artigo::get($id); return View::make("artigos/$formato", compact('artigo')); })->where('formato', '[A-Za-z]+') ->where('id', '[0-9]+'); |
Prefixos nas Rotas
1 2 3 4 5 6 7 8 9 10 11 12 |
Route::group(array('prefix' => 'artigos'), function() { Route::get('/php', function() { return 'Artigos PHP'; }); Route::get('/java', function() { return 'Artigos Java'; }); Route::get('/cpp', function() { return 'Artigos C++'; }); }); |
Utilizando prefixos como no exemplo acima, o Laravel vai entender as URLs no formato artigos/php, artigos/java, etc.
Controllers
Como já expliquei anteriormente podemos associar nossas rotas aos nossos controllers da seguinte maneira:
1 |
Route::get('/artigos', 'ArtigoController@showIndex'); |
O problema é que a cada método, teríamos que alterar nosso arquivo de rotas e adicionar a nova rota para o novo método. A maneira que o Laravel utiliza para resolver este problema é utilizar controllers REST.
1 |
Route::controller('artigos', 'ArtigosController'); |
O que vai acontecer agora é o seguinte:
- Se você acessar /artigos, o Laravel vai executar o método getIndex() do ArtigosController.
- Se você acessar /artigos/inserir, o Laravel vai executar o método getInserir() do ArtigosController.
- Se você fizer o POST do /artigos/inserir, o Laravel vai executar o método postInserir() do ArtigosController.
Já deu para perceber como vai funcionar? O Laravel vai utilizar o tipo de pedido HTTP e a segunda parte da URL para acessar diretamente o método correto. Quando não existe a segunda parte da URL, então será executado o método padrão Index.
Controllers
Os controllers são a “central lógica” da nossa aplicação. É no controller que processamos a entrada do usuário e executamos nossos models. Na prática eles são classes que criamos em app/controllers e que estendem a classe BaseController. Vamos agora voltar a nossa aplicação, vou fazendo as alterações e mostrando o código.
Aplicação
Vamos começar modificando nosso arquivo de rotas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?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'); }); // Rota de artigos Route::controller('artigos', 'ArtigosController'); |
Se você comparar com o código anterior, removemos todo aquele código que estava aqui. Aquela lógica será movida para um novo arquivo, o ArtigosController, que deve ser criado em app/controllers.
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 |
<?php class ArtigosController extends BaseController { public function getIndex() { $artigos = Artigo::get(); return View::make('artigos', compact('artigos')); } public function getInserir() { return View::make('artigos_inserir'); } public function postInserir() { $artigo = new Artigo(); $artigo->titulo = Input::get('titulo'); $artigo->conteudo = Input::get('conteudo'); $artigo->save(); return Redirect::to('/artigos'); } } |
E é só isso que precisamos para organizar as rotas e criar nosso primeiro controller. Mas vamos aproveitar e melhorar a aplicação um pouco. Ainda não somos capazes de editar e excluir os artigos, portanto vamos adicionar alguns links na nossa view artigos.php:
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 |
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Lista de Artigos</title> </head> <body> <h1>Artigos</h1> <a href="artigos/inserir" title="Novo">Novo artigo</a> <hr> <?php if($artigos->count()) : ?> <?php foreach ($artigos as $artigo) : ?> <h2><?php echo $artigo->titulo; ?></h2> <small> <a href="artigos/editar/<?php echo $artigo->id; ?>" title="Editar">Editar</a> | <a href="artigos/remover/<?php echo $artigo->id; ?>" title="Remover">Remover</a> </small> <p><?php echo $artigo->conteudo; ?></p> <hr> <?php endforeach; ?> <?php else : ?> <h2>Nenhum artigo encontrado.</h2> <?php endif; ?> </body> </html> |
Nesta view exibimos a lista de artigos existentes no banco de dados. Eu adicionei um link para inserir um novo artigo, links para editar e remover artigos existentes e umas linhas (HR) para facilitar a visualização. Se você tentar acessar os links para editar ou remover, adivinhe o que acontecerá? Um erro. E por que? Porque não temos métodos para editar ou remover no nosso controller.
Lembre-se que a rota foi definida usando Route::controller, isso significa que quando acessarmos artigos/editar, o Laravel vai procurar no controller ArtigosController pelo método getEditar(). Vamos atualizar nosso controller então:
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?php class ArtigosController extends BaseController { public function getIndex() { $artigos = Artigo::get(); return View::make('artigos', compact('artigos')); } public function getInserir() { return View::make('artigos_inserir'); } public function postInserir() { $artigo = new Artigo(); $artigo->titulo = Input::get('titulo'); $artigo->conteudo = Input::get('conteudo'); $artigo->save(); return Redirect::to('/artigos'); } public function getEditar($id) { $artigo = Artigo::find($id); return View::make('artigos_editar', compact('artigo')); } public function postEditar() { $artigo = Artigo::find(Input::get('id')); $artigo->titulo = Input::get('titulo'); $artigo->conteudo = Input::get('conteudo'); $artigo->save(); return Redirect::to('/artigos'); } public function getRemover($id) { $artigo = Artigo::find($id); $artigo->delete(); return Redirect::to('/artigos'); } } |
Temos três métodos novos no ArtigosController:
- getEditar e postEditar: responsáveis pela alteração dos artigos – ainda precisamos de uma nova view aqui.
- getRemover: responsável pela exclusão de artigos.
Os dois métodos GET tem um parâmetro $id. Este parâmetro é um número que é passado na URL artigos/editar/id. Vamos criar a view artigos_editar, para a edição:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Editar artigo</title> </head> <body> <h1>Editar artigo</h1> <form method="post"> <div> <input name="titulo" type="text" placeholder="Título" value="<?php echo $artigo->titulo; ?>"> </div> <div> <textarea name="conteudo"><?php echo $artigo->conteudo; ?></textarea> </div> <input type="hidden" name="id" value="<?php echo $artigo->id; ?>"> <input type="submit" value="Salvar"> </form> </body> </html> |
Nesta view temos os valores preenchidos com o que buscamos no banco. Também acrescentei um campo INPUT do tipo hidden, para o ID do artigo. Este ID é enviado junto dos dados do formulário no POST, para que no método postEditar() possamos usar este ID com o comando Input::get(‘id’).
Conclusão
Até agora já montamos a estrutura básica de uma aplicação. Vimos a interação com o banco de dados usando migrações e o Eloquent ORM. Hoje expliquei como funcionam as rotas e isso foi aplicado no nosso aplicativo. Para a próxima etapa, vamos reescrever nossas views usando o sistema de templates Blade. Até a próxima.
Chegamos até aqui…. ansioso pela próxima.
Previsão ?
Previsão para o dia 15/6, próximo final de semana.
deus o abençoe Oscar, muito obrigado pela força.
Boas,
Trabalho em asp.net mvc e estou a gostar do laravel, visto que é um MVC como conheço.
Dúvida: no getRemover() vc faz $artigo->delete(); e pronto, redirecciona.
Não tem que fazer $artigo->save(); ?
Estou abituado a usar o EntitieFramework com o ADO.NET e tenho que fazer o save depois do remove 😉
Cumprimentos e muito bom trabalho!!!
Olá Márcio,
Não precisa fazer o save(). O delete() é suficiente para excluir o registro. Você pode usar também Artigo::destroy($id), ele faz a mesma coisa.
Abraço
E no caso de querer apresentar uma view para confirmar o delete ?
Posso usar o getRemover() para construir uma view com os dados dos artigo, e depois nessa view uma form onde vou enviar para o postRemover() e aí é que faz o Artigo::destroy($id). Legal assim?
Cumprimentos
Sim, com certeza. Cria a view no get e faz a deleção no post. Perfeito 😉
Olá
não lembro quando aconteceu isso, se só notei hoje… mas mesmo tendo no arquivo de rotas Route::controller(‘artigos’, ‘ArtigosController’); o meu arquivo acessado é o controllers/Artigos (era um arquivo de teste, ele tem o mesmo conteúdo do ArtigosCotroller.php mas se eu modificar o Artigos.php dá pau na aplicação)
Nao consigo entendr o porquê
achei isso
Line 10: ‘ArtigosController’ => $baseDir . ‘/app/controllers/Artigos.php’,
mas nao sei editar
achei
vendor/composer/autoload_classmap.php
Dependendo da estrutura que você montar, com namespaces, sub diretórios, etc, pode ser necessário executar o composer dump-autoload para que o composer crie arquivo que carrega as classes novamente.
Com relação a nomenclatura, eu geralmente utilizo ArtigoController para não dar conflito com um modelo Artigo.
como faço para colocar um formulário de contato nesse mesmo projeto seu.
Várias maneiras… minha sugestão é adicionar uma rota e um controller para isso, com as respectivas views. Dentro do controller você cria os métodos get e post para o formulário de contato com a lógica necessária. O Laravel já possui uma boa biblioteca pra envio do contato por email.
Muito simplificada e objetiva explicação, agradeço este conhecimento compartilhado. Ajudou na construção do meu projeto. Obrigado novamente!