Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Casa do Código

Casa do Código “A tudo que tenho de mais importante nessa vida, minha esposa Jordana e nossa primeira filha, Clara.” – Rodrigo Turini i Casa do Código Agradecimentos Por mais que eu queira e tente muito, nenhum conjunto de palavras será o suficiente para expressar meu eterno agradecimento à minha esposa Jordana e nossa família, que agora está maior com a chegada da pequena Clara. Gostaria também de agradecer ao Daniel Turini, que sempre influenciou em meu crescimento pessoal e profissional. Foi ele quem deu meu primeiro computador e me encaminhou ao mundo da Ciência da computação e recentemente aos diversos projetos em PHP que desenvolvemos juntos. Agradeço também às equipes da Caelum, Alura e Casa do Código, que são empresas sensacionais constituídas por profissionais extraordinários. Em especial a meus amigos Paulo e Guilherme Silveira, Victor Harada, Maurício Aniche e Luiz Corte Real. iii Casa do Código Prefácio Apesar de ser intimamente ligado ao Java, uma outra linguagem de programação, há pouco mais de um ano recebi o desafio de manter a aplicação interna de uma empresa americana, totalmente escrita em PHP. No começo, o trabalho foi bem mais difícil do que eu esperava, já que se tratava de um projeto legado, nem um pouco orientado a objetos e muitíssimo complicado de manter. Depois de um tempo, vimos que a solução mais simples para esse caso seria reescrever todo o projeto, utilizando boas práticas, testes automatizados e as possibilidades mais atuais da linguagem. É aí que entra o Laravel. Eu já havia tido algum contato com esse framework quando estava trabalhando no conteúdo técnico do curso de PHP da Caelum, mas ao estudá-lo a fundo enquanto viabilizava as opções atuais, tive a sensação de que seria uma excelente escolha. Hoje, eu tenho certeza. O objetivo desse livro será mostrar por que o Laravel é a aposta do mercado atual e minha primeira opção de framework MVC em PHP. Criar aplicações elegantes em pouco tempo nunca foi tão fácil. v Casa do Código Sumário Sumário 1 2 3 Introdução 1.1 O projeto e as tecnologias . . . . . . . 1.2 O que veremos durante o livro . . . . 1.3 Download do Laravel . . . . . . . . . 1.4 Acesse o código desse livro . . . . . . 1.5 Aproveitando ao máximo o conteúdo 1.6 Tirando suas dúvidas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 3 4 5 5 5 Novo projeto com Laravel 2.1 Como criar cinco telas de uma aplicação web? . 2.2 Framework, pra que te quero? . . . . . . . . . . 2.3 Novo projeto: Uma app de controle de estoque 2.4 Entendendo a estrutura de pastas . . . . . . . . 2.5 Alterando o namespace padrão com Artisan . . 2.6 Criando nossa primeira lógica . . . . . . . . . . 2.7 Conflito entre rotas do Laravel . . . . . . . . . . 2.8 Antes de continuar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 7 9 12 13 14 17 18 . . . . . . 19 19 21 23 27 30 32 . . . . . . . . . . . . . . . . . . MVC e conexão com banco de dados 3.1 Model-View-Controller . . . . . . . . . . . 3.2 Colocando MVC em prática . . . . . . . . 3.3 Trabalhando com banco de dados . . . . . 3.4 Configuração e conexão com MySQL . . . 3.5 Para saber mais: Environment . . . . . . . 3.6 Aonde chegamos e para onde queremos ir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii Casa do Código Sumário 4 Camada de modelo, migrations e seeds 4.1 O modelo Produto . . . . . . . . . . 4.2 Trabalhando com migrations . . . . 4.3 Apagando a tabela produtos . . . . 4.4 Executando migrações . . . . . . . 4.5 Inserindo dados na tabela produtos 5 . . . . . . . . . . . . . . . viii . . . . . . . . . . . . . . . Trabalhando com a View 5.1 Cada coisa em seu lugar: camada View . . . . . 5.2 Consultando os arquivos de log do framework 5.3 Incluindo parâmetros na view . . . . . . . . . . 5.4 Alguns detalhes e curiosidades . . . . . . . . . . 5.5 Melhorando a aparência da nossa listagem . . . 5.6 Aonde chegamos e para onde queremos ir . . . 6 Parâmetros da request e URL 6.1 Exibindo detalhes do produto . . . . . . 6.2 Enviando parâmetros na request . . . . . 6.3 Recuperando parâmetros da request . . 6.4 Conhecendo um pouco mais da Request 6.5 URLs mais semânticas . . . . . . . . . . 6.6 Recuperando parâmetros da URL . . . . 6.7 Alguns cuidados necessários . . . . . . . 7 . . . . . Views mais flexíveis e poderosas 7.1 Blade, a template engine do Laravel . . . 7.2 Variáveis com blade . . . . . . . . . . . . 7.3 Migrando a listagem para Blade . . . . . 7.4 Loopings e mais loopings . . . . . . . . . 7.5 Adicionando condições na view . . . . . 7.6 Marcando produtos em falta no estoque 7.7 Organizando nossas views . . . . . . . . 7.8 Estilizando o template principal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 33 35 38 38 39 . . . . . . 45 46 49 52 53 55 60 . . . . . . . 61 61 67 68 71 72 73 76 . . . . . . . . 79 81 87 89 90 92 95 99 103 Casa do Código 8 Sumário Request e métodos HTTP 8.1 Criando formulário de novos produtos . . 8.2 Criando o método adiciona . . . . . . . . 8.3 Inserindo os produtos no BD . . . . . . . . 8.4 Retornando uma view de confirmação . . 8.5 Utilizando os métodos do HTTP . . . . . 8.6 Mais métodos HTTP e quando utilizá-los . . . . . . 9 Os diferentes tipos de resposta 9.1 Redirecionando para outras lógicas . . . . . 9.2 Mas e a mensagem de confirmação? . . . . . 9.3 Recuperando valores da requisição anterior 9.4 Escolhendo quais valores manter . . . . . . 9.5 Outros tipos de redirect . . . . . . . . . . . . 9.6 Para saber mais: rotas nomeadas . . . . . . . 9.7 Outros tipos de resposta . . . . . . . . . . . 10 Eloquent ORM 10.1 Conheça a solução: ORM . . . . . . 10.2 Tudo mais simples com Eloquent . 10.3 Buscando pelo ID com Eloquent . . 10.4 Migrando a inserção de produtos . 10.5 Função de remover produtos . . . . 10.6 Código final do ProdutoController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Validando os dados de entrada 11.1 Validação com Laravel . . . . . . . . . . . . . . . . 11.2 Validando com Form Requests . . . . . . . . . . . . 11.3 Usando Form Request ao adicionar . . . . . . . . . 11.4 Exibindo errors de validação . . . . . . . . . . . . . 11.5 Customizando as mensagens . . . . . . . . . . . . . 11.6 Customizando mensagens de um campo específico 11.7 Boa prática: experiência de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 109 113 118 118 123 128 . . . . . . . 129 129 132 134 136 137 138 140 . . . . . . 143 144 145 147 149 153 155 . . . . . . . 159 161 165 169 171 176 178 180 ix Casa do Código Sumário 12 Autenticação e segurança 12.1 Tela de login e autenticação . . . . . . . . . . . . . 12.2 Entendendo o login da aplicação . . . . . . . . . 12.3 Criando uma lógica de autenticação . . . . . . . . 12.4 Autorização com Middlewares . . . . . . . . . . . 12.5 Registrando o middleware para rotas específicas 12.6 Utilizando o middleware padrão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 183 188 191 197 202 205 13 Mais produtividade com Artisan 207 13.1 Como lembrar de todos os comandos? . . . . . . . . . . . . . 208 14 Os próximos passos x 213 Capítulo 1 Introdução 1.1 O projeto e as tecnologias Nosso projeto será de controle de estoque. Como usuário final, seremos capazes de gerenciar os produtos que serão persistidos em um banco de dados MySQL, visualizar com facilidade os que estão em falta no estoque, e mais. O contexto é simples, mas será uma boa base para explorar os poderosos recursos e facilidades que o Laravel oferece. Ao final deste livro, teremos uma listagem parecida com: 1.1. O projeto e as tecnologias Fig. 1.1: Listagem com alguns produtos. Adição de produtos com validação de dados: Fig. 1.2: Adição e validação de produtos. Autenticação e segurança: 2 Casa do Código Casa do Código Capítulo 1. Introdução Fig. 1.3: Formulário de Login da aplicação. E muito mais, como veremos a seguir. Um ponto importante é que o livro não focará apenas nas funcionalidades da aplicação, mas sim nos conceitos e recursos como um todo. Você entenderá, entre diversos outros, como funciona o MVC e importantes conceitos relacionados aos projetos web. 1.2 O que veremos durante o livro Durante a leitura veremos: • Como criar e configurar uma aplicação com Laravel. • Como configurar rotas no arquivo routes.php. • Como funciona o padrão arquitetural MVC, como ele se aplica ao framework e quais suas vantagens. • Quais as configurações necessárias para integrar seu projeto web com um banco de dados. • Como utilizar o facade DB para executar instruções no banco de dados. • Como tirar proveito do Eloquent, um poderoso framework ORM. Suas vantagens e principais operações. 3 1.3. Download do Laravel Casa do Código • Como enviar parâmetros para a view, redirecionar para outras ações e recuperar parâmetros da requisição do navegador. • Como dominar o Blade, um mecanismo de template padrão do Laravel, utilizar layouts e operadores lógicos. • Serializar o resultado em diferentes formatos, como JSON. • Criar validações de dados e entender o conceito de Form Requests. • Cuidar da autenticação e segurança com Middlewares. • Como tirar proveito dos zilhares de comandos do Artisan. 1.3 Download do Laravel Se ainda não tem o Laravel instalado, você pode fazer seu download e ver um passo a passo detalhado de instalação em seu próprio site: http://laravel.com/docs/installation Se tiver qualquer problema no processo de instalação, que vai variar de acordo com o sistema operacional, não desanime. Envie um e-mail na lista de discussões desse livro que vamos ajudá-lo. O endereço é: https://groups.google.com/d/forum/livro-laravel Laravel Homestead Se preferir, em vez de instalar o PHP, Laravel e todas as suas dependências em sua máquina local, você pode utilizar o Laravel Homestead para preparar o ambiente de uma forma bem simples e elegante em uma máquina virtual. O Homestead é uma solução oficial e já inclui PHP 5.6, MySQL, além de diversos outros recursos de que você pode precisar para desenvolver aplicações completas em Laravel. Se quiser, você pode ler mais a respeito e ver instrução de instalação e uso em: http://laravel.com/docs/homestead 4 Casa do Código Capítulo 1. Introdução 1.4 Acesse o código desse livro O código completo do projeto que desenvolveremos no decorrer desse livro está disponível em meu repositório do GitHub. Você pode acessá-lo em: https://github.com/Turini/estoque-laravel 1.5 Aproveitando ao máximo o conteúdo Para tirar um maior proveito dessa leitura, não fique preso à teoria. Você pode e deve acompanhar o livro com seu editor favorito aberto, escrevendo todo o código e testes dos capítulos. Além disso, eu sempre recomendo que você vá além escrevendo novos testes para solidificar ainda mais o conhecimento. 1.6 Tirando suas dúvidas Ficou com alguma dúvida? Não deixe de me enviar um e-mail. A lista de discussão a seguir foi criada exclusivamente para este livro: https://groups.google.com/d/forum/livro-laravel Essa lista é um canal de comunicação direta comigo e com os demais leitores, portanto fique à vontade para levantar discussões técnicas, apontar correções, indicar melhorias etc. Seu feedback é sempre muito bem-vindo. Além da lista, não deixe de consultar a documentação do framework durante todo o aprendizado. Ela é bem completa e explicativa: http://laravel.com/docs/ Outro recurso que você pode utilizar para esclarecer suas dúvidas e participar ativamente na comunidade é o fórum do GUJ. Lá você não só pode perguntar, mas também responder, editar, comentar e assistir a diversas discussões sobre o universo da programação. http://www.guj.com.br/ 5 Capítulo 2 Novo projeto com Laravel 2.1 Como criar cinco telas de uma aplicação web? Imagine uma aplicação que tenha as funções de adicionar, remover, listar, enviar e-mail, entre diversas outras que são essenciais para toda aplicação web. Para cada uma, devemos executar um código de lógica, buscar ou atualizar informações do banco, mostrar um HTML como resposta. Bastante coisa repetitiva, não é? Será que alguém não pode nos ajudar? 2.2 Framework, pra que te quero? Independente da linguagem ou tecnologia que estamos usando, um conceito global é: não queremos ficar nos preocupando com infraestrutura. É aí 2.2. Framework, pra que te quero? Casa do Código que os frameworks entram. Eles nos ajudam e muito a agilizar o processo de desenvolvimento, de forma organizada, evitando repetições de código e muito mais. Quem nunca foi criar um projeto novo e acabou copiando a estrutura de algum outro projeto que já tinha criado antes? Isso acontece porque boa parte dessa estrutura será igual, você não precisa reinventar a roda a cada novo projeto. Essa é uma das ideias dos frameworks, oferecer essa estrutura padrão entre os projetos, de forma bem organizada e fácil de manter, segundo as melhores práticas do mercado. Essa reutilização de código entre vários projetos vai lhe poupar muito tempo e trabalho. Precisa conectar com o banco? Enviar um e-mail? Migrar seu banco de dados? Você perceberá que o Laravel, assim como diversos outros frameworks do mercado, já tem tudo isso pronto e pré-configurado. Ao longo desse livro você perceberá que não precisa usar frameworks, não é obrigatório. Mas mesmo assim você não vai querer mais viver sem eles, que nos tornam muito mais produtivos e simplificam bastante o processo de desenvolvimento. Algumas outras opções famosas Além do Laravel, que vamos aprender, existem diversas opções bastante interessantes no mercado. Uma das mais populares é o Zend Framework 2, da própria Zend Technologies. Além dele, há também o CodeIgniter, Symphony, CakePHP, Phalcon, entre diversos outros. O Laravel é uma das maiores apostas da atualidade. Muito se deve à sua simplicidade, sintaxe, flexibilidade e rica documentação. Além de seu site oficial, que é o http://laravel.com, você também conta com bastante conteúdo, discussões, exemplos de código, perguntas e respostas em um site de receitas, o http://laravel-recipes.com/. Eu recomendo que, além do livro, você use e abuse desses sites para dominar completamente a ferramenta. 8 Casa do Código Capítulo 2. Novo projeto com Laravel 2.3 Novo projeto: Uma app de controle de estoque Para começar a aprender Laravel, vamos criar um novo projeto chamado estoque. Fazer isso é verdadeiramente simples, desde o começo você já percebe as vantagens em usar o framework. Quer ver como é fácil? Se você já tem o Laravel instalado, tudo que precisa fazer é rodar o comando laravel new pelo terminal, passando o nome do projeto que queremos criar. Em nosso caso será: laravel new estoque A saída será parecida com: Fig. 2.1: Criando o projeto via terminal, na pasta Desktop. O texto Application ready! Build something amazing será exibido e pronto, ele cuidou de todo o trabalho pesado. Note que uma pasta com o nome do projeto ( estoque, neste caso) foi criada no mesmo diretório em que você executou o comando. Ela já tem toda a estrutura de pastas, algumas classes e dependências configuradas. 9 2.3. Novo projeto: Uma app de controle de estoque Casa do Código Fig. 2.2: Estrutura inicial do projeto. Legal, não é? Se você está se perguntando o que significa cada uma dessas pastas, não se preocupe: em breve vamos falar mais sobre essa estrutura inicial e a entenderemos mais a fundo no decorrer do livro. Logo você estará dominando tudo isso. Mas por agora, vamos rodar o projeto estoque para garantir que tudo está funcionando conforme o esperado? Normalmente usamos o próprio Apache do servidor em que fizermos deploy, mas em ambiente de desenvolvimento, podemos servir nossa aplicação utilizando o comando php artisan serve, que executa a aplicação no servidor de desenvolvimento do PHP. Basta rodar esse comando de dentro da pasta do projeto: Fig. 2.3: Executando servidor de desenvolvimento do PHP com Laravel. 10 Casa do Código Capítulo 2. Novo projeto com Laravel Navegando nas pastas do OS pelo terminal Você pode utilizar o comando cd (change directory) para navegar pelas suas pastas via terminal. Um exemplo em Windows seria: D:\> cd Desktop\estoque D:\Desktop\estoque> O mesmo comando pode ser utilizado em um ambiente Unix (Linux ou Mac OS). Para listar, há uma diferença. Em Windows utilizamos o comando dir: D:\Desktop\estoque> dir // vai mostrar todos os arquivos Porém, nos outros sistemas que foram citados o comando será ls. Repare: turini ~ $ cd Desktop/estoque turini/Desktop/estoque ~ $ ls // vai mostrar todos os arquivos Considerando, portanto, que meu projeto se chama estoque e foi criado dentro da pasta Desktop, para executar o comando que inicia o servidor você precisará executar os dois comandos: cd Desktop/estoque/ php artisan serve Se tudo correu bem, a mensagem Laravel development server started on http://localhost:8000 deve ter aparecido no seu terminal. Vamos testar? Basta acessar essa URL no navegador de sua preferência. O resultado será: 11 2.4. Entendendo a estrutura de pastas Casa do Código Fig. 2.4: Página de boas-vindas do Laravel 5. Note que, para criar um projeto e executar o Laravel, nós não precisamos de nenhuma configuração extra. Basta criar o projeto em alguns segundos, com o comando laravel new, e sair usando! Isso é muito conveniente quando estamos começando um novo projeto. Podemos gastar nosso tempo com o que realmente importa: nossas regras de negócio. Precisa fazer alguma configuração adicional? O framework faz todo o possível para que você não precise ficar configurando nada, mas em alguns momentos isso pode ser necessário. Se algo de que você precisa não está configurado por default, como por exemplo o locale, você pode fazer isso facilmente pelo arquivo de configurações presente em app/config/app.php. Quer saber mais sobre essas configurações adicionais? Talvez você queira dar uma olhada na página de configurações do framework, disponível em: http://laravel.com/docs/configuration Mas, por enquanto, não precisaremos de nenhuma configuração adicional em nosso projeto. Se ele executou sem nenhum problema, já estamos prontos para prosseguir. 2.4 Entendendo a estrutura de pastas Como vimos, ao criar um novo projeto, diversas classes e arquivos foram criados. O objetivo dessa estrutura inicial é oferecer um padrão e o mínimo de esforço possível para começar o seu projeto. Isso é bem legal, mas claro, caso preferir, você também pode renomear as classes e mudar a estrutura de pastas 12 Casa do Código Capítulo 2. Novo projeto com Laravel para ficar como melhor lhe agrada, o framework não impõe muitas restrições quanto a isso. Durante todo o livro conheceremos um pouco mais sobre as pastas e essa estrutura inicial, mas desde já podemos ter uma breve noção do que vai em cada lugar. Essas são algumas das principais pastas: • app: nela ficam seus modelos, views e controllers, que serão bem detalhados no próximo capítulo. Em poucas palavras, é onde boa parte do seu código vai ficar. Ela possui uma série de subdiretórios, como Commands, Console, Http, Events, entre outros. Não se preocupe em entender o significado de cada um deles agora, vamos vê-los melhor conforme formos precisando. • config: como o nome já indica, é onde ficam os arquivos de configuração do seu projeto. Se você precisar alterar as configurações de cache, e-mail, banco de dados, entre outras, já sabe onde encontrar. • public: é a pasta pra onde seu web server vai apontar. Lá fica o arquivo index.php, que aponta para sua aplicação. Além disso, é comum colocarmos os arquivos css, imagens, javascript e todos os demais arquivos públicos nesse diretório. • vendor: é onde fica o source code do Laravel, plugins e outras dependências. Tudo que você usar de terceiros (bibliotecas, frameworks etc.) deve ficar nela. Esse é só um pouco, claro, mas já é o bastante por agora. Vamos entrar mais a fundo nesse conteúdo no decorrer do livro, mas se quiser adiantar, talvez queira dar uma olhada na página do Laravel que explica essa estrutura inicial: http://laravel.com/docs/structure 2.5 Alterando o namespace padrão com Artisan O namespace padrão de toda aplicação com Laravel é App, mas é muito comum e bastante recomendado que você altere o namespace para o nome da 13 2.6. Criando nossa primeira lógica Casa do Código sua aplicação. Como fazer isso? É muito fácil, basta rodar um simples comando e pronto. Pra subir o server e testar, usamos o php artisan serve, lembra? Esse Artisan é uma ferramenta de linha de comando já inclusa no framework. Ela nos oferece uma série de comandos úteis para tornar nosso desenvolvimento mais produtivo. Para mudar o namespace, por exemplo, podemos usar o php artisan app:name. Vamos mudá-lo para estoque, que é o nome do projeto. Basta executar o seguinte comando pelo terminal, dentro da pasta de seu projeto: php artisan app:name estoque A saída será parecida com: Fig. 2.5: Alterando o namespace com Artisan. Tudo pronto, namespace alterado! 2.6 Criando nossa primeira lógica Agora que já conhecemos um pouco mais sobre o Laravel, queremos ensinálo como queremos que ele reaja quando alguém acessar determinada URL, isto é, criar as nossas próprias rotas. Mas quão complicado é fazer isso? Quando acessamos http://localhost:8000/, ou seja, a URL / da nossa aplicação, em algum lugar foi configurado que a página padrão do Laravel deveria ser exibida, não é? Esse trabalho é feito no o arquivo routes.php, que fica dentro da pasta app/Http/. Abra o arquivo para conferir seu conteúdo, que deve estar parecido com: 14 Casa do Código Capítulo 2. Novo projeto com Laravel <?php // comentário omitido Route::get(’/’, ’WelcomeController@index’); Route::get(’home’, ’HomeController@index’); Route::controllers([ ’auth’ => ’Auth\AuthController’, ’password’ => ’Auth\PasswordController’, ]); Isso pode mudar de acordo com a versão do Laravel que você estiver utilizando. Na versão 5.1, por exemplo, o conteúdo será: <?php // comentário omitido Route::get(’/’, function () { return view(’welcome’); }); Como você pode ver, ele já vem com algumas rotas configuradas, como a / que nos leva para a página default do Laravel. O código pode parecer diferente no começo, mas não é complicado. Vamos entender em detalhes cada linha de código desse arquivo, mas antes disso, apague todo esse código para criarmos nossa própria rota. Que acha? É bem fácil, basta deixar seu arquivo vazio, só com a tag do php: <?php // nosso código vai aqui E agora, usar o Route:get para definir uma nova rota. Podemos fazer algo como: <?php 15 2.6. Criando nossa primeira lógica Casa do Código Route::get(’/’, function() { return ’Primeira lógica com Laravel’; }); Sucesso, já criamos nossa primeira rota. Vamos entender o que aconteceu? Usamos o método estático get, da classe Route, passando dois parâmetros. O primeiro é a rota (caminho, ou path como comumente é chamado) que será acessado pelo navegador. O segundo parâmetro é uma função com a resposta que deverá ser enviada. Em poucas palavras, esse código ensina que, quando alguém acessar a URL /, o Laravel deve retornar o texto Primeira lógica com Laravel para o navegador. Vamos testar? Basta rodar o comando php artisan serve para subir o servidor novamente (caso ainda não esteja startado) e acesse http: //localhost:8000/ em seu navegador. Veja a resposta: Fig. 2.6: Primeira lógica com Laravel. Perfeito, o texto foi exibido no navegador! Mandamos um texto comum, mas podemos responder com qualquer HTML válido. Por exemplo, envolvendo esse texto em um h1 para ter um destaque maior na página: <?php Route::get(’/’, function() { return ’<h1>Primeira lógica com Laravel</h1’; }); 16 Casa do Código Capítulo 2. Novo projeto com Laravel Você não precisa restartar o servidor, basta acessar a página novamente para ver o resultado: Fig. 2.7: Resposta com tag html. 2.7 Conflito entre rotas do Laravel É importante perceber desde já que você pode criar quantas rotas quiser no arquivo routes.php: <?php Route::get(’/’, function() { return ’<h1>Primeira lógica com Laravel</h1’; }); Route::get(’/outra’, function() { return ’<h1>Outra lógica com Laravel</h1’; }); Agora temos duas rotas, uma para a URL / e outra para /outra. Mas o que aconteceria se as duas fossem registradas na mesma URL? Na /, por exemplo. <?php Route::get(’/’, function() { return ’<h1>Primeira lógica com Laravel</h1’; 17 2.8. Antes de continuar Casa do Código }); Route::get(’/’, function() { return ’<h1>Outra lógica com Laravel</h1’; }); Tente rodar esse código para ver o resultado, a segunda rota vai sobrescrever a primeira e o texto Outra lógica com Laravel será exibido. Ou seja, em caso de ambiguidade sempre a última rota é quem será registrada. Há outras formas de lidar com ambiguidade, como quando usamos diferentes métodos HTTP, mas entraremos nesse assunto um pouco mais à frente. 2.8 Antes de continuar Se quiser praticar um pouco mais, eu recomendo que crie outras rotas e novos testes antes de seguir para o próximo capítulo. Não se preocupe em entender as partes que ainda não vimos, ok? Foque apenas neste conteúdo inicial. Ficou com qualquer dúvida? Não deixe de perguntar! Lembre-se que um grupo de discussões foi criado especialmente para este livro: https://groups.google.com/d/forum/livro-laravel Agora que já sabemos o essencial, podemos partir para as regras de negócio do nosso sistema de estoque. Está preparado? 18 Capítulo 3 MVC e conexão com banco de dados 3.1 Model-View-Controller Repare novamente na forma como fizemos para registrar nossa primeira lógica: Route::get(’/’, function() { return ’<h1>Primeira lógica com Laravel</h1>’; }); O problema de definir as rotas dessa forma, com o código de resposta implementado diretamente em uma função anônima, é que não estamos se- 3.1. Model-View-Controller Casa do Código guindo nem um pouco as boas práticas da orientação a objetos. O código fica todo espalhado, difícil de manter e reutilizar. Conforme nossa aplicação for crescendo, vamos precisar fazer acesso ao banco de dados, executar lógicas com nossas regras de negócio etc. Tudo isso ficará dentro do mesmo arquivo route.php? Seria uma bagunça! Além disso, vamos misturar nossa lógica de negócio com nossa lógica de apresentação, o HTML de resposta. Precisamos de uma forma melhor de dividir as responsabilidades de nossa aplicação. É aí que entra o MVC, ou Model View Controller. A grande ideia desse padrão arquitetural é que você separe suas regras de negócio em 3 camadas, cada uma com sua responsabilidade muito bem definida: • Model é a camada onde ficam nossas regras de negócio, nossas entidades e classes de acesso ao banco de dados. • View é a responsável por apresentar as páginas e outros tipos de resultado para o usuário (ou mesmo para outros sistemas, que se comunicam). É a resposta que o framework envia para o navegador, que normalmente é um HTML. • Controller é quem cuida de receber as requisições web e decidir o que fazer com elas. Nessa camada definimos quais modelos devem ser executados para determinada ação e para qual view vamos encaminhar a resposta. Em outras palavras, essa camada quem faz o link entre todas as outras. Diversos frameworks, das mais diferentes linguagens, seguem esse padrão do MVC. Com Laravel não é diferente, o fluxo fica assim: 20 Casa do Código Capítulo 3. MVC e conexão com banco de dados Fig. 3.1: Fluxo do MVC com Laravel. Repare que, quando nosso cliente envia uma requisição pelo navegador, primeiramente temos um arquivo PHP, que é o routes.php, que está frente de todos. Ele cuida de atender as requisições e enviá-las para o local correto, no caso os nossos controllers. Os controllers, por sua vez, decidem o que fazer com as requisições, passando pela camada de model (que fazem acesso ao banco, executam as regras de negócio etc.) e logo em seguida delegam pra view que será exibida como resposta no navegador do cliente. Agora que já sabemos um pouco da teoria, vamos colocar o MVC em prática? 3.2 Colocando MVC em prática Em vez de definir todas as lógicas do nosso sistema nesse arquivo único, o routes.php, vamos organizá-las desde o início em Controllers distintos. Nosso sistema de estoques vai ter uma página principal, com a listagem de produtos. Podemos começar por ela. Vamos criar um novo arquivo chamado ProdutoController, dentro da pasta app/Http/Controllers, que é o diretório padrão para esse tipo de classe. Dentro do controller, crie um método chamado lista. O arquivo ProdutoController.php ficará assim: <?php namespace estoque\Http\Controllers; class ProdutoController { 21 3.2. Colocando MVC em prática Casa do Código public function lista(){ // nosso código vai aqui } } Note que, como estamos trabalhando com uma estrutura de diretórios, tivemos que definir o namespace no cabeçalho do arquivo. Outra regra importante é que todo controller do Laravel deve herdar de uma classe chamada Controller, que foi criada automaticamente junto com nosso projeto. Para isso, só precisamos adicionar extends Controller na declaração da nossa classe, que ficará assim: <?php class ProdutoController extends Controller { public function lista(){ // nosso código vai aqui } } A classe Controller existe exclusivamente para que seja possível definirmos lógicas em comum que podem ser compartilhadas entre todos os controller de nossa aplicação. Além disso, ela já traz alguns imports ( uses) essenciais para os controllers, que serão detalhados conforme formos precisando. Inicialmente, vamos fazer o método lista retornar um HTML puro, com o cabeçalho da listagem de produtos. O código ficará assim: <?php class ProdutoController extends Controller { public function lista(){ return ’<h1>Listagem de produtos com Laravel</h1>’; } } E agora que temos esse comportamento definido no controller, vamos criar uma rota para ele no arquivo routes.php, que continua responsá22 Casa do Código Capítulo 3. MVC e conexão com banco de dados vel pelo registro de nossas rotas e outras configurações, mas a diferença é que agora ele apenas apontará para o método do controller que deve ser executado. O arquivo de rotas deve ficar assim: <?php Route::get(’/produtos’, ’ProdutoController@lista’); // demais rotas omitidas Note que o padrão é o nome do controller, seguido de um @ e o nome do método. Agora quando uma requisição for feita para a URL /produtos, o método lista do ProdutoController será executado. Bem simples, não acha? Vamos testar? Basta acessar http://localhost:8000/produtos em seu navegador. Fig. 3.2: Início da listagem de produtos. Sucesso, o HTML de resposta foi exibido conforme esperado. 3.3 Trabalhando com banco de dados A listagem ainda está muito simples, ainda estamos mostrando apenas um texto onde queremos mostrar todos os produtos. Nosso objetivo agora será 23 3.3. Trabalhando com banco de dados Casa do Código buscar essas informações de um banco de dados, como é natural nas aplicações do dia a dia. Iniciando com MySQL Usaremos o banco de dados MySQL em nosso projeto, já que é um dos bancos de dados relacionais mais utilizados no mercado, é gratuito e bastante simples de instalar. Se você ainda não tem o MySQL instalado, pode fazer download pelo link: http://dev.mysql.com/downloads/mysql/ Fig. 3.3: Página de download do MySQL. Ao final da página, você encontrará um campo para escolher seu sistema operacional. Selecione e depois clique em download, na opção que preferir: 24 Casa do Código Capítulo 3. MVC e conexão com banco de dados Fig. 3.4: Selecionando opção de download de acordo com o sistema operacional. No mesmo site, você encontra um tutorial de instalação de acordo com o seu sistema operacional. Ou você pode acessar o tutorial diretamente em: http://dev.mysql.com/doc/refman/5.7/en/installing.html Após baixar e instalar o MySQL, vamos nos logar no MySQL e criar a base de dados (database) de nosso projeto. O primeiro passo é bem simples, tudo que você precisa fazer para se logar é abrir seu terminal e digitar: mysql -u SEU_USUARIO -p SUA_SENHA Como em meu caso o usuário é root e eu não tenho uma senha, só preciso fazer: mysql -u root 25 3.3. Trabalhando com banco de dados Casa do Código Fig. 3.5: Tela inicial do MySQL Command Line MySQL Command Line ou Workbench? Há quem prefira utilizar o MySQL Workbench, que é uma ferramenta visual. Pela simplicidade, nos exemplos do livro vamos utilizar o MySQL Command Line. Nele, nós executamos as instruções diretamente pela linha de comando, no terminal. Se tiver qualquer dificuldade para instalar ou executar os comandos, não pense duas vezes antes de mandar um e-mail para lista deste livro. Agora que já estamos logados, podemos criar uma nova database chamada estoque_laravel. Isso pode ser feito com o comando: create database estoque_laravel; Após executá-lo, a saída deve ser parecida com: Query OK, 1 row affected (0.00 sec) Perfeito, já temos o banco. Para evitar que você tenha que criar todas as tabelas e cadastrar algumas informações manualmente nesse momento, vamos importar o arquivo dump.sql com alguns produtos já cadastrados em 26 Casa do Código Capítulo 3. MVC e conexão com banco de dados uma tabela de produtos. Não se preocupe, muito em breve substituiremos esse dump com recursos oferecidos pelo Laravel. Um arquivo de dump é um arquivo de texto com instruções SQL, com os inserts de dados ou até mesmo com as instruções de criação de tabelas. Criando seu próprio dump Se quiser saber mais sobre esse arquivo de dump, ou mesmo criar o seu próprio, você pode dar uma olhada no link: http://dev.mysql.com/doc/refman/5.0/en/mysqldump-sql-format. html Isso pode ser bem útil quando queremos fazer backups de segurança de nossas bases de dados. Faça o download do dump.sql nesse link: http://bit.ly/1BKZMgo Feito isso, tudo que precisamos fazer para importar esse dump em nosso banco de dados é executar a seguinte instrução no terminal: mysql -uroot estoque_laravel < CAMINHO_COMPLETO_PARA_O_DUMP Para facilitar o processo, você pode copiar o arquivo dump.sql para a pasta raiz de seu usuário, e executar apenas: mysql -uroot estoque_laravel < dump.sql Ótimo! Já podemos dar o próximo passo. 3.4 Configuração e conexão com MySQL Agora que já temos o banco instalado e configurado, nosso objetivo será estabelecer uma conexão com o MySQL. O Laravel torna essa tarefa bem fácil, basta adicionar as informações do nosso banco no arquivo config/database.php. Ele já vem pré-configurado no projeto, só precisamos mudar o trecho de código do MySQL, que deve estar assim: 27 3.4. Configuração e conexão com MySQL ’mysql’ => [ ’driver’ ’host’ ’database’ ’username’ ’password’ ’charset’ ’collation’ ’prefix’ ’strict’ ], => => => => => => => => => Casa do Código ’mysql’, env(’DB_HOST’, ’localhost’), env(’DB_DATABASE’, ’forge’), env(’DB_USERNAME’, ’forge’), env(’DB_PASSWORD’, ’’), ’utf8’, ’utf8_unicode_ci’, ’’, false, No campo database usaremos estoque_laravel, usuário root e senha vazia. Caso seu MySQL tenha um usuário ou senha diferente, basta substituir o valor desses campos. O trecho de configuração do MySQL no arquivo database.php ficará assim: ’mysql’ => [ ’driver’ ’host’ ’database’ ’username’ ’password’ ’charset’ ’collation’ ’prefix’ ’strict’ ], => => => => => => => => => ’mysql’, env(’DB_HOST’, ’localhost’), env(’DB_DATABASE’, ’estoque_laravel’), env(’DB_USERNAME’, ’root’), env(’DB_PASSWORD’, ’’), ’utf8’, ’utf8_unicode_ci’, ’’, false, Pronto, isso é tudo que precisamos configurar para acesso ao banco de dados. Vamos testar? Uma forma simples, porém bem manual, de rodar SQLs pelo Laravel é utilizando a classe DB, presente em Illuminate\Support\Facades\DB. Ela tem uma série de métodos para nos ajudar a realizar operações no banco de dados, como select, insert etc. Quer ver como é simples? Para buscar todos os registros da tabela produtos, basta utilizar o método select com a SQL de consulta: $produtos = DB::select(’select * from produtos’); 28 Casa do Código Capítulo 3. MVC e conexão com banco de dados Nosso controller ficará assim: <?php namespace estoque\Http\Controllers; use Illuminate\Support\Facades\DB; class ProdutoController extends Controller { public function lista(){ $produtos = DB::select(’select * from produtos’); return ’<h1>Listagem de produtos com Laravel</h1>’; } } Todos os valores dos produtos do banco de dados serão retornados em um array, que chamamos de $produtos. Vamos garantir que isso está funcionando? Podemos mudar o método lista para concatenar o nome e descrição de cada um dos produtos na string de resposta. O código ficará assim: <?php namespace estoque\Http\Controllers; use Illuminate\Support\Facades\DB; class ProdutoController extends Controller { public function lista(){ $html = ’<h1>Listagem de produtos com Laravel</h1>’; $html .= ’<ul>’; $produtos = DB::select(’select * from produtos’); foreach ($produtos as $p) { $html .= ’<li> Nome: ’. $p->nome .’, Descrição: ’. $p->descricao .’</li>’; } 29 3.5. Para saber mais: Environment Casa do Código $html .= ’</ul>’; return $html; } } Achou o código feio? Então somos dois! Mas não se preocupe com isso, logo ele será melhorado, o nosso objetivo agora é garantir que a conexão com o banco de dados foi estabelecida corretamente. Como já temos alguns produtos cadastrados pelo dump, ao acessar http://localhost:8000/produtos o resultado deverá ser parecido com: Fig. 3.6: Lista de produtos com registros do banco. Excelente, com quase nenhuma configuração já conseguimos nos conectar e executar consultas no banco de dados! 3.5 Para saber mais: Environment Você provavelmente percebeu que adicionamos as configurações de banco de dados como segundo parâmetro de um método env, diretamente no arquivo de database.php. ’database’ ’username’ ’password’ 30 => env(’DB_DATABASE’, ’estoque_laravel’), => env(’DB_USERNAME’, ’root’), => env(’DB_PASSWORD’, ’’), Casa do Código Capítulo 3. MVC e conexão com banco de dados Mas quando o projeto estiver pronto, não vamos querer usar esses dados em produção. Por uma questão de segurança, é importantíssimo que o banco de produção tenha uma senha, e que ela fique protegida. Além disso, não queremos que os desenvolvedores usem o banco de dados de produção para fazerem seus testes futuros. É aí que entra o método env. O Laravel possui um recurso conhecido como Environment, que nos possibilita definir um conjunto de configurações de podem mudar de acordo com o ambiente de execução, como o caso do banco de dados. Veja que seu projeto já possui um arquivo .env.example, presente no diretório raiz. Dentro dele você encontrará, entre outras opções, as linhas: DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret O método env, utilizado no arquivo database.php, primeiro procura pela configuração definida no arquivo de environment e, caso não encontre, utiliza o segundo valor que passamos. Em outras palavras, se quisermos que o Laravel passe a utilizar as configurações de produção em vez dos valores fixos passados para o arquivo database, tudo o que precisamos fazer é criar um arquivo .env com essas configurações. Se você estiver utilizando a versão mais nova do Laravel, ele já existirá. Basta adicionar o conteúdo: DB_HOST=meuprojeto.com.br DB_DATABASE=estoque_producao DB_USERNAME=production DB_PASSWORD=uma_senha_super_hiper_secreta Mas cuidado. Esse arquivo terá informações importantes de seu ambiente de produção, como a senha do banco de dados. É bastante recomendado que você não deixe esse arquivo no projeto ou commit no repositório, caso esteja utilizando alguma ferramenta de controle de versão como o git. Se quiser, você pode ver mais detalhes sobre essa funcionalidade em: http://laravel.com/docs/configuration#environment-configuration 31 3.6. Aonde chegamos e para onde queremos ir 3.6 Casa do Código Aonde chegamos e para onde queremos ir Com um pouco de teoria e prática, o significado do padrão MVC começa a fazer sentido. Como vimos, em vez de deixar todas as responsabilidades no arquivo de rotas, cada comportamento pode ficar muito bem encapsulado em seu devido controller. As vantagens são inúmeras, mas pra mim as principais são: • Legibilidade: pois em vez de ter várias funções anônimas e inúmeras linhas de código em um mesmo arquivo, tudo fica muito bem distribuído. Cada comportamento em seu devido lugar, em classes e métodos com nomes bem definidos. • Manutenibilidade: pois se tudo está bem organizado e encapsulado, não precisaremos mexer em um arquivo com 1500 linhas sempre que um problema aparecer. A listagem de produtos parou de funcionar? Sei que tenho que ir a um método do ProdutoController. Não consigo adicionar usuários? Sei que tenho que verificar na classe UsuarioController. Viu só? Isso nada mais é do que bom uso da orientação a objetos, que cada vez mais faz parte e influencia o PHP e seus frameworks. Mas espera, nosso código ainda não obedece o princípio das 3 camadas do MVC, por enquanto todo o trabalho está sendo feito na camada controller! Isso não é bom, vamos resolver? 32 Capítulo 4 Camada de modelo, migrations e seeds Até então usamos um dump do banco de dados para importar a tabela produtos e alguns dados prontos, mas, ao criar um novo projeto, nem sempre teremos essas informações prontas, não é? Neste capítulo veremos como o Laravel pode ajudar nesse trabalho, criando todas essas informações do zero, sem termos que nos preocupar em acessar o banco de dados e executar esses create tables e inserts na mão. 4.1 O modelo Produto Nosso primeiro passo será criar uma classe de modelo que represente nossa tabela de produtos do banco de dados. Algo bem simples, como: 4.1. O modelo Produto Casa do Código <?php namespace estoque; use Illuminate\Database\Eloquent\Model; class Produto extends Model { } Veja que nossa classe Produto precisa herdar de Model, mas não se preocupe com isso por enquanto, em breve entenderemos o motivo e ganhos dessa herança. Mas espera, acha trabalhoso digitar essas linhas pra criar a classe na mão? Que tal deixar o Artisan fazer isso pra você? Isso mesmo, o Artisan faz muito mais do que subir o servidor, ele tem uma série de recursos que exploraremos no decorrer do livro. Um deles é o make:model, que criará toda a estrutura da classe de modelo para você. Repare como é simples: php artisan make:model Produto Após executar esse comando, a classe será criada automaticamente: Fig. 4.1: Comando do Artisan para criação de modelos. Tudo pronto, procure pela classe Produto dentro da pasta app de seu projeto, ela estará assim: <?php namespace estoque; use Illuminate\Database\Eloquent\Model; 34 Casa do Código Capítulo 4. Camada de modelo, migrations e seeds class Produto extends Model { // } Legal, não é? Até nisso o Laravel nos ajuda. O comando php artisan make:model é opcional, claro, mas muito útil em nosso dia a dia. Você só precisa passar o nome da classe e todo o código padrão será criado para você, assim em nenhum momento precisaremos nos preocupar em lembrar de herdar de Model, adicionar o import e namespace dos nossos modelos. A partir de agora, a classe Produto representará a tabela produtos que será criada no banco de dados. Mas em qual lugar isso foi configurado? A resposta é: em nenhum. Quando não definimos explicitamente, o framework assume que a tabela terá o nome da classe com letra minúscula e no plural. Nesse caso, a classe Produto será vinculada à tabela produtos. Mas se você preferir, ou mesmo precisar, também é possível configurar isso explicitamente. Basta adicionar em nosso modelo uma propriedade com visibilidade protected, chamada table: <?php namespace estoque; use Illuminate\Database\Eloquent\Model; class Produto extends Model { protected $table = ’produtos’; } 4.2 Trabalhando com migrations Se você estiver utilizando a versão 5.0 do Laravel, além da classe Produto, um arquivo de migração (migration) chamado 2015_05_25_151007_create_produto_table foi criado. A partir da versão 5.1, ele não é criado automaticamente; logo, você precisará executar o comando com o argumento -m: php artisan make:model Produto -m 35 4.2. Trabalhando com migrations Casa do Código Esse arquivo será o responsável por ensinar ao Laravel como criar, atualizar ou recuperar o estado anterior do esquema de seu banco de dados. Migrations, ou migrações, trabalham como um controle de versão do seu banco de dados. Veja que, inclusive, o nome do arquivo de migração começa com a data em que ela foi criada, pois assim o Laravel saberá exatamente a ordem em que elas devem ser executadas. Ao abrir esse arquivo de migração, que por padrão será criado na pasta database/migrations, veremos que uma classe CreateProdutosTable foi criada com o seguinte conteúdo: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProdutosTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create(’produtos’, function(Blueprint $table) { $table->increments(’id’); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { 36 Casa do Código Capítulo 4. Camada de modelo, migrations e seeds Schema::drop(’produtos’); } } Essa classe, que herda de Migration, terá dois métodos importantíssimos. O primeiro ( up) ensina como criar a tabela produto, enquanto o segundo ( down) mostra como desfazê-la, ou seja, fazer um rollback. Porém, repare que, no método up, apenas o id e timestamp da tabela são definidos. Queremos que, além disso, um produto tenha nome, valor, descricao e quantidade, portanto adicionaremos essas as informações a seguir: public function up() { Schema::create(’produtos’, function(Blueprint $table) { $table->increments(’id’); $table->string(’nome’); $table->decimal(’valor’, 5, 2); $table->string(’descricao’); $table->integer(’quantidade’); $table->timestamps(); }); } Utilizamos o método string para os campos de texto, integer para a quantidade e decimal para o valor do produto. Existem diversas outras opções, como date para datas, time para hora, entre outros. Você pode ver uma relação completa dos métodos desse builder em: http://laravel.com/docs/schema#adding-columns Com isso, nossa migração está pronta, o método up fornece todas as informações necessárias para que o Laravel saiba criar a tabela produtos, e o método down ensina ao framework como desfazer essa alteração, que nesse caso será apagando a tabela: public function down() { Schema::drop(’produtos’); } 37 4.3. Apagando a tabela produtos 4.3 Casa do Código Apagando a tabela produtos Agora que já temos uma migração para fazer esse trabalho, vamos apagar a tabela de produtos já existente em nosso banco de dados. Para isso, basta acessar o MySQL e executar a instrução: DROP TABLE produtos; Tudo pronto. Acessar a listagem de produtos pelo navegador agora resultaria em um erro, claro. Para que o código volte a funcionar precisamos executar a migração de criação da tabela. Mas como? 4.4 Executando migrações Executar uma migration é uma tarefa extremamente simples. O comando php artisan migrate é quem fará todo o trabalho. Ao executá-lo, teremos como resultado algo como: Migration Migrated: Migrated: Migrated: table created successfully. 2014_10_12_000000_create_users_table 2014_10_12_100000_create_password_resets_table 2015_05_25_151007_create_produtos_table Veja que, além de nossa migração de produtos, uma migração de criação de usuários e reset de senha foi executada. Essas migrações foram criadas automaticamente junto com o projeto, para a funcionalidade de autenticação que veremos mais à frente. Vamos conferir o resultado? Basta acessar o banco de dados do projeto e conferir a estrutura da tabela que foi criada. Podemos fazer isso com o comando desc do MySQL. mysql> desc produtos; O resultado será: 38 Casa do Código Capítulo 4. Camada de modelo, migrations e seeds Fig. 4.2: Estrutura da tabela produtos no banco de dados. Excelente, já temos a tabela produtos com tudo que precisamos. E quanto ao rollback? Eventualmente surgirá a necessidade de desfazer uma migração, seja por um erro de digitação em algum de seus campos, adicionar alguma informação ou algo do tipo. Se você acabou de executar o comando migrate e viu que alguma coisa não saiu como esperado, basta executar um php artisan migrate:rollback que a ultima migração será desfeita. Legal, não é? Mas cuidado. Muitas vezes será mais interessante criar uma nova migração com as mudanças, em vez de sair desfazendo as últimas migrações executadas. Lembre-se que, caso alguma informação já tenha sido inserida na tabela, ao fazer rollback ela será perdida. Você pode criar quantas migrações quiser com o comando: php artisan make:migration NOME_DA_MIGRATION 4.5 Inserindo dados na tabela produtos O código voltará a funcionar, porém, ao acessar a listagem de produtos novamente pelo navegador, nenhum dado será exibido. É o esperado, afinal, apagamos a tabela de produtos anterior e criamos uma nova. 39 4.5. Inserindo dados na tabela produtos Casa do Código Em breve implementaremos a função de adicionar novos produtos, mas enquanto isso podemos inserir alguns produtos manualmente no banco de dados. Uma forma simples de fazer isso seria acessando o MySQL e executando diretamente os inserts, mas veremos uma forma ainda mais interessante e automatizada, com um novo recurso do Laravel chamado Seed. A ideia é simples, um seeder é uma classe que popula seu banco de dados com algumas informações iniciais normalmente informações de testes. Toda a mágica acontece dentro de uma classe chamada DatabaseSeeder, presente no diretório database/seeds. Quando fizemos um novo projeto, essa classe foi criada com o seguinte conteúdo: <?php use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Model::unguard(); //$this->call(’UserTableSeeder’); } } Note que há uma linha comentada com o exemplo de como executar um seeder, com auxílio do método call. Criando o ProdutoTableSeeder Vamos agora criar um seeder que deverá inserir alguns dados na tabela produtos. A princípio o código pode parecer estranho, mas é 40 Casa do Código Capítulo 4. Camada de modelo, migrations e seeds verdadeiramente simples. Assim como o DatabaseSeeder, nosso ProdutoTableSeeder deve herdar da classe Seeder e implementar seu método run, como a seguir: class ProdutoTableSeeder extends Seeder { public function run() { // código vai aqui } } Nada demais, não é? Como por padrão o DatabaseSeeder não vem com namespace, é uma prática muito comum definirmos os seeders dentro de um mesmo arquivo. Portanto, o código ficará assim: <?php use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Model::unguard(); //$this->call(’UserTableSeeder’); } } class ProdutoTableSeeder extends Seeder { public function run() 41 4.5. Inserindo dados na tabela produtos Casa do Código { // código vai aqui } } Ótimo, agora só falta adicionarmos alguns dados! Assim como usamos o DB:select para fazer um select no banco de dados, podemos usar um DB:insert para fazer uma inserção. O código ficará assim: class ProdutoTableSeeder extends Seeder { public function run() { DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array(’Geladeira’, 2, 5900.00, ’Side by Side com gelo na porta’)); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array(’Fogão’, 5, 950.00, ’Painel automático e forno elétrico’)); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array(’Microondas’, 1, 1520.00, ’Manda SMS quando termina de esquentar’)); } } Não se preocupe em entender o DB:insert por enquanto, voltaremos a falar sobre esse método muito em breve. O que importa nesse momento é que, em vez de executarmos 3 inserts no banco de dados manualmente, deixamos esse código isolado em uma classe responsável por fazer esse trabalho para nós. O ganho é que, em qualquer ambiente, podemos executar esse seed 42 Casa do Código Capítulo 4. Camada de modelo, migrations e seeds para inserir alguns dados na tabela de produtos sempre que quisermos. Legal, não é? Só faltam dois detalhes para que o código fique completo. O primeiro é adicionar o import da classe DB, assim como fizemos no controller de produtos: use Illuminate\Support\Facades\DB; O outro detalhe será adicionar a chamada do ProdutoTableSeeder dentro do método run da classe DatabaseSeeder. O código completo ficará assim: <?php use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Model::unguard(); $this->call(’ProdutoTableSeeder’); } } class ProdutoTableSeeder extends Seeder { public function run() { DB::insert(’insert into produtos (nome, quantidade, valor, descricao) 43 4.5. Inserindo dados na tabela produtos Casa do Código values (?,?,?,?)’, array(’Geladeira’, 2, 5900.00, ’Side by Side com gelo na porta’)); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array(’Fogão’, 5, 950.00, ’Painel automático e forno elétrico’)); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array(’Microondas’, 1, 1520.00, ’Manda SMS quando termina de esquentar’)); } } Excelente. Vamos testar? Podemos utilizar o comando db:seed do Artisan sempre que quisermos executar o DatabaseSeeder. Basta acessar o terminal e executar: php artisan db:seed Tudo pronto! A mensagem Seeded: ProdutoTableSeeder será impressa e os produtos estarão salvos no banco de dados. Acesse a listagem novamente para conferir. 44 Capítulo 5 Trabalhando com a View Sabemos que nosso controller está com muitas responsabilidades, mas por onde começar a melhorá-lo? Atualmente, o código está assim: <?php namespace estoque\Http\Controllers; use Illuminate\Support\Facades\DB; class ProdutoController extends Controller { public function lista(){ $html = ’<h1>Listagem de produtos com Laravel</h1>’; $html .= ’<ul>’; 5.1. Cada coisa em seu lugar: camada View Casa do Código $produtos = DB::select(’select * from produtos’); foreach ($produtos as $p) { $html .= ’<li> Nome: ’. $p->nome .’, Descrição: ’. $p->descricao .’</li>’; } $html .= ’</ul>’; return $html; } } Isso não é legal por uma infinidade de motivos, por exemplo, como faríamos para estilizar esse HTML? E se eu quisesse adicionar uma imagem, uma tabela, o header, título etc., tudo isso fica mesmo dentro do código do meu controller? 5.1 Cada coisa em seu lugar: camada View Em vez de retornar um HTML, o controller deveria delegar esse trabalho para a camada de view, que é a especialista do MVC nesse assunto. A mudança será simples, basta criar um arquivo PHP com toda essa lógica de visualização. Podemos chamá-lo de listagem.php, e é importante que ele esteja no diretório resources/views. 46 Casa do Código Capítulo 5. Trabalhando com a View Fig. 5.1: Arquivo /estoque/resources/views/listagem.php Nesse arquivo podemos fazer algo muito parecido com o que estamos fazendo no controller, um foreach em todos os produtos, mostrando seu nome, descrição, e agora que estamos no lugar certo, também podemos exibir os outros campos, que são o valor e a quantidade. Em vez de uma lista, podemos usar uma tabela para organizar um pouco melhor os dados. O código poderá ficar parecido com: <html> <body> <h1>Listagem de produtos</h1> <table> <?php foreach ($produtos as $p): ?> <tr> <td><? = $p->nome ?> </td> <td><? = $p->valor ?> </td> <td><? = $p->descricao ?> </td> <td><? = $p->quantidade ?> </td> </tr> <?php endforeach ?> </table> 47 5.1. Cada coisa em seu lugar: camada View Casa do Código </body> </html> Agora que temos o arquivo pronto, só precisamos ensinar ao controller que, em vez de escrever o HTML todo, ele deve simplesmente delegar o trabalho para essa nossa view. Mas como fazer isso? Vamos por partes, antes de mais nada, vamos tirar todo aquele código HTML do método lista. Ele ficar assim: class ProdutoController extends Controller { public function lista(){ $produtos = DB::select(’select * from produtos’); return // o que retornar aqui? } } Ufa, bem melhor, não acha? Tiramos toda a lógica de visualização de nosso controller, já que temos uma camada própria para fazer esse trabalho, a view. O próximo passo é ensinar ao controller que, após fazer seu trabalho, ele deve renderizar o HTML da view listagem.php que acabamos de criar. Para fazer isso, basta mudar o retorno do método para view(‘listagem’), como a seguir: class ProdutoController extends Controller { public function lista(){ $produtos = DB::select(’select * from produtos’); return view(’listagem’); } } Esse método recebe como parâmetro o nome da página que você quer exibir, sem a extensão, e para utilizá-lo você não precisa fazer nenhum import, já que isso é feito pela classe Controller que herdamos. 48 Casa do Código Capítulo 5. Trabalhando com a View Vamos ver se tudo está funcionando? Basta acessar http://localhost:8000/ produtos novamente. Diferente do esperado, o resultado será: Fig. 5.2: Página de listagem com mensagem de erro. Ops, alguma coisa deu errado, mas como descobrir? A mensagem do navegador não nos diz muito. 5.2 Consultando os arquivos de log do framework Sempre que você receber uma mensagem como essa, dizendo que alguma coisa deu errado em seu código, você pode e deve consultar os arquivos de log da sua aplicação para ter uma descrição mais detalhada sobre o que está acontecendo. Esses arquivos ficam no diretório /storage/logs/ e são nomeados por data. 49 5.2. Consultando os arquivos de log do framework Casa do Código Fig. 5.3: Arquivo de log da aplicação na data atual. Ao abrir o log da data e hora atual, podemos ver logo no início do arquivo a seguinte mensagem de erro: [2015-02-25 22:07:50] production.ERROR: exception ’ErrorException’ with message ’Undefined variable: produtos’ in /Users/Turini/Desktop/estoque/resources /views/listagem.php:9 Veja a mensagem: Undefined variable: produtos. Faz todo sentido, não é? Afinal, se criamos a variável produtos no controller, não podemos acessá-la na view, que é um outro arquivo. Há ainda uma forma mais interessante de consultar os errors da nossa aplicação quando estamos em ambiente de desenvolvimento, sem precisar a todo o momento procurar a mensagem nos arquivos de log. É fácil, basta criar um arquivo chamado .env dentro da pasta do projeto, e dentro dele colocar as instruções: APP_DEBUG=true 50 Casa do Código Capítulo 5. Trabalhando com a View O seu arquivo pode ficar assim: Fig. 5.4: Arquivo .env com debug ativado. Com isso, estamos informando ao Laravel que queremos depurar as mensagens de erro, o que é bastante recomendado quando estamos desenvolvendo a aplicação. Faça a alteração e acesse a página novamente para ver o resultado: Fig. 5.5: Listagem com erro em modo debug. Excelente! Assim fica muito mais fácil identificar o que aconteceu de errado, não é? 51 5.3. Incluindo parâmetros na view 5.3 Casa do Código Incluindo parâmetros na view O que faltou foi, de alguma forma, mandar esse array de produtos para a view. Poderíamos, sim, criar o array diretamente no arquivo listagem.php da view, mas não estaríamos respeitando a divisão de responsabilidades do MVC. A view é responsável pela lógica de visualização, não pelo acesso ao banco de dados nem nada desse tipo. Uma forma muito mais interessante de fazer isso é, no retorno do controller, ensinar ao Laravel que queremos deixar essa variável produtos acessível pela view com auxilio do método with: $produtos = DB::select(’select * from produtos’); view(’listagem’)->with(’produtos’, $produtos); Veja que passamos para o método with uma chave e valor, ou seja, a forma como ele será acessado na view (produtos, nesse caso) e o valor associado a ele. O código completo fica assim: <?php namespace estoque\Http\Controllers; use Illuminate\Support\Facades\DB; use estoque\Produto; class ProdutoController extends Controller { public function lista(){ $produtos = DB::select(’select * from produtos’); return view(’listagem’)->with(’produtos’, $produtos); } } Já podemos testar. Acessando a listagem novamente teremos como resultado: 52 Casa do Código Capítulo 5. Trabalhando com a View Fig. 5.6: Listagem de produtos. Pronto, tudo continua funcionando e agora nosso código está muito mais bem organizado. O controller faz seu trabalho e ao final delega a responsabilidade de apresentação do resultado para a view, que é a camada que cuida disso. Bem justo, não é? 5.4 Alguns detalhes e curiosidades Esse método view, que usamos no retorno do nosso controller, é um helper method para simplificar esse processo de trabalhar com views. Se você já viu algum código com as versões anteriores do Laravel, vai se lembrar que para fazer o mesmo trabalho tínhamos que escrever: View::make(’listagem’)->with(’produtos’, $produtos); No fim das contas, o que acontece é que, ao chamar o método view, sem passar nenhum parâmetro, ele vai retornar uma implementação do contrato View definido pelo Laravel. Chamando o método com parâmetro, ele delegará a chamada para o método make, assim como era feito anteriormente só que com uma sintaxe um pouco mais enxuta e agradável. Incluindo parâmetros com magic methods Uma curiosidade é que, em vez de escrever: 53 5.4. Alguns detalhes e curiosidades Casa do Código view(’listagem’)->with(’produtos’, $produtos); Você pode chamar um método withProdutos: view(’listagem’)->withProdutos($produtos); Faça a alteração para testar, o resultado será o mesmo! Como isso é possível? Na verdade, esse método “não existe”. Em nenhum momento os desenvolvedores escreveram um método withProdutos dentro da view, mas sim um magic method que faz esse trabalho dinamicamente. Em outras palavras, se você chamou withAlgumaCoisa, ele usará o algumaCoisa como chave do parâmetro disponibilizado na view. Passando um array de dados para a view Existem diversas outras formas de passar os dados para a view, além do método with. Uma das mais conhecidas e utilizadas é passando um array como segundo parâmetro do método view. Em vez de fazer: view(’listagem’)->with(’produtos’, $produtos); Você faria algo como: return view(’listagem’, [’produtos’ => $produtos]); Ou extraindo o array para uma variável: $data = [’produtos’ => $produtos]; return view(’listagem’, $data); Ou ainda criando o array e adicionando cada item manualmente: $data = []; $data[’produtos’] = $produtos; return view(’listagem’, $data); O que vai mudar é a sintaxe, mas o efeito será o mesmo. Você pode escolher a forma que preferir. 54 Casa do Código Capítulo 5. Trabalhando com a View Mais alguns métodos: exists e file Outro ponto é que você pode verificar a existência de uma view com o método exists: if (view()->exists(’listagem’)) { return view(’listagem’); } Ou mesmo usar o método file para gerar a view a partir de um caminho/diretório diferente: view()->file(’/caminho/para/sua/view’); Legal, não é? Se quiser conhecer um pouco mais sobre a view, eu recomendo que dê uma boa olhada em sua documentação. O link é: http://laravel.com/docs/views 5.5 Melhorando a aparência da nossa listagem Agora que nosso HTML está no lugar certo, que acha de melhorar um pouco o visual da listagem? O Laravel já traz um arquivo chamado app.css com alguns estilos prontos, para ajudá-lo a poupar tempo com esse trabalho também. Esse arquivo usa internamente o conhecido Bootstrap (http: //getbootstrap.com) para o estilo, Glyphicons (http://glyphicons.com/) para os ícones e muito mais. 55 5.5. Melhorando a aparência da nossa listagem Casa do Código Não encontrou esse arquivo de CSS? Não deixe de verificar se o arquivo app.css está presente no diretório /public/css do seu projeto. Se não estiver, significa que você está utilizando Laravel na versão 5.1 ou maior. Desde o 5.1, por uma decisão interna dos desenvolvedores do framework, esse e alguns outros arquivos foram deixados como opcionais. Para adicioná-los em seu projeto, basta instalar o pacote scafold: https://github.com/bestmomo/scafold. Se preferir, você também pode copiar os arquivos do projeto que desenvolvemos durante esse livro, no repositório: https://github.com/ Turini/estoque-laravel. Para começar a usar esse estilo, basta adicionar o head no seu HTML e o link com o caminho do arquivo, algo como: <html> <head> <link href="/css/app.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <!-- continuação do seu html aqui --> Note que já aproveitamos para colocar um título na página também. Ao acessar a listagem você já deve perceber alguma diferença, mas por enquanto, só na fonte: 56 Casa do Código Capítulo 5. Trabalhando com a View Fig. 5.7: Listagem de produtos com css e título. Ainda queremos estilizar a tabela, mas como? É bem simples, basta adicionar a classe table que você já vai ter algum resultado. O HTML da tabela deve ficar assim: <table class="table"> <?php foreach ($produtos as $p): ?> <tr> <!-- código omitido --> </tr> <?php endforeach ?> </table> Essa simples mudança já deixa nossa listagem com um visual bem mais agradável: 57 5.5. Melhorando a aparência da nossa listagem Casa do Código Fig. 5.8: Utilizando classes do bootstrap na tabela. Além da classe table, também podemos usar: • table-striped para zebrar as linhas • table-bordered para adicionar bordas • table-hover para efeito de hover Há diversos outros, que você pode encontrar na documentação do bootstrap: http://getbootstrap.com/css/#tables Vamos usar alguns desses estilos? Basta adicionar as novas classes na tabela: <table class="table table-striped table-bordered table-hover"> <!-- restante do html omitido --> Você pode usar qualquer outro estilo que preferir. Além disso, para desgrudar a tabela e o título das bordas da página, podemos envolvê-las em uma div com a classe container. O arquivo listagem.php completo pode ficar assim: 58 Casa do Código Capítulo 5. Trabalhando com a View <html> <head> <link href="/css/app.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <div class="container"> <h1>Listagem de produtos</h1> <table class="table table-striped table-bordered table-hover"> <?php foreach ($produtos as $p): ?> <tr> <td><? = $p->nome ?> </td> <td><? = $p->valor ?> </td> <td><? = $p->descricao ?> </td> <td><? = $p->quantidade ?> </td> </tr> <?php endforeach ?> </table> </div> </body> </html> O resultado final será: 59 5.6. Aonde chegamos e para onde queremos ir Casa do Código Fig. 5.9: Aparência final da listagem de produtos. 5.6 Aonde chegamos e para onde queremos ir Neste capítulo demos um grande passo, que foi separar a lógica de apresentação das regras de negócio do controller. Vimos que os ganhos são muitos, já que agora cada coisa está em seu efetivo lugar. Além disso, conhecemos o helper method view e vimos sobre as diferentes variações para inclusão de parâmetros utilizando, ou não, o método with. Pronto para dar mais alguns passos? 60 Capítulo 6 Parâmetros da request e URL Agora que temos nosso código um pouco mais organizado, com a lógica de acesso ao banco no controller separada da lógica de apresentação na view, podemos adicionar um link em cada produto da tabela para que seja possível visualizar seus detalhes em uma nova tela. 6.1 Exibindo detalhes do produto A mudança no HTML do arquivo listagem.php será pequena, só precisamos adicionar uma nova coluna com um link: <h1>Listagem de produtos</h1> <table class="table ..."> <?php foreach ($produtos as $p): ?> <tr> <td><? = $p->nome ?> </td> 6.1. Exibindo detalhes do produto Casa do Código <!-- outras colunas omitidas --> <td> <a href="/produtos/mostra>"> Visualizar </a> </td> </tr> <?php endforeach ?> </table> Fig. 6.1: Listagem com link de visualizar detalhes. Ou, para deixar a aparência um pouco mais interessante, podemos usar um ícone de lupa no lugar desse texto. Como estamos usando bootstrap, que já vem com o framework, só precisamos adicionar um span com a classe do ícone que queremos utilizar. Veja como é simples: <a href="/produtos/mostra"> <span class="glyphicon glyphicon-search"></span> </a> Pronto, com isso nossa listagem ficou assim: 62 Casa do Código Capítulo 6. Parâmetros da request e URL Fig. 6.2: Listagem com ícone de lupa no link. Criando uma nova rota Veja que o link já aponta para a URL /produtos/mostra, que ainda não existe em nosso sistema. Vamos criá-la? Só precisamos acessar o arquivo routes.php e, da mesma forma como fizemos para a listagem, vincular essa URL a um método de nosso controller. O código ficará assim: Route::get(’/produtos/mostra’, ’ProdutoController@mostra’); Agora, no ProdutoController, criamos o método mostra: public function mostra(){ // retorna uma view com os detalhes } Você já deve imaginar qual o próximo passo, não é? Queremos retornar uma view com os dados do produto, portanto, vamos criar um novo arquivo chamado detalhes.php, por enquanto em branco, dentro da pasta /resources/views/. Em nosso controller, precisamos adicionar o retorno do método mostra, que deve renderizar essa nova view de detalhes do produto: 63 6.1. Exibindo detalhes do produto Casa do Código public function mostra(){ return view(’detalhes’); } Até aqui nada novo, certo? Mas agora entraremos em um ponto interessante. A view, neste caso detalhes.php, precisa das informações do produto clicado para exibi-las em seu HTML. Algo muito parecido com o que fizemos na listagem, porém em vez de fazer um forEach na lista de produtos, teremos só um: <h1>Detalhes do produto: <? = $p->nome ?> </h1> <ul> <li> <b>Valor:</b> R$ <? = $p->valor ?> </li> <li> <b>Descrição:</b> <? = $p->descricao ?> </li> <li> <b>Quantidade em estoque:</b> <? = $p->quantidade ?> </li> </ul> Veja que utilizei um ul do HTML pra exibir as informações em formato de lista, mas fique à vontade para montar o HTML da forma que preferir. Utilizando a mesma estrutura da nossa view de listagem, incluindo header com css, título etc., nosso arquivo detalhes.php completo pode ficar assim: <html> <head> <link href="/css/app.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <div class="container"> <h1>Detalhes do produto: <? = $p->nome ?> </h1> <ul> 64 Casa do Código Capítulo 6. Parâmetros da request e URL <li> <b>Valor:</b> R$ <? = $p->valor ?> </li> <li> <b>Descrição:</b> <? = $p->descricao ?> </li> <li> <b>Quantidade em estoque:</b> <? = $p->quantidade ?> </li> </ul> </div> </body> </html> Agora, em nosso controller, precisaremos deixar a variável $p acessível na view. Não tem erro, afinal, já fizemos isso na listagem também! Só precisamos buscar o elemento do banco de dados e depois usar o método with para disponibilizá-lo no detalhes.php. Algo como: public function mostra(){ $resposta = // busca do banco return view(’detalhes’)->with(’p’, $resposta); } A busca será um select normal, que pode ser feito com o DB:select que já conhecemos, mas diferente de quando queremos listar todos os elementos, agora precisaremos passar o id do produto como parâmetro. Ele deve ficar assim: $id = 1; // precisamos pegar o id de alguma forma $resposta = DB::select(’select * from produtos where id = ?’,[$id]); Por enquanto, estamos passando o número 1 como id fixo, logo resolveremos isso. Veja que usamos um sinal de interrogação para mostrar o local onde o parâmetro passado no segundo argumento do método select (que é um array) deve ser aplicado. 65 6.1. Exibindo detalhes do produto Casa do Código Não vamos focar no SQL... agora Não se preocupe em entender a fundo o SQL e sintaxe do DB:select agora, pois teremos um capítulo dedicado a isso e também veremos uma forma bem mais interessante de fazer esse tipo de consulta sem nos preocuparmos tanto em entender esses detalhes. Outra informação importante é que o método select sempre retorna um array, que neste caso pode: • ter 1 elemento, caso seja encontrado; • ser vazio, caso contrário. Precisamos portanto verificar se o array de resposta é vazio, mostrando uma mensagem de erro, ou caso contrário incluir o elemento da primeira posição na view. Vale lembrar que a primeira posição de um array é o índice zero, portanto o código fica assim: public function mostra(){ $id = 1; // precisamos pegar o id de alguma forma $resposta = DB::select(’select * from produtos where id = ?’, [$id]); if(empty($resposta)) { return "Esse produto não existe"; } return view(’detalhes’)->with(’p’, $resposta[0]); } Vamos testar? Basta clicar na lupa de detalhes de qualquer produto da listagem, o resultado deverá ser parecido com: 66 Casa do Código Capítulo 6. Parâmetros da request e URL Fig. 6.3: Página de detalhes, porém sempre com o mesmo produto. Excelente, já sabemos que nosso controller e view estão funcionando como esperado, o SQL foi executado, o elemento com id 1 listado e o HTML foi exibido corretamente. Mas claro, independente do produto que eu clicar, sempre estamos mostrando os dados do produto com id 1, que no meu caso é a Geladeira. Como resolver? 6.2 Enviando parâmetros na request Como saber exatamente qual o id do produto que foi clicado no HTML? Uma forma bem tradicional de fazer isso seria passando o id do produto como um parâmetro na requisição, para isso bastaria mudar o link da listagem como a seguir: <a href="/produtos/mostra?id=<? = $p->id ?> "> <span class="glyphicon glyphicon-search"></span> </a> Repare que logo após a URL adicionamos um ?id=<?= $p->id ?>, ou seja, estamos passando um parâmetro na requisição chamado id, com o valor do id do produto. Fazendo a alteração, você consegue verificar no códigofonte da listagem que os valores são substituídos e os links ficam assim: 67 6.3. Recuperando parâmetros da request Casa do Código Fig. 6.4: Código-fonte da listagem de produtos. 6.3 Recuperando parâmetros da request Agora que o parâmetro id está sendo enviado na requisição, podemos recuperar seu respectivo valor em nosso controller com uso da interface Request. Por sinal, esse tipo de interface estática é conhecida como facade. Elas nos oferecem uma forma simples de acessar algumas das classes fundamentais do framework. Se quiser, você pode ler mais sobre esses facades em: http://laravel.com/docs/facades A interface Request tem métodos que nos auxiliam com esse trabalho, como o input, em que você passa a chave (name) do parâmetro que foi enviado na requisição e ele retorna o valor, ou null caso não seja encontrado. Portanto, podemos fazer algo como: 68 Casa do Código Capítulo 6. Parâmetros da request e URL $id = Request::input(’id’); Simples, não é? Você ainda pode passar um valor default, caso o parâmetro não seja encontrado. Um exemplo seria: $id = Request::input(’id’, ’0’); Agora o $id terá o valor passado, ou zero, caso nenhum valor de id tenha sido enviado na requisição. Vamos fazer essa alteração? Nosso método mostra pode ficar assim: public function mostra(){ $id = Request::input(’id’, ’0’); $resposta = DB::select(’select * from produtos where id = ?’, [$id]); if(empty($resposta)) { return "Esse produto não existe"; } return view(’detalhes’)->with(’p’, $resposta[0]); } Vale lembrar que, como estamos em um namespace diferente, temos que importar o Request no início de nosso controller, assim como fizemos com a interface DB: <?php namespace estoque\Http\Controllers; use Illuminate\Support\Facades\DB; use Request; class ProdutoController extends Controller { // ... Tudo pronto, podemos voltar para o navegador e acessar o link de detalhes novamente que, desta vez, os detalhes do produto correto serão exibidos: 69 6.3. Recuperando parâmetros da request Casa do Código Fig. 6.5: Página de detalhes com parâmetro id. Veja que dessa vez eu selecionei o fogão, que tem id 2, e seus dados foram exibidos conforme esperado. Também podemos testar com um id que não existe, mudando o final da URL pra 10, por exemplo: Fig. 6.6: Página de detalhes com erro de produto inexistente. Excelente, a mensagem de erro do nosso if foi exibida como planejado. 70 Casa do Código Capítulo 6. Parâmetros da request e URL 6.4 Conhecendo um pouco mais da Request Vimos como é simples recuperar os parâmetros da requisição com o método input, passando ou não um valor default. Mas além dele, a Request tem uma variedade bem grande de métodos que nos ajudam em trabalhos como esse. Por exemplo, quer saber se existe um parâmetro específico na requisição? Que tal fazer: if (Request::has(’id’)) { // faz alguma coisa } Há ainda o método all, que retorna um array com todos os parâmetros da requisição, os métodos only e except, com que você pode restringir quais parâmetros quer listar. // lista todos os params $input = Request::all(); // apenas nome e id $input = Request::only(’nome’, ’id’); // todos os params, menos o id $input = Request::except(’id’); Há também métodos como url, que retorna a URL da request atual, ou o path que retorna a URI. Por exemplo, em uma requisição para o método mostra, ao fazer: $url = Request::url(); O valor da $url seria http://localhost:8000/produtos/mostra, mas já no caso do path: $uri = Request::path(); O valor da $uri seria produtos/mostra. 71 6.5. URLs mais semânticas Casa do Código Vamos usar mais alguns desses métodos no decorrer dos capítulos, mas se desde já quiser conhecer um pouco mais e dominar as possibilidades da interface Request, você pode dar uma olhada na sua documentação em: http://laravel.com/docs/requests 6.5 URLs mais semânticas Nossa estratégia atual de exibir o produto já resolve o problema, mas há uma alternativa bastante elegante que é adotada em situações como a nossa, onde queremos exibir detalhes de um recurso específico de nosso projeto. A ideia é que, em vez de passar o parâmetro pela request, dessa forma: /produtos/mostra?id=2 você use o id como parte de sua rota, tendo uma URL mais semântica: /produtos/mostra/2 Essas duas alternativas são conhecidas como parâmetro de busca (query param) e parâmetro de rota (path param), respectivamente. Normalmente a segunda estratégia é utilizada quando o parâmetro é obrigatório, como é o nosso caso, mas pode depender bastante do gosto do desenvolvedor. Que tal aplicarmos essa alteração em nosso código? Não vai dar muito trabalho. Podemos começar pelo link, como de costume. No arquivo de listagem, em vez de fazer: <a href="/produtos/mostra?id=<? = $p->id ?> "> <span class="glyphicon glyphicon-search"></span> </a> Vamos mudar para: <a href="/produtos/mostra/<? = $p->id ?> "> <span class="glyphicon glyphicon-search"></span> </a> Veja que só tiramos o ?id= e adicionamos uma barra no lugar. Simples, não é? Mas a alteração vai ser um pouco maior em nosso arquivo de rotas, o routes.php. Atualmente, a rota está assim: 72 Casa do Código Capítulo 6. Parâmetros da request e URL Route::get(’/produtos/mostra’, ’ProdutoController@mostra’); Mas, na verdade, agora queremos algo como: Route::get(’/produtos/mostra/1’, ’ProdutoController@mostra’); Bem, já sabe o problema, não é? O id não pode ser fixo, se não a URI sempre seria /produtos/mostra/1. O id precisa ser uma variável! Veja como isso é feito: Route::get(’/produtos/mostra/{id}’, ’ProdutoController@mostra’); É só isso, basta envolvermos o nome do parâmetro em um par de chaves e pronto! A rota já está feita. E agora, o que falta? Vamos testar para ver se está tudo ok? Basta abrir a listagem e clicar no ícone de detalhes de qualquer produto. Fig. 6.7: Produto não encontrado, mesmo com id correto. Ops, o produto não existe! Isso está acontecendo com todos os produtos, o que fizemos de errado? 6.6 Recuperando parâmetros da URL O problema está no método do controller, que atualmente usa o Request:input para recuperar o valor do id: public function mostra(){ 73 6.6. Recuperando parâmetros da URL Casa do Código $id = Request::input(’id’, ’0’); $resposta = DB::select(’select * from produtos where id = ?’, [$id]); if(empty($resposta)) { return "Esse produto não existe"; } return view(’detalhes’)->with(’p’, $resposta[0]); } Como você já deve estar imaginando, o método input não busca parâmetros da URL, como é o caso do nosso id agora. No lugar disso, experimente mudar para: $id = Request::route(’id’); Agora sim, o resultado será o esperado! Fig. 6.8: Detalhes do produto com path param. Mas espera, e quanto ao valor default? Quando estávamos usando o método input, nosso código estava assim: $id = Request::input(’id’, ’0’); 74 Casa do Código Capítulo 6. Parâmetros da request e URL Por que não fizemos o mesmo com o route? A resposta é simples: o {id} da rota agora é obrigatório! Se não passar, não entra no método e pontofinal. Se você quiser que o id do final da url seja opcional, você precisará deixar isso explícito no momento de registrar sua rota. Route::get(’/produtos/mostra/{id?}’, ’ProdutoController@mostra’); Repare que há uma ? após o id indicando que ele é opcional. Mas, claro, em nosso caso não queremos isso. O id sempre precisará ser passado. Isso não é tudo, o Laravel nos oferece uma forma ainda mais legal de recuperar parâmetros da URL. Quer ver como? Basta adicionar um argumento com o mesmo nome do parâmetro na assinatura do seu método, ele vai ser populado como um passe de mágica. Veja como fica o método do controller: public function mostra($id){ $resposta = DB::select(’select * from produtos where id = ?’, [$id]); if(empty($resposta)) { return "Esse produto não existe"; } return view(’detalhes’)->with(’p’, $resposta[0]); } Note que em nenhum momento precisamos usar a Request, o framework faz esse trabalho por nós. Abra a página novamente para testar, o resultado será o mesmo: 75 6.7. Alguns cuidados necessários Casa do Código Fig. 6.9: Página de detalhes do produto. 6.7 Alguns cuidados necessários Quando estamos usando parâmetros na URL, sempre precisamos nos atentar a alguns detalhes. Por exemplo, em algum momento falamos que o id precisaria ser um número? Não. Isso significa que eu consigo acessar o método mostra pela seguinte URL: http://localhost:8000/produtos/mostra/teste Claramente isso não deveria acontecer. Por nossa sorte, a única consequência aqui será ver a mensagem de que o produto não foi encontrado. Mas existem problemas piores, como ambiguidade entre rotas. Em nosso caso, isso seria um pouco difícil, mas imagine que para simplificar optássemos por remover o /mostra de nossa URL. Isso causaria sérios problemas, pois as seguintes URLs seriam equivalentes: • http://localhost:8000/produtos/1 • http://localhost:8000/produtos/adiciona Já que o {id} pode ser qualquer coisa, inclusive um texto, ele pode ser a palavra adiciona. Em outras palavras, dependendo da ordem em que eu registrar as rotas, pode acontecer de eu acessar /produtos/adiciona e a aplicação me responder que esse produto não existe... Ops! 76 Casa do Código Capítulo 6. Parâmetros da request e URL Nesse caso, precisaríamos de alguma forma ensinar ao Laravel que o {id} da rota sempre será um número. Isso pode ser feito com auxilio do método where, como no exemplo: Route::get( ’/produtos/mostra/{id}’, ’ProdutoController@mostra’ ) ->where(’id’, ’[0-9]+’); Observe que estamos passando para o método where o nome do parâmetro e uma expressão regular (regex) com o pattern que pode ser seguido. Faça a alteração e tente acessar a URL http://localhost:8000/produtos/mostra/teste novamente. O resultado dessa vez será uma NotFoundHttpException. Fig. 6.10: NotFoundHttpException na URL com param não numérico. Justo, não é? Afinal, essa URL realmente não existe. Repare que, passando um número válido, tudo continua funcionando. 77 Capítulo 7 Views mais flexíveis e poderosas Um dos problemas de nosso código atual é que existe muita repetição em nossas views. Veja, por exemplo, como está nosso arquivo de listagem.php: <html> <head> <link href="/css/app.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <div class="container"> <h1>Listagem de produtos</h1> <table class="table table-striped ..."> <!-- conteúdo omitido --> Casa do Código </div> </body> </html> Agora perceba a semelhança com o HTML do arquivo detalhes.php: <html> <head> <link href="/css/app.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <div class="container"> <h1>Detalhes do produto: <? = $p->nome ?> </h1> <!-- conteúdo omitido --> </div> </body> </html> O conteúdo da div principal é diferente, afinal, cada página tem suas regras e objetivos específicos, mas todo o resto está igual! Com isso, no dia a dia se queremos criar uma nova página, sem pensar duas vezes acabamos copiando e colando alguma que existe, mudando apenas o miolo. Quem nunca fez isso? O problema dessa abordagem é que, hora ou outra, você pode querer mudar o cabeçalho, menu, ou qualquer outro elemento que foi copiado e colado em quase todas as suas páginas. E agora? Replico a mudança nas minhas 200 views manualmente? Não parece uma solução ideal, não é? Neste capítulo veremos como o Laravel pode nos ajudar nesse trabalho, tornando nossas views mais flexíveis, evitando repetições e ainda ganhando poderosos recursos que nos ajudam nas diferentes necessidades do nosso cotidiano. 80 Casa do Código Capítulo 7. Views mais flexíveis e poderosas 7.1 Blade, a template engine do Laravel Se o problema estivesse acontecendo no código de nosso controller ou em nossas regras de negócio, bastaria extrair o código repetido pra um arquivo ou classe e pronto, poderíamos reutilizá-la sempre. Que tal se fosse possível fazer o mesmo na view, extraindo toda essa estrutura principal em um único arquivo e reutilizá-lo sempre que quiséssemos? Pois bem, isso não só é possível como também muito recomendado. Dessa forma, quando quisermos adicionar algum link, mudar os elementos ou fazer qualquer alteração nessa parte em comum, mudamos em um único lugar, e todo o resto é atualizado. O ganho em manutenibilidade é enorme. Quem nos ajuda com esse trabalho é o Blade, a template engine do Laravel. Quer ver como é simples? Para resolver esse problema de repetição, basta criar um novo arquivo com o código em comum, que será nosso layout principal. Um fator importante é que todo arquivo do blade precisar ter a extensão .blade.php, portanto, vamos chamá-lo de principal.blade.php, e lembre-se de que, como é uma view, precisa estar no diretório /resources/views. 81 7.1. Blade, a template engine do Laravel Casa do Código Fig. 7.1: Layout do blade dentro da pasta /resources/views. Dentro desse arquivo, vamos colocar todo o HTML em comum, e utilizar a marcação @yield com o nome da seção que deverá ser sobrescrita pelas outras views. A ideia é simples, suas views “herdam” desse layout principal, e sobrescrevem as seções nele definidas: 82 Casa do Código Capítulo 7. Views mais flexíveis e poderosas Fig. 7.2: Layout principal com seção de conteúdo. O código do arquivo principal.blade.php pode ficar assim: <html> <head> <link href="/css/app.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <div class="container"> @yield(’conteudo’) </div> </body> </html> Nada muito diferente, não é? Tem todo o HTML em comum, e a marcação @yield(‘conteudo’) para delimitar onde o conteúdo das outras views deve ser inserido. Vamos testar? O primeiro passo será mudar a extensão do arquivo detalhes.php para detalhes.blade.php. 83 7.1. Blade, a template engine do Laravel Casa do Código Fig. 7.3: Arquivo de detalhes com extensão do blade. Feito isso, podemos tirar todo o código que já existe no layout principal.blade.php, e deixar apenas o miolo que estava dentro da div com classe container. O arquivo vai ficar apenas com este conteúdo: <h1>Detalhes do produto: <? = $p->nome ?> </h1> <ul> <li> <b>Valor:</b> R$ <? = $p->valor ?> </li> <li> <b>Descrição:</b> <? = $p->descricao ?> </li> <li> <b>Quantidade em estoque:</b> <? = $p->quantidade ?> </li> </ul> Ótimo! Mas ainda precisamos dizer que esse arquivo deve utilizar o layout principal, do arquivo principal.blade.php. Para isso, basta adicionar a marcação @extends com o nome do layout que ele deve herdar (neste caso: principal, que é o nome do arquivo sem a extensão). 84 Casa do Código Capítulo 7. Views mais flexíveis e poderosas @extends(’principal’) <!-- restante do html aqui --> Também é preciso colocar todo o conteúdo restante do nosso arquivo de detalhes dentro de uma @section, com o nome que foi definido no @yield do layout principal, que neste caso é conteudo: @extends(’principal’) @section(’conteudo’) <h1>Detalhes do produto: <? = $p->nome ?> </h1> <ul> <li> <b>Valor:</b> R$ <? = $p->valor ?> </li> <li> <b>Descrição:</b> <? = $p->descricao ?> </li> <li> <b>Quantidade em estoque:</b> <? = $p->quantidade ?> </li> </ul> @stop Veja que usamos um @stop para marcar o fim da section. Isso é necessário pois, em alguns momentos, podemos querer sobrescrever mais de uma section do layout principal. 85 7.1. Blade, a template engine do Laravel Casa do Código Fig. 7.4: Visão geral da hierarquia de views. Vamos ver se após a mudança tudo continua funcionando? Basta acessar a página de detalhes de qualquer produto, por exemplo: http://localhost:8000/produtos/mostra/1 Fig. 7.5: Página de detalhes agora com blade. Veja que, como esperado, a página continua funcionando normalmente. Se quiser, clique com o botão direito na página e selecione a opção view page source. Repare que o HTML continua o mesmo! 86 Casa do Código Capítulo 7. Views mais flexíveis e poderosas Fig. 7.6: Código HTML da página de detalhes renderizado após a migração para blade. Legal, não é? Mas o blade nos oferece bem mais que isso! Temos muito mais o que explorar; vamos ver as formas de exibir o valor de nossas variáveis. 7.2 Variáveis com blade Atualmente estamos usando a marcação do PHP puro para imprimir os valores do produto. Quando fazemos: <li> <b>Valor:</b> R$ <? = $p->valor ?> </li> 87 7.2. Variáveis com blade Casa do Código Não há problema nisso e, se quiser, você ainda pode continuar exibindo dessa forma. Mas se quiser e eu recomendo , você também pode fazer da forma do blade, envolvendo a variável em chaves duplas como a seguir: <li> <b>Valor:</b> R$ {{ $p->valor }}> </li> A mudança é simples e o resultado será o mesmo, mas a sintaxe, em minha opinião, é um pouco mais agradável. Entretanto, o ganho vai além de uma sintaxe melhor, ou um caractere a menos. Você ganha bastante flexibilidade e alguns recursos extras, como o or, para definir um valor default caso a variável não esteja populada. Um exemplo seria: {{$p->descricao or ’nenhuma descrição informada’}} Legal, não é? Vamos conhecer diversos outros recursos no decorrer deste capítulo e livro, mas por enquanto, já podemos fazer a mudança no restante do arquivo, que deve ficar assim: @extends(’principal’) @section(’conteudo’) <h1>Detalhes do produto: {{$p->nome}} </h1> <ul> <li> <b>Valor:</b> R$ {{$p->valor}} </li> <li> <b>Descrição:</b> {{$p->descricao}} </li> <li> <b>Quantidade em estoque:</b> {{$p->quantidade}} </li> </ul> @stop 88 Casa do Código Capítulo 7. Views mais flexíveis e poderosas 7.3 Migrando a listagem para Blade Vamos fazer a mesma alteração no arquivo listagem.php? Os passos serão os mesmos, começando por renomear o arquivo, que agora deve se chamar listagem.blade.php. Além disso, já podemos tirar todo o código que já está definido no layout principal e adicionar o @extends e @section. A listagem deve ficar assim: @extends(’principal’) @section(’conteudo’) <h1>Listagem de produtos</h1> <table class="table table-striped table-bordered table-hover"> <?php foreach ($produtos as $p): ?> <tr> <td><? = $p->nome ?> </td> <td><? = $p->valor ?> </td> <td><? = $p->descricao ?> </td> <td><? = $p->quantidade ?> </td> <td> <a href="/produtos/mostra/<? = $p->id ?> "> <span class="glyphicon glyphicon-search"></span> </a> </td> </tr> <?php endforeach ?> </table> @stop Legal, não é? O arquivo já fica um pouco mais limpo. Já podemos também mudar a forma como estamos exibindo nossas variáveis, utilizando as chaves duplas do blade. O corpo da tabela ficará assim: <tr> <td>{{ $p->nome }} </td> <td>{{ $p->valor }} </td> <td>{{ $p->descricao }} </td> 89 7.4. Loopings e mais loopings Casa do Código <td>{{ $p->quantidade }} </td> <td> <a href="/produtos/mostra/{{$p->id}}>"> <span class="glyphicon glyphicon-search"></span> </a> </td> </tr> 7.4 Loopings e mais loopings O blade também nos oferece uma maneira bastante interessante de enxugar a sintaxe do nosso foreach, na listagem de produtos. Veja que atualmente estamos fazendo com PHP puro, ou seja, utilizando a tag <?php ?>. Isso funciona, e você pode continuar fazendo assim se preferir, claro, mas que tal tirar toda essa verbosidade e adicionar um simples @ antes desse operador? Em outras palavras, em vez de fazer: <?php foreach ($produtos as $p): ?> <!-- codigo omitido --> <?php endforeach ?> Você pode mudar para: @foreach ($produtos as $p) <!-- codigo omitido --> @endforeach Isso melhora bastante a legibilidade do código. Agora o arquivo listagem.blade.php completo deve estar parecido com: @extends(’principal’) @section(’conteudo’) <h1>Listagem de produtos</h1> <table class="table table-striped table-bordered table-hover"> @foreach ($produtos as $p) <tr> <td> {{$p->nome}} </td> 90 Casa do Código Capítulo 7. Views mais flexíveis e poderosas <td> {{$p->valor}} </td> <td> {{$p->descricao}} </td> <td> {{$p->quantidade}} </td> <td> <a href="/produtos/mostra/{{$p->id}}>"> <span class="glyphicon glyphicon-search"></span> </a> </td> </tr> @endforeach </table> @stop Outros tipos de looping Além do foreach, também podemos fazer o mesmo com o for tradicional: @for ($i = 0; $i < 10; $i++) O indice atual é {{ $i }} @endfor Ou ainda com um while: @while (true) Entrando em looping infinito! @endwhile Ha ainda uma variação bastante interessante, chamada forelse. Se a lista for vazia, ele executa o código do bloco marcado com @empty: @forelse($produtos as $p) <li>{{ $p->nome }}</li> @empty <p>Não tem nenhum produto!</p> @endforelse Legal, não é? Mas como estamos fazendo o foreach no miolo de uma tabela, não seria muito interessante. No lugar disso, podemos usar um if! 91 7.5. Adicionando condições na view 7.5 Casa do Código Adicionando condições na view Podemos fazer um if verificando se a lista de produtos está vazia, e se estiver, mostrar uma mensagem de que não existe nenhum produto cadastrado. Em PHP puro, o código ficaria assim: <?php if(empty($produtos)) { ?> <div class="alert alert-danger"> Você não tem nenhum produto cadastrado. </div> <?php } else { ?> <h1>Listagem de produtos</h1> <table> <? foreach ($produtos as $p) { ?> ?> <!-- continuação do código --> <?php } ?> <!-- fechando o foreach --> <?php } ?> <!-- fechando o else --> Um pouco confuso e difícil de ler. Que tal com blade? @if(empty($produtos)) <div class="alert alert-danger"> Você não tem nenhum produto cadastrado. </div> @else <h1>Listagem de produtos</h1> <table> @foreach ($produtos as $p) <!-- continuação do código --> @endforeach 92 Casa do Código Capítulo 7. Views mais flexíveis e poderosas @endif A ausência das chaves e tags do PHP ajuda bastante, nosso código fica mais simples e legível. Vamos fazer a alteração? O arquivo listage.blade.php completo pode ficar assim: @extends(’principal’) @section(’conteudo’) @if(empty($produtos)) <div>Você não tem nenhum produto cadastrado.</div> @else <h1>Listagem de produtos</h1> <table class="table table-striped table-bordered table-hover"> @foreach ($produtos as $p) <tr> <td> {{$p->nome}} </td> <td> {{$p->valor}} </td> <td> {{$p->descricao}} </td> <td> {{$p->quantidade}} </td> <td> <a href="/produtos/mostra/{{$p->id}}>"> <span class="glyphicon glyphicon-search"></span> </a> </td> </tr> @endforeach </table> @endif @stop Vamos testar? Para não ter que apagar os produtos do banco, vamos roubar um pouco, incluindo um array vazio no método lista do nosso controller: 93 7.5. Adicionando condições na view Casa do Código class ProdutoController extends Controller { public function lista(){ // busca os produtos do banco return view(’listagem’)->with(’produtos’, array()); } // continuação do código omitido Com isso, ao acessar a listagem, o resultado será: Fig. 7.7: Listagem de produtos com mensagem de lista vazia. Excelente, tudo está funcionando como esperamos! Agora que testamos, não se esqueça de voltar o método lista ao normal, passando o array de $produtos no lugar de um array vazio. Outros formas de condicionar Além do @if e @else, você também pode usar @elseifs ou mesmo o @unless, que faz a condição inversa. Por exemplo, no caso a seguir, o valor condicionado sempre será exibido: @unless (1 == 2) Esse texto sempre será exibido! @endunless 94 Casa do Código 7.6 Capítulo 7. Views mais flexíveis e poderosas Marcando produtos em falta no estoque Queremos agora pintar de vermelho as colunas da tabela que contêm produtos com quantidade menor ou igual a 1. Essa marcação visual vai nos ajudar a perceber quando um produto está em falta em nosso estoque. Pra colorir a linha, podemos adicionar a classe danger, do bootstrap, nos trs dos produtos em falta. Apenas para ver o efeito da alteração, vamos adicionar a classe em todas as colunas: <table class="table table-striped ..."> @foreach ($produtos as $p) <tr class="danger"> <!-- código omitido --> </tr> @endforeach Atualize a página da listagem para ver a diferença, ela agora deve estar parecida com: Fig. 7.8: Listagem com todos os produtos marcados de vermelho. Ótimo, agora só falta condicionar quais os produtos que devem ficar pintados de vermelho. Claro, um @if resolveria, mas ficaria bem feio e verboso. Em vez disso, podemos fazer uma condição parecida com: 95 7.6. Marcando produtos em falta no estoque Casa do Código {{ $p->quantidade <= 1 ? ’danger’ : ’’}} Esse é o tão conhecido ternário que, como você já deve ter percebido, tem a seguinte estrutura: {{ condicao ? ’valor_se_true’ : ’valor_se_false’}} Ou seja, se a condição booleana for verdadeira (true), o primeiro valor será devolvido. Caso contrário, retornará o segundo valor. É importante saber que esse não é um recurso exclusivo do blade, claro, em PHP puro você também pode fazer: <?php $p->quantidade <= 1 ? ’danger’ : ’’ ?> O que mudou foi a tag, assim como nos demais exemplos, mas nesse caso, usando o blade não precisamos adicionar nenhum @ para que isso funcione, basta colocar o ternário dentro das chaves duplas e pronto. Vamos testar? Basta adicionar essa condição dentro da class do tr. A tabela vai ficar assim: <table class="table table-striped ..."> @foreach ($produtos as $p) <tr class="{{$p->quantidade<=1 ? ’danger’ : ’’ }}"> <!-- código omitido --> </tr> @endforeach </table> Pronto para testar? É só acessar a listagem novamente! No meu caso, ela ficará assim: 96 Casa do Código Capítulo 7. Views mais flexíveis e poderosas Fig. 7.9: Listagem de produtos com verificação de quantidade em estoque. Também podemos adicionar uma legenda para deixar essa informação mais intuitiva. Vamos colocar um span logo depois do @endif, no final da tabela: <h4> <span class="label label-danger pull-right"> Um ou menos itens no estoque </span> </h4> Veja que ele já tem algumas classes do bootstrap para estilizá-lo. Com essa alteração, o arquivo listagem.blade.php completo deve ficar assim: @extends(’principal’) @section(’conteudo’) @if(empty($produtos)) <div class="alert alert-danger"> Você não tem nenhum produto cadastrado. </div> @else 97 7.6. Marcando produtos em falta no estoque Casa do Código <h1>Listagem de produtos</h1> <table class="table table-striped table-bordered table-hover"> @foreach ($produtos as $p) <tr class="{{$p->quantidade<=1 ? ’danger’ : ’’ }}"> <td> {{$p->nome}} </td> <td> {{$p->valor}} </td> <td> {{$p->descricao}} </td> <td> {{$p->quantidade}} </td> <td> <a href="/produtos/mostra/{{$p->id}}>"> <span class="glyphicon glyphicon-search"></span> </a> </td> </tr> @endforeach </table> @endif <h4> <span class="label label-danger pull-right"> Um ou menos itens no estoque </span> </h4> @stop Acesse a listagem novamente para ver o resultado! Ela deve estar parecida com: 98 Casa do Código Capítulo 7. Views mais flexíveis e poderosas Fig. 7.10: Aparência final da listagem com legenda. 7.7 Organizando nossas views Conforme nosso sistema vai crescendo, mais e mais views devem aparecer e precisamos organizá-las de alguma forma. É muito comum separar esses arquivos por controller, ou qualquer estrutura que facilite seu trabalho e também o trabalho de sua equipe. Um exemplo de separação por controller seria, se temos um ProdutoController, criar um diretório chamado produto e colocar nele todas as views relacionadas. O mesmo para usuários e qualquer outro elemento que venha a aparecer em nossa aplicação. Vamos criar uma nova pasta chamada produto, dentro de resources/views, e mover para ela os arquivos listagem.blade.php e detalhes.blade.php. Seu projeto deve ficar assim: 99 7.7. Organizando nossas views Casa do Código Fig. 7.11: Pasta produto com arquivos de listagem e detalhes. Além disso, vamos criar uma nova pasta chamada layout, também em resources/views, para mover o arquivo principal.blade.php: 100 Casa do Código Capítulo 7. Views mais flexíveis e poderosas Fig. 7.12: Pasta layout com arquivo principal. Mas, agora que mudamos isso, tente acessar qualquer página da sua aplicação. O resultado, talvez já esperado, será um erro! Fig. 7.13: Exception de arquivo não encontrado. A mensagem já dá uma pista: View listagem not found. Agora que mudamos a view para um subdiretório, precisamos alterar o retorno dos métodos 101 7.7. Organizando nossas views Casa do Código do nosso controller. Atualmente fazemos algo como: return view(’listagem’); Em vez disso, precisamos ensinar ao Laravel que a view está dentro da pasta produto: return view(’produto/listagem’); Isso já funciona, mas é ainda mais comum fazermos desta forma: return view(’produto.listagem’); Veja que usamos um ponto no lugar da barra. O resultado será o mesmo, você pode escolher o que preferir. Nosso controller deve ficar assim: class ProdutoController extends Controller { public function lista(){ // código omitido return view(’produto.listagem’)->with(’produtos’, $produtos); } public function mostra($id){ // código omitido return view(’produto.detalhes’)->with(’p’, $produto); } Vamos testar? Acessamos a página novamente e... ops! 102 Casa do Código Capítulo 7. Views mais flexíveis e poderosas Fig. 7.14: Exception de arquivo não encontrado. O arquivo principal.blade.php não foi encontrado nas views, também precisamos mudar o lugar onde usamos o @extends! Agora, no lugar de @extends(‘principal’), precisamos mudar para @extends(‘layout.principal’). A alteração deve ser feita nos arquivos detalhes.blade.php e listagem.blade.php: @extends(’layout.principal’) @section(’conteudo’) <!-- restante do conteúdo omitido --> Ótimo, com isso nossa aplicação volta a funcionar como esperado. 7.8 Estilizando o template principal Como nossas views seguem um template padrão, definido no principal.blade.php, agora fica muito mais fácil evoluir e estilizar o design da nossa aplicação web. Podemos adicionar um header com o nome do projeto, links, footer com mensagem de direitos autorais etc. Veja como é simples. Como o layout está isolado, só precisamos aplicar as alterações no principal.blade.php e ela será propagada em todas as páginas. Um exemplo de layout seria: 103 7.8. Estilizando o template principal Casa do Código <html> <head> <link href="/css/app.css" rel="stylesheet"> <link href="/css/custom.css" rel="stylesheet"> <title>Controle de estoque</title> </head> <body> <div class="container"> <nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="/produtos"> Estoque Laravel </a> </div> <ul class="nav navbar-nav navbar-right"> <li><a href="/produtos">Listagem</a></li> </ul> </div> </nav> @yield(’conteudo’) <footer class="footer"> <p>© Livro de Laravel da Casa do Código.</p> </footer> </div> </body> </html> Repare que estamos importando um arquivo custom.css, que ainda não existe. Se quiser, você pode criar o arquivo dentro de /public/css/, com o conteúdo a seguir: 104 Casa do Código Capítulo 7. Views mais flexíveis e poderosas nav.navbar.navbar-default{ background-color: #337ab7; border-color: #2e6da4; margin-top: 10px; } nav.navbar.navbar-default a { color: white; } li p.navbar-text, a.navbar-brand { color: white!important ; font-weight: bolder; } footer.footer { background-color: #337ab7; border-color: #2e6da4; margin-top: 55px; color: white; border-radius: 5px; } footer.footer p { padding: 10px; } Com esse estilo, a listagem vai ficar parecida com: 105 7.8. Estilizando o template principal Casa do Código Fig. 7.15: Aparência final da listagem. E a view de detalhes deve ficar assim: Fig. 7.16: Aparência final da view de detalhes. Legal, não é? Mudamos um único arquivo e o estilo foi propagado em 106 Casa do Código Capítulo 7. Views mais flexíveis e poderosas todos. Claro, fique à vontade para estilizar o HTML da forma que preferir, você pode e deve ir sempre além do que vemos aqui no livro. 107 Capítulo 8 Request e métodos HTTP 8.1 Criando formulário de novos produtos Já somos capazes de visualizar uma lista com todos os produtos e os detalhes de um produto específico, mas ainda não temos uma função de adicionar novos produtos em estoque. Vamos implementá-la? Podemos começar pela rota, no arquivo routes.php. Vamos registrar uma URI /produtos/novo apontando para o método novo, do ProdutoController. Ela pode ficar assim: Route::get(’/produtos/novo’, ’ProdutoController@novo’); Vale lembrar que, antes de adicionar um produto no banco de dados, precisamos abrir uma página com formulário para que o usuário possa preencher os dados, portanto é isso o que o método novo fará. Ele retornará uma view chamada formulario, que será criada dentro da pasta produto: 8.1. Criando formulário de novos produtos Casa do Código public function novo(){ return view(’produto.formulario’); } Para fechar esse passo inicial, precisamos criar o arquivo formulario.blade.php dentro de /resources/view/produtos/. Como queremos que ele use o layout padrão, no início do arquivo declaramos que ele herda do layout principal e declaramos a section de conteúdo, assim como fizemos com os demais arquivos. Já vamos adicionar um form também. O arquivo deve ficar assim: @extends(’templates.principal’) @section(’conteudo’) <h1>Novo produto</h1> <form> <label>Nome</label> <input/> <label>Descricao</label> <input/> <label>Valor</label> <input/> <label>Quantidade</label> <input type="number"/> <button type="submit">Submit</button> </form> @stop Vamos testar para garantir que tudo funcionou até aqui? Só precisamos acessar a URL http://localhost:8000/produtos/novo. O resultado será: 110 Casa do Código Capítulo 8. Request e métodos HTTP Fig. 8.1: Formulário sem estilos de CSS. O formulário está um pouco estranho, com as labels e inputs todos bagunçados em uma mesma linha. Já que estamos usando bootstrap, podemos usar um de seus modelos de formulários que pode ser encontrado em sua documentação: http://getbootstrap.com/css/#forms Fazendo a alteração, o HTML poderá ficar assim: @extends(’templates.principal’) @section(’conteudo’) <h1>Novo produto</h1> <form> <div class="form-group"> <label>Nome</label> <input class="form-control"> </div> <div class="form-group"> <label>Descricao</label> <input class="form-control"> 111 8.1. Criando formulário de novos produtos Casa do Código </div> <div class="form-group"> <label>Valor</label> <input class="form-control"> </div> <div class="form-group"> <label>Quantidade</label> <input type="number" class="form-control"> </div> <button type="submit" class="btn btn-primary btn-block">Submit</button> </form> @stop Vamos ver o resultado? Fig. 8.2: Formulários com estilos CSS do bootstrap. 112 Casa do Código Capítulo 8. Request e métodos HTTP Excelente! Agora que temos uma nova página, podemos adicionar um novo link no cabeçalho do arquivo principal.blade.php. O HTML com o novo link ficará assim: <ul class="nav navbar-nav navbar-right"> <li><a href="/produtos">Listagem</a></li> <li><a href="/produtos/novo">Novo</a></li> </ul> Com isso boa parte do trabalho está pronto, mas agora precisamos implementar a função que efetivamente adicionará um novo produto ao banco. 8.2 Criando o método adiciona Como de costume, podemos começar pelo mapeamento desse método, que deve ser feito no routes.php. Podemos registrar uma nova rota /produtos/adiciona, que apontará para o método adiciona do ProdutoController. O código ficará assim: Route::get(’/produtos/adiciona’, ’ProdutoController@adiciona’); Agora que já temos a rota, precisamos criar esse novo método no controller. Sua assinatura ficará desta forma: public function adiciona(){ // deve adicionar os produtos no banco } E agora, qual o próximo passo? Antes mesmo de pensar no SQL que deve ser executado no banco de dados, ou na view que deve ser retornada, precisamos pegar os parâmetros digitados no formulário de alguma maneira! Os passos necessários para o método adiciona serão: public function adiciona(){ // pegar dados do formulario // salvar no banco de dados // retornar alguma view } 113 8.2. Criando o método adiciona Casa do Código Já vimos que é a classe Request quem cuida desse trabalho, de pegar os parâmetros da requisição. Portanto podemos fazer algo como: $nome = Request::input(’o_que_passar_aqui_?’); Eis a questão. Quando estávamos buscando o parâmetro enviado pelo link de visualizar, sabíamos que a chave era id, pois ela era enviada pela URL dessa forma: /produtos/mostra?id=1 Veja que estamos deixando explícito que o parâmetro se chama id, e que ele recebe o valor 1. Como agora temos um formulário, e não um link, precisaremos adicionar um name em cada input do formulário para que seja possível recuperar suas informações pelo controller. O HTML completo ficará assim: @extends(’templates.principal’) @section(’conteudo’) <h1>Novo produto</h1> <form action="/produtos/adiciona"> <div class="form-group"> <label>Nome</label> <input name="nome" class="form-control"/> </div> <div class="form-group"> <label>Descricao</label> <input name="descricao" class="form-control"/> </div> <div class="form-group"> <label>Valor</label> <input name="valor" class="form-control"/> </div> <div class="form-group"> <label>Quantidade</label> <input type="number" name="quantidade" class="form-control"/> </div> 114 Casa do Código Capítulo 8. Request e métodos HTTP <button type="submit" class="btn btn-primary btn-block">Submit</button> </form> @stop Com isso, em nosso controller, podemos recuperar cada um dos valores: public function adiciona(){ $nome = Request::input(’nome’); $descricao = Request::input(’descricao’); $valor = Request::input(’valor’); $quantidade = Request::input(’quantidade’); // salvar no banco de dados // retornar alguma view } Outras alternativas Podemos sim utilizar o método all da Request para pegar todos os valores de uma única vez em um array, ou ainda o método only deixando explícito quais parâmetros queremos buscar: $all = Request::all(); $only = Request::only(’nome’, ’valor’, ’...’); Mas para deixar bem explícito, e garantir a ordem dos dados, por enquanto faremos dessa forma. Voltaremos a falar sobre esse método para melhorar este e outros pontos que serão detalhados mais à frente. Ótimo, com isso já temos cada um dos valores digitados no formulário. Vamos testar? Podemos mudar o método adiciona para retornar esses valores: public function adiciona(){ 115 8.2. Criando o método adiciona Casa do Código $nome = Request::input(’nome’); $descricao = Request::input(’descricao’); $valor = Request::input(’valor’); $quantidade = Request::input(’quantidade’); return implode( ’, ’, array($nome, $descricao, $valor, $quantidade)); } O implode fará com que cada um dos valores seja impresso separado por vírgula. Agora só falta um detalhe, precisamos adicionar a action do formulário, para que ele envie para o método adiciona: <form action="/produtos/adiciona"> <!-- restante do html omitido --> Vamos testar? Basta abrir o formulário no navegador e preencher os campos com algum valor de teste: 116 Casa do Código Capítulo 8. Request e métodos HTTP Fig. 8.3: Formulário preenchido com valores de teste. Ao clicar no botão Adicionar, a saída será: Fig. 8.4: Mensagem com informações do produto adicionado. 117 8.3. Inserindo os produtos no BD Casa do Código 8.3 Inserindo os produtos no BD Boa parte do trabalho está pronta, mas ainda precisamos persistir o produto no banco de dados. Por enquanto, faremos isso com a função insert da interface DB, que já usamos no método lista. O código de adicionar ficará parecido com: DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); Veja que, assim como fizemos no select do método mostra, utilizamos uma ? no lugar dos valores e passamos um array na ordem em que esses elementos devem ser substituídos. Não tem muito segredo, mas se você não está muito acostumado com SQL pode se assustar um pouco. Muito em breve veremos como o Laravel nos ajuda neste trabalho. 8.4 Retornando uma view de confirmação Agora que já estamos persistindo, podemos retornar uma view com a mensagem de confirmação, dizendo que o produto foi adicionado com sucesso. Podemos chamá-la de adicionado.blade.php. @extends(’templates.principal’) @section(’conteudo’) O produto foi adicionado com sucesso! @stop Vale lembrar que essa view será criada dentro de /resources/views/produto, assim como as demais, portanto, no retorno do controller faremos produto.adicionado: public function adiciona(){ $nome = Request::input(’nome’); 118 Casa do Código Capítulo 8. Request e métodos HTTP $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); return view(’produto.adicionado’); } Já podemos fazer um novo teste, garantindo que a mensagem será exibida e que o produto será persistido no banco. Fig. 8.5: Adicionando um novo produto. Ao submeter o formulário, recebemos o seguinte resultado: 119 8.4. Retornando uma view de confirmação Casa do Código Fig. 8.6: Mensagem de produto adicionado em texto puro. Tudo funcionou como esperado, mas ainda assim alguns detalhes podem ser melhorados. Seria legal, por exemplo, exibir o nome do produto que foi adicionado, não é? Isso é bem simples, basta incluir o parâmetro pelo controller, com o método with que já vimos. O método adiciona completo ficará como o seguinte: public function adiciona(){ $nome = Request::input(’nome’); $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); return view(’produto.adicionado’)->with(’nome’, $nome); } Na view, exibimos o $nome incluído: @extends(’templates.principal’) 120 Casa do Código Capítulo 8. Request e métodos HTTP @section(’conteudo’) O produto {{$nome}} foi adicionado com sucesso! @stop Além disso, podemos adicionar uma div com as classes do bootstrap para deixar a mensagem com uma aparência um pouco mais amigável. Uma alternativa seria: @extends(’templates.principal’) @section(’conteudo’) <div class="alert alert-success"> <strong>Sucesso!</strong> O produto {{$nome}} foi adicionado. </div> @stop Agora sim, o resultado será: Fig. 8.7: Mensagem de produto adicionado com estilos do bootstrap. Vamos acessar também a listagem, para garantir que o produto foi persistido no banco de dados como deveria. 121 8.4. Retornando uma view de confirmação Casa do Código Fig. 8.8: Listagem já com novo produto adicionado. Perfeito, deu tudo certo! 122 Casa do Código Capítulo 8. Request e métodos HTTP Query Builder Em vez de utilizar o DB:insert como fizemos, temos como alternativa um recurso conhecido como query builder. Ele nos oferece uma interface fluente, que é bastante conveniente para não nos preocuparmos tanto com o SQL. A adição de produtos poderia ficar assim: DB::table(’produtos’)->insert( [’nome’ => $nome, ’valor’ => $valor, ’descricao’ => $descricao, ’quantidade’ => $quantidade ] ); Legal, não é? Se você se interessar, pode ver mais das possibilidades em http://laravel.com/docs/4.0/queries. Em breve veremos uma forma ainda mais simples de fazer esse trabalho, com uso do Eloquent. 8.5 Utilizando os métodos do HTTP O cadastro de produtos está funcionando bem, ele é adicionado ao banco, uma página de confirmação bonita é exibida, tudo parece correto. Só que há um ponto muito importante que precisa ser corrigido. Veja que, ao adicionar um produto, todos os parâmetros da requisição estão sendo enviados pela URL: http://localhost:8000/produtos /adiciona?nome=Frigobar &descricao=Com+controle+de+temperatura &valor=1200.00&quantidade=2 Isso acontece porque, por padrão, o método HTTP utilizado no form do HTML é o GET. Mas nem sempre queremos deixar tudo visível dessa forma, imagine por exemplo se esse fosse um formulário de login, a URL ficaria assim: 123 8.5. Utilizando os métodos do HTTP Casa do Código http://localhost:8000 /login?usuario=Rodrigo &senha=mamae+querida Um caos! Além disso, existe um limite para o tamanho da URL, ou seja, se fosse um formulário muito grande, ou pequeno com muito texto, correríamos o risco de perder informações se esse conteúdo ultrapassasse esse limite. Em vez de enviar por GET, podemos modificar o formulário para utilizar um outro método HTTP, o POST. Dessa forma, os dados não ficarão mais visíveis na URL, tudo será passado dentro do corpo do protocolo HTTP. A URL ficará http://localhost:8000/produtos/adiciona, sem os dados visíveis, independente da quantidade de parâmetros enviados. É muito importante lembrar que isso não é uma medida de segurança, afinal os dados ainda podem ser consultados pelo navegador do usuário. O ganho dessa abordagem é que o corpo do POST é muito maior que a URI do GET, e evitamos deixar os dados sempre tão expostos. Tudo o que precisamos fazer para que os dados sejam enviados via POST no form de novo de produto é adicionar o atributo method="post", como a seguir: <h1>Novo produto</h1> <form action="/produtos/adiciona" method="post"> <div class="form-group"> <label>Nome</label> <input name="nome" class="form-control"/> </div> <!-- continuação do html --> Mas, com essa alteração, nosso formulário para de funcionar! Tente adicionar um novo produto para ver o resultado. 124 Casa do Código Capítulo 8. Request e métodos HTTP Fig. 8.9: MethodNotAllowedHttpException ao submeter o formulário. Recebemos uma MethodNotAllowedHttpException, mas o que isso significa? O que acontece é que no momento de mapear uma rota para o método adiciona, fizemos: Route::get(’/produtos/adiciona’, ’ProdutoController@adiciona’); Repare que, como utilizamos o método get, definimos explicitamente que apenas requisições desse tipo serão aceitas. É justo. Como resolver? Uma possibilidade seria utilizando o método any, ou seja, liberando qualquer tipo de requisição para essa URL. Mas não é isso que queremos, não é? Queremos enviar os dados por POST; portanto, utilizaremos o método post: Route::post(’/produtos/adiciona’, ’ProdutoController@adiciona’); 125 8.5. Utilizando os métodos do HTTP Casa do Código Para saber mais: O método match Além de get, post e any, a interface Route ainda nos oferece um método chamado match, que nos permite passar um array especificando exatamente quais métodos HTTP devem ser aceitos. Um exemplo seria: Route::match(array(’GET’, ’POST’), ’/produtos/adiciona’, ’ProdutoController@adiciona’); Neste caso, requisições de tipo GET ou POST seriam aceitas. Vamos testar? É só abrir o formulário novamente no navegador, preencher os campos e clicar em adicionar. Para nossa surpresa, ou não, ainda não funcionará. Mas desta vez o erro será outro: Fig. 8.10: TokenMismatchException ao submeter o formulário. Recebemos um TokenMismatchException, que acontece sempre que enviamos uma requisição do tipo POST sem mandar um parâmetro chamado _token. Mas por que isso é necessário? Por padrão, o Laravel tem um mecanismo de proteção contra CSRF (Cross-site request forgery, ou falsificação de solicitação entre sites). Esse é um tipo de ataque, bastante conhecido no 126 Casa do Código Capítulo 8. Request e métodos HTTP mundo web, que tira proveito da relação de confiança entre seu usuário legítimo e um aplicativo web. Você pode ler um pouco mais sobre ele aqui: http://pt.wikipedia.org/wiki/Cross-site_request_forgery A solução é simples, o Laravel tem um helper method chamado csrf_token() que nos retorna esse token. Só precisamos adicionar um novo input, que pode e deve ficar escondido, portanto será type="hidden". Além disso, o name desse input precisa ser _token, que é esperado pelas requisições desse tipo. O input ficará assim: <input type="hidden" name="_token" value="{{{ csrf_token() }}}" /> O código no nosso formulario.blade.php completo fica: @extends(’layout.principal’) @section(’conteudo’) <h1>Novo produto</h1> <form action="/produtos/adiciona" method="post"> <input type="hidden" name="_token" value="{{{ csrf_token() }}}" /> <div class="form-group"> <label>Nome</label> <input name="nome" class="form-control"/> </div> <div class="form-group"> <label>Descricao</label> <input name="descricao" class="form-control"/> </div> <div class="form-group"> <label>Valor</label> <input name="valor" class="form-control"/> </div> <div class="form-group"> <label>Quantidade</label> 127 8.6. Mais métodos HTTP e quando utilizá-los Casa do Código <input type="number" name="quantidade" class="form-control"/> </div> <button type="submit" class="btn btn-primary btn-block">Adicionar</button> </form> @stop Agora sim, vamos testar? Fig. 8.11: Formulário enviado com sucesso. Sucesso! 8.6 Mais métodos HTTP e quando utilizá-los Mas, afinal, quando devemos usar cada método HTTP? É muito comum utilizarmos o método GET para listagens ou visualização de algum de nossos recursos, sempre em operações que não modificam os valores do sistema. O POST deve ser utilizado para adição de informações a um recuso existente ou adição de um novo recurso, como estamos fazendo agora com os produtos. Além disso, existem outros verbos como o PUT e DELETE, que são utilizados para atualização e remoção, respectivamente. 128 Capítulo 9 Os diferentes tipos de resposta O problema da abordagem que utilizamos ao adicionar um novo produto é que, normalmente, não queremos ir para uma tela que apenas mostra uma mensagem. Se pensarmos bem, a view adicionado.blade.php não tem muita utilidade. A mensagem que ela exibe, sim, é muito válida, mas isso pode ser feito de diversas outras formas, sem a necessidade de uma view intermediaria. 9.1 Redirecionando para outras lógicas Uma alternativa bastante utilizada seria encaminhar o usuário para a página de listagem após a adição de um novo produto. Podemos ainda exibir a mensagem de que o produto foi adicionado com sucesso na própria listagem, como é de costume. Mas o que precisaríamos alterar em nosso código? Em vez de retornar a view adicionada, como estamos fazendo, po- 9.1. Redirecionando para outras lógicas Casa do Código demos modificar o final do método adiciona para que ele retorne a view listagem. public function adiciona(){ $nome = Request::input(’nome’); $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); return view(’produtos.listagem’); } Mas isso não seria o suficiente, afinal, a listagem precisa da variável $produtos com todos os registros dessa tabela no banco de dados para fun- cionar. Em outras palavras, com essa alteração, a view de listagem será exibida corretamente, porém ela sempre estará vazia. Fig. 9.1: Listagem de produtos vazia. Bem, não é o que esperávamos! Mas a solução é bem simples, basta listar os produtos e passar a variável para view, por exemplo: 130 Casa do Código Capítulo 9. Os diferentes tipos de resposta public function adiciona(){ $nome = Request::input(’nome’); $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); $produtos = DB::select(’select * from produtos’); return view(’produtos.listagem’)->with(’produtos’, $produtos); } Agora sim, tudo vai funcionar como esperado! Mas calma, o final do nosso método adiciona ficou idêntico ao método lista: public function lista(){ $produtos = DB::select(’select * from produtos’); return view(’produtos.listagem’)->with(’produtos’, $produtos); } Sabemos que isso não é legal, afinal, estamos duplicando código. Do ponto de vista da orientação a objetos, isso é um problema bem grande, já que não estamos seguindo seu princípio básico de reutilizar comportamentos. Se o método lista já faz esse trabalho, não queremos copiar seu código, mas sim delegar essa responsabilidade para ele. A mudança no método adiciona será mínima, mas muito valiosa. Em vez de fazer o select no banco e retornar a view de listagem, simplesmente redirecionamos o fluxo para a URI /produtos, que será atendida pelo método lista. Assim como o view, há um helper method que nos ajuda a fazer redirecionamentos como esse: return redirect(’/produtos’); Simples, não é? Podemos e devemos redirecionar o fluxo da requisição para outras lógicas sempre que necessário. O método adiciona completo ficará assim: 131 9.2. Mas e a mensagem de confirmação? Casa do Código public function adiciona(){ $nome = Request::input(’nome’); $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); return redirect(’/produtos’); } Ainda há muito o que melhorar, mas essa alteração já foi um passo para o caminho certo. Teste para garantir que tudo está funcionando como esperado, após adicionar um novo produto, a tela de listagem deve ser exibida. 9.2 Mas e a mensagem de confirmação? O único problema dessa alteração é que agora não temos mais uma mensagem bonita com o feedback de que o processo foi concluído com sucesso, mas isso é simples, podemos mover a div da página adicionado.blade.php para o final da listagem: @extends(’layout.principal’) @section(’conteudo’) @if(empty($produtos)) <div class="alert alert-danger"> Você não tem nenhum produto cadastrado. </div> @else <h1>Listagem de produtos</h1> <table class="table table-striped ..."> @foreach ($produtos as $p) 132 Casa do Código Capítulo 9. Os diferentes tipos de resposta <!-- conteúdo omitido --> @endforeach </table> @endif <h4> <span class="label label-danger pull-right"> Um ou menos itens no estoque </span> </h4> <div class="alert alert-success"> <strong>Sucesso!</strong> O produto {{ $nome }} foi adicionado. </div> @stop Vamos testar? Adicionamos um novo produto, mas veja o resultado: Fig. 9.2: Exception de variável nome não definida. Uma exception! O Undefined variable: nome, ou variável nome não definida em bom português, aconteceu porque, quando fizemos o redirect, os dados da requisição anterior foram perdidos. Isso é natural, claro, afinal o esperado é que os dados de uma requisição morram junto com ele, 133 9.3. Recuperando valores da requisição anterior Casa do Código mas como resolver? Há algumas alternativas, uma das quais seria incluir o parâmetro manualmente na nova requisição feita pelo redirect, mas e se forem vários? Daria um bom trabalho! 9.3 Recuperando valores da requisição anterior Por nossa sorte o Laravel nos oferece uma forma bem simples de recuperar os valores da requisição anterior, já que é uma necessidade comum em casos como esse. Observe como é simples: public function adiciona(){ $nome = Request::input(’nome’); <!-- código omitido --> return redirect(’/produtos’)->withInput(); } É só isso, basta chamar o método withInput depois do redirect e pronto, todos os parâmetros serão mantidos. Mas há uma questão. Na view, em vez de acessar o parâmetro da forma tradicional, utilizamos um método auxiliar chamado old. Portanto, o código do final da listagem deverá ficar assim: <div class="alert alert-success"> <strong>Sucesso!</strong> O produto {{ old(’nome’) }} foi adicionado. </div> Para mim é uma abordagem bem justa, pois dessa forma fica explícito que estamos exibindo um valor da requisição anterior. Vamos testar? Tentamos adicionar um novo produto e sucesso, a mensagem foi exibida: 134 Casa do Código Capítulo 9. Os diferentes tipos de resposta Fig. 9.3: Listagem com mensagem de produto adicionado com sucesso. Mas vamos fazer um novo teste, abrindo a listagem sem que nenhum produto tenha sido adicionado. Ops, a mensagem está lá! 135 9.4. Escolhendo quais valores manter Casa do Código Fig. 9.4: Mensagem de sucesso sem nome de produto. Veja que a única diferença é que o nome ficou em branco, já que nenhum produto foi adicionado. Mas a solução é bem simples, só precisamos garantir que a mensagem será exibida apenas se o valor do nome estiver presente na requisição anterior. Um simples if resolve o problema: @if(old(’nome’)) <div class="alert alert-success"> <strong>Sucesso!</strong> O produto {{ old(’nome’) }} foi adicionado. </div> @endif 9.4 Escolhendo quais valores manter O problema de usar o withInput dessa forma é que todos os parâmetros são mantidos, mas isso é desnecessário, já que estamos usando apenas o nome. 136 Casa do Código Capítulo 9. Os diferentes tipos de resposta Para evitar que isso aconteça, você pode definir explicitamente quais parâmetros devem ser mantidos: return redirect(’/produtos’) ->withInput(Request::only(’nome’)); Legal, não é? O mesmo vale para os outros métodos da Request, como por exemplo o except. No caso a seguir, todos os parâmetros exceto a senha serão mantidos na próxima requisição. return redirect(’/usuarios’) ->withInput(Request::except(’senha’)); 9.5 Outros tipos de redirect Além de como fizemos, existem diversas possibilidades interessantes para o método redirect. Uma delas, que por sinal eu recomendo bastante, é redirecionar para uma ação do controller e não uma URI. Redirecionando para uma action Quer ver como é simples? Como o método adiciona quer redirecionar para o método lista, da classe ProdutoController, em vez de usar o redirect(‘/produtos’), ou seja, com a URI, ele pode fazer: return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); Assim ele está dizendo explicitamente qual o método que quer redirecionar, independente de sua URI. Se decidirmos no futuro mudar a rota desse método, precisaríamos lembrar de mudar todos os lugares que faziam redirect. Já quando fazemos dessa forma, apontando para uma action específica, tudo continua funcionado. Claro, se alguém mudar o nome do método o redirect também vai parar de funcionar, mas esse tipo de mudança acontece com uma frequência bem menor do que a mudança de URI. 137 9.6. Para saber mais: rotas nomeadas Casa do Código Mas oferecer essa possibilidade apenas para o redirect não ajudaria muito, pois, todos os links quebrariam se uma rota fosse alterada! Imagine que em vez de /produtos a URI da listagem fosse alterada para /produtos/lista. O que acontece quando você clicar no link de listagem do menu? Quebra! É justo, afinal, ele vai continuar apontando para a URI anterior já que estamos deixando isso fixo (ou hardcoded, como é comumente chamado) em nosso HTML. Repare no navbar do layout principal.blade.php: <ul class="nav navbar-nav navbar-right"> <li><a href="/produtos">Listagem</a></li> <li><a href="/produtos/novo">Novo</a></li> </ul> 9.6 Para saber mais: rotas nomeadas O Laravel ainda oferece uma outra alternativa para o redirect. Em vez de redirecionar para uma URI ou action, é possível definir um nome para suas rotas, ao declará-las no arquivo routes.php. Um exemplo seria: Route::get(’/produtos’, [ ’as’ => ’apelido’, ’uses’ => ’ProdutoController@lista’ ]); Esse recurso é conhecido como named route. Uma vez que tenhamos definido um apelido para a rota, podemos passar a fazer os redirects da seguinte forma: return redirect()->route(’apelido’); Linkando para ações Em vez de linkar para uma URI, é sempre mais interessante linkar para uma ação do controller, assim como fizemos no redirect. A mudança será simples, basta chamar o método auxiliar action de dentro das chaves duplas do blade. Os links devem ficar assim: 138 Casa do Código Capítulo 9. Os diferentes tipos de resposta <ul class="nav navbar-nav navbar-right"> <li> <a href="{{action(’ProdutoController@lista’)}}"> Listagem </a> </li> <li> <a href="{{action(’ProdutoController@novo’)}}"> Novo </a> </li> </ul> Faça a alteração e clique nos links para testar, o resultado será o mesmo! Se você clicar com o botão direito no navegador e escolher a opção view page source, verá que o código-fonte do HTML gerado continuará igual: 139 9.7. Outros tipos de resposta Casa do Código Fig. 9.5: Código HTML da listagem, com os links após uso do action. 9.7 Outros tipos de resposta Além de retornar uma view, ou redirecionar para outra lógica, existem momentos em que queremos enviar outros tipos de resposta, por exemplo quando estamos trabalhando com serviços ou comunicação entre sistemas. Um formato bem comum e muito utilizado atualmente é o JSON, e por esse motivo ele é o formato padrão de resposta do Laravel. Você em algum momento já experimentou retornar um objeto ou variável em vez de uma view? Se não fez, com certeza vai gostar de testar. Podemos criar um novo método no ProdutoController chamado listaJson, que pode fazer o seguinte: 140 Casa do Código Capítulo 9. Os diferentes tipos de resposta public function listaJson(){ $produtos = DB::select(’select * from produtos’); return $produtos; } Não se esqueça de que a cada novo método uma rota deve ser criada no routes.php, para que ele fique acessível pelo navegador. Route::get(’/produtos/json’, ’ProdutoController@listaJson’); Excelente, agora que já temos o método e ele está devidamente mapeado, podemos acessar sua URL pelo navegador: Fig. 9.6: Lista de produtos em formato JSON. O resultado foram todos os produtos serializados em formato json. Bem simples, não foi? Mas também podemos fazer isso explicitamente. Em vez de retornar a lista de produtos, poderíamos fazer: public function listaJson(){ $produtos = DB::select(’select * from produtos’); return response()->json($produtos); } Executando no navegador, o resultado será o mesmo! 141 9.7. Outros tipos de resposta Casa do Código Fig. 9.7: Lista de produtos em formato JSON. Claro, no caso de o JSON retornar o objeto ou lista de objetos diretamente seria muito mais simples, mas além do método json existem diversos outros que podem ser muito úteis em nosso dia a dia. Um exemplo seria: return response() ->download($caminhoParaUmArquivo); Acessar um método com esse retorno resultaria no download do arquivo presente no caminho especificado. Muito simples e prático, não acha? 142 Capítulo 10 Eloquent ORM Nossa aplicação está cada vez mais completa, porém os métodos do controller estão ficando mais complexos, com muitas linhas de código, e exigem um certo conhecimento de SQL. Veja o método adiciona, por exemplo: public function adiciona(){ $nome = Request::input(’nome’); $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); 10.1. Conheça a solução: ORM Casa do Código return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } A ordem em que os parâmetros são passados para o método insert é importantíssima! Um erro pode causar uma confusão em nosso banco de dados. Além disso, se você não está acostumado com SQL, deve estar achando bastante complicado, não é? Neste capítulo veremos como o Laravel, com ajuda de outro framework, pode nos auxiliar nesses problemas. O método adiciona, por exemplo, terminará com 2 linhas, muito mais legível e fácil de manter. 10.1 Conheça a solução: ORM Sabemos que o problema dessa abordagem é que estamos nos preocupando muito com instruções SQL, o código fica verboso e exige um conhecimento maior sobre outro paradigma com o qual, na maior parte do tempo, não queremos ficar nos preocupando. Há uma forma bem mais simples e interessante de fazer essa e outras operações no banco de dados, sem termos que nos preocupar tanto com SQL e detalhes do mundo relacional dos bancos de dados. Integrado ao Laravel temos outro framework, conhecido como Eloquent ORM. Ele cuida de encapsular boa parte da complexidade do mundo relacional, permitindo acesso aos nossos dados de uma forma bem elegante e fluente. 144 Casa do Código Capítulo 10. Eloquent ORM Frameworks ORM Por mais que o PHP já simplifique muito esse processo, ainda assim gastamos uma boa parte do nosso desenvolvimento com queries SQL e nos preocupando com detalhes de seu paradigma. Quer trocar de banco? Nem sempre isso será fácil. Apesar do padrão, muita coisa muda de acordo com o fabricante. Há ainda o desafio de pensar de duas maneiras diferentes, já que o paradigma relacional é bem diferente da orientação a objetos. Essas são algumas das muitas razões que motivaram os frameworks ORM (Object Relational Mapping), que como o próprio nome indica, fazem o mapeamento dos nossos objetos para o mundo relacional dos bancos de dados. Você vai perceber que essa abstração é bastante conveniente, por isso é tão bem-vinda em linguagens como o PHP, Java, C# e outras. 10.2 Tudo mais simples com Eloquent Quer ver como é fácil começar a usar o Eloquent? Em vez de sair escrevendo SQLs, só precisamos usar a classe de modelo que criamos anteriormente para representar nossa tabela de produtos. Veja que, assim como qualquer classe de modelo do Eloquent, a classe Produto herda de Model. Toda classe que herda de Model ganha uma série de métodos que nos auxilia a fazer operações no banco de dados. O primeiro método que usaremos será o all, que retorna todos os registros da tabela. Veja como estamos fazendo isso no método lista atualmente: public function lista(){ $produtos = DB::select(’select * from produtos’); return view(’produtos.listagem’)->with(’produtos’, $produtos); } No lugar disso, com método all, o código ficará assim: 145 10.2. Tudo mais simples com Eloquent Casa do Código public function lista(){ $produtos = Produto::all(); return view(’produtos.listagem’)->with(’produtos’, $produtos); } O resultado será o mesmo, só que agora não existe nenhum instrução SQL nesse método. Resolvemos o problema com uma simples chamada de método. Vamos testar? Fig. 10.1: Exception de classe Produto não encontrada. Ops, precisamos importar a classe Produto! <?php namespace estoque\Http\Controllers; use Illuminate\Support\Facades\DB; use estoque\Produto; use Request; class ProdutoController extends Controller { // restante do código escondido Agora sim, vamos abrir a listagem novamente: 146 Casa do Código Capítulo 10. Eloquent ORM Fig. 10.2: Listagem de produtos. Sucesso, tudo funcionando. Podemos fazer o mesmo com o método listaJson, que atualmente está assim: public function listaJson(){ $produtos = DB::select(’select * from produtos’); return response()->json($produtos); } Agora, com o método all, ele pode ficar assim: public function listaJson(){ $produtos = Produto::all(); return response()->json($produtos); } 10.3 Buscando pelo ID com Eloquent Outro ponto em que podemos tirar proveito do Eloquent é na busca de produtos pelo id, como é feito no método mostra: 147 10.3. Buscando pelo ID com Eloquent Casa do Código public function mostra($id){ $resposta = DB::select(’select * from produtos where id = ?’, [$id]); if(empty($resposta)) { return "Esse produto não existe"; } return view(’produto.detalhes’)->with(’p’, $resposta[0]); } Para isso funcionar foi necessário passarmos um array com os parâmetros pro método select, que além disso retorna um array. Muito trabalhoso, não é? Que tal trocar todo esse trabalho por um simples find($id)? public function mostra($id){ $produto = Produto::find($id); if(empty($produto)) { return "Esse produto não existe"; } return view(’produto.detalhes’)->with(’p’, $produto); } Com isso temos um código mais simples, legível e expressivo. Não se esqueça de testar: Fig. 10.3: Detalhes do produto. 148 Casa do Código Capítulo 10. Eloquent ORM 10.4 Migrando a inserção de produtos O método adiciona é um dos mais problemáticos, e com isso, o ganho será ainda maior. Lembra como ele está? Dê uma boa olhada, esse código todo vai sumir em breve! public function adiciona(){ $nome = Request::input(’nome’); $valor = Request::input(’valor’); $descricao = Request::input(’descricao’); $quantidade = Request::input(’quantidade’); DB::insert(’insert into produtos (nome, quantidade, valor, descricao) values (?,?,?,?)’, array($nome, $valor, $descricao, $quantidade)); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Vamos melhorá-lo aos poucos, a começar, tirando esse SQL daí: public function adiciona(){ $produto = new Produto(); $produto->nome = Request::input(’nome’); $produto->valor = Request::input(’valor’); $produto->descricao = Request::input(’descricao’); $produto->quantidade = Request::input(’quantidade’); $produto->save(); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } A diferença é que agora instanciamos um produto, populamos os valores da requisição e, em vez de executar um SQL, simplesmente chamamos o mé149 10.4. Migrando a inserção de produtos Casa do Código todo save. Bem melhor, mas ainda tem muito código aí, não é? Podemos reduzir ainda mais: public function adiciona(){ $params = Request::all(); $produto = new Produto($params); $produto->save(); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Note que estamos passando o array com os parâmetros da requisição no construtor do modelo nesse caso Produto. Isso é muito comum no mundo PHP e de outras linguagens, mas pode ser um pouco perigoso. Por isso, ao tentar adicionar um produto após essa alteração, o resultado será: Fig. 10.4: MassAssignmentException ao tentar adicionar. Recebemos um MassAssignmentException, mas o que isso significa? ::Mass-assignment Sendo assim, sempre que quisermos fazer a atribuição dessa forma, via mass-assignable, para nos proteger, precisamos adicionar uma propriedade chamada $fillable em nosso modelo especificando exatamente 150 Casa do Código Capítulo 10. Eloquent ORM quais atributos podem ser populados, caso contrário receberemos uma MassAssignmentException. No caso do produto, serão o nome, descrição, valor e quantidade: protected $fillable = array(’nome’, ’descricao’, ’valor’, ’quantidade’); A classe completa deve ficar assim: <?php namespace estoque; use Illuminate\Database\Eloquent\Model; class Produto extends Model { protected $table = ’produtos’; public $timestamps = false; protected $fillable = array(’nome’, ’descricao’, ’valor’, ’quantidade’); } Tudo pronto, agora a adição voltará a funcionar como esperado. 151 10.4. Migrando a inserção de produtos Casa do Código Uma outra opção: guarded Além do $fillable, você também pode declarar um atributo chamado $guarded, que faz justamente o papel inverso. Ele é uma espécie de black-list dos valores cuja atribuição você não quer permitir via massassignment. Um bom caso de uso para o $guarded seria impedir que o usuário altere o id de seu modelo. Veja como é simples: <?php namespace estoque; use Illuminate\Database\Eloquent\Model; class Produto extends Model { protected $table = ’produtos’; public $timestamps = false; protected $fillable = array(’nome’, ’descricao’, ’valor’, ’quantidade’); protected $guarded = [’id’]; } O código ficou bem mais simples, não é? public function adiciona(){ $params = Request::all(); $produto = new Produto($params); $produto->save(); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } 152 Casa do Código Capítulo 10. Eloquent ORM Mas, apesar de já estar simples, podemos enxugá-lo ainda mais. Em vez de criar uma nova instância de produto, podemos utilizar um factory method chamado create: $params = Request::all(); Produto::create($params); Nem precisamos chamar o método save! Legal, não é? Agora que está mais simples, podemos declarar o código em um único statement: Produto::create(Request::all()); Veja como nosso código final ficou mais enxuto: public function adiciona(){ Produto::create(Request::all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } 10.5 Função de remover produtos Para deixar nossa aplicação ainda mais completa e de bônus conhecer um novo método do Eloquent, que tal criarmos uma nova função de remover produtos? O fundamental nós já sabemos muito bem, criamos uma nova rota no routes.php: Route::get(’/produtos/remove/{id}’, ’ProdutoController@remove’); Note que, assim como no método mostra, estamos passando o id como path param, sendo assim podemos recebê-lo como argumento no método remove, que vamos criar no ProdutoController: public function remove($id){ // ... } 153 10.5. Função de remover produtos Casa do Código Assim como as outras operações, deletar um produto com Eloquent é bem simples. Só precisamos buscar o modelo que deve ser excluído e chamar seu método delete: public function remove($id){ $produto = Produto::find($id); $produto->delete(); } Excelente, e ao final, podemos redirecionar para a listagem: public function remove($id){ $produto = Produto::find($id); $produto->delete(); return redirect() ->action(’ProdutoController@lista’); } Quase tudo pronto, só precisamos adicionar um novo link na tabela da view listagem.blade.php. Ele ficará muito parecido com o link de exibir detalhes do produto, mudando apenas seu ícone e a action que será chamada: <table class="table table-striped ..."> @foreach ($produtos as $p) <!-- código escondido --> <td> <a href="{{action(’ProdutoController@remove’, $p->id)}}"> <span class="glyphicon glyphicon-trash"></span> </a> </td> </tr> @endforeach </table> Vamos ver o resultado? Ao recarregar a página de listagem de produtos no navegador, o novo ícone com link estará disponível: 154 Casa do Código Capítulo 10. Eloquent ORM Fig. 10.5: Listagem com icone de remover. Tente excluir um produto para testar! 10.6 Código final do ProdutoController Após essa série de mudanças com a introdução do Eloquent, o código do nosso controller de produtos ficou assim: <?php namespace estoque\Http\Controllers; use estoque\Produto; use Request; use estoque\Http\Requests\ProdutosRequest; class ProdutoController extends Controller { public function lista(){ $produtos = Produto::all(); return view(’produto.listagem’) ->with(’produtos’, $produtos); } public function mostra($id){ $produto = Produto::find($id); 155 10.6. Código final do ProdutoController Casa do Código if(empty($produto)) { return "Esse produto não existe"; } return view(’produto.detalhes’) ->with(’p’, $produto); } public function novo(){ return view(’produto.formulario’); } public function adiciona(){ Produto::create(Request::all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } public function listaJson(){ $produtos = Produto::all(); return response()->json($produtos); } public function remove($id){ $produto = Produto::find($id); $produto->delete(); return redirect() ->action(’ProdutoController@lista’); } } Mais simples, não acha? O Eloquent abstrai boa parte da complexidade de se trabalhar com instruções do banco de dados. Antes de seguir, que tal dar uma olhada na documentação desse framework? Com certeza você encontrará muita coisa legal por lá. http://laravel.com/docs/eloquent 156 Casa do Código Capítulo 10. Eloquent ORM Desafio Que tal tentar implementar a função de alterar produtos por conta própria? Esse é o momento em que você perceberá se realmente está afiado no Laravel. O método de alterar e seu formulário HTML serão muito parecidos com o de adicionar, com a diferença de que você precisará passar o id do produto como parâmetro. Ficou com alguma dúvida? Não deixe de mandar um e-mail na lista do livro. 157 Capítulo 11 Validando os dados de entrada Apesar de estar mais simples e já fazer boa parte de seu trabalho muito bem, o método adiciona ainda peca em um ponto muito importante. Os dados de entrada não são validados, permitindo que qualquer informação seja persistida no banco. Veja por exemplo o que acontece quando adicionamos um produto sem nenhum campo preenchido: Casa do Código Fig. 11.1: Produto em branco adicionado na listagem. O processo foi concluído sem que nenhum erro fosse apresentado, ou seja, um registro vazio foi adicionado no banco e ponto final. E não é só isso, tente por exemplo adicionar um produto com valor e quantidade negativa: Fig. 11.2: Produto com valor e quantidade negativa. 160 Casa do Código Capítulo 11. Validando os dados de entrada Funciona, mas não deveria. Sempre que estamos adicionando recursos no banco de dados precisamos tomar esse cuidado, de validar os valores passados pelo usuário de acordo com a nossa regra de negócio. Em alguns casos, passar um valor negativo será totalmente válido, mas não é o nosso. O mesmo para as demais regras. Mas como validar cada valor que foi passado? Claro, um if já resolveria o problema: public function adiciona(){ $valor = Request::input(’valor’); if(floatval($valor) < 0) { // volta para o formulário com uma mensagem de erro } // faz o restante do trabalho } Mas quantos desse seriam necessários para validar um formulário de 30 campos? Pense em como ficaria o método adiciona, cheio de regras de validação espalhadas nos diversos ifs. Um pesadelo de manutenção. 11.1 Validação com Laravel Se é uma necessidade de muitos, com certeza o Laravel tem algo para nos ajudar. Esse é o espírito desse e dos outros frameworks também, facilitar a vida de quem desenvolve e oferecer recursos essenciais para nossos projetos. Existem diversas formas de fazer validação dos dados, e o Validator:make é uma delas. Com ele você pode fazer algo como: $validator = Validator::make( [’nome’ => Request::input(’nome’)], [’nome’ => ’required|min:5’] ); Repare que passamos dois arrays, um com os valores e outro com as regras de validação. Nesse exemplo, o nome será obrigatório (required) e precisará 161 11.1. Validação com Laravel Casa do Código ter no mínimo 5 caracteres. Você pode ver todas as regras de validações existentes neste link: http://laravel.com/docs/validation#available-validation-rules Agora, voltando ao exemplo, o método make retorna um $validator que tem uma série de métodos fundamentais para esse trabalho. Por exemplo, se tiver um erro de validação, preciso voltar para o formulário, certo? Podemos verificar isso com o método fails: if ($validator->fails()) { return redirect() ->action(’ProdutoController@novo’); } Simples! O código do método adiciona completo ficará assim: public function adiciona(){ $validator = Validator::make( [’nome’ => Request::input(’nome’)], [’nome’ => ’required|min:5’] ); if ($validator->fails()) { return redirect() ->action(’ProdutoController@novo’); } Produto::create(Request::all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Para testar, podemos abrir a página de adição de produtos e clicar em adicionar, sem preencher nada no campo nome. Veja que ele retornará para o formulário, sem persistir o produto no banco de dados. 162 Casa do Código Capítulo 11. Validando os dados de entrada Fig. 11.3: Retornou ao formulário, porém sem exibir uma mensagem. O problema é que dessa forma o usuário não sabe o que fez de errado, precisamos mostrar uma mensagem de feedback para ele! O Validator também tem um método que nos ajuda com isso, repare: $messages = $validator->messages(); O método messages nos retorna um array com todas as mensagens de validação. Poderíamos, por exemplo, devolver essa lista de erros para a view. Mas, antes de nos preocuparmos com isso, existem outros detalhes que precisamos atacar. O grande problema dessa abordagem é que, normalmente, queremos validar mais de um campo. Veja que apenas com a validação do nome o método adiciona já ficou enorme, imagine quando adicionarmos regras pra descrição, valor, quantidade e outros campos que podem vir a existir. Vai ficar enorme! 163 11.1. Validação com Laravel Casa do Código public function adiciona(){ $validator = Validator::make( [ ’nome’ => Request::input(’nome’), ’descricao’ => Request::input(’descricao’), ’valor’ => Request::input(’valor’), ’quantidade’ => Request::input(’quantidade’) ], [ ’nome’ => ’required|min:5’ ’descricao’ => ’required|max:255’, ’valor’ => ’required|numeric’, ’quantidade’ => ’required|numeric’ ] ); if ($validator->fails()) { return redirect() ->action(’ProdutoController@novo’); } Produto::create(Request::all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Isso porque nosso produto só tem 4 propriedades, imagine se tivesse 30! Seria um caos. Utilizar o Validator dessa forma é muito simples, mas costuma ser recomendado apenas para casos simples e isolados. Nos demais casos, recomenda-se a criação de uma nova classe, especialista por aplicar essas regras de validação. 164 Casa do Código 11.2 Capítulo 11. Validando os dados de entrada Validando com Form Requests Esse tipo de classe especial, cuja responsabilidade é validar os dados do formulário enviados pela requisição, é conhecida pelo Laravel como Form Requests. É uma classe normal, que você cria dentro do diretório app/Http/Requests e deve herdar da classe Request presente neste mesmo diretório. Mas em vez de criar a classe na mão, adicionar os imports, namespace e todos esses detalhes, podemos deixar todo esse trabalho para o Artisan. Veja como é simples: php artisan make:request ProdutosRequest Muito parecido com quando estamos criando um novo modelo, mas em vez de make:model, faremos um make:request. Vamos testar? É só digitar esse comando no terminal, dentro da pasta do nosso projeto: Fig. 11.4: Criando um form request com Artisan. A mensagem request created successfully deve ser exibida e pronto, já podemos procurar pelo arquivo ProdutosRequest dentro de app/Http/Requests. 165 11.2. Validando com Form Requests Casa do Código Fig. 11.5: ProdutosRequest criado na pasta app/Http/Requests. Chamamos à classe de ProdutosRequest porque é uma prática comum utilizar esse sufixo Request, mas não é obrigatório. Você pode escolher o nome que preferir. No geral, a vantagem de seguir essa convenção é que apenas ao ler o nome da classe você já sabe do que se trata, é um form request de produtos! A classe foi criada com a seguinte estrutura: <?php namespace estoque\Http\Requests; use estoque\Http\Requests\Request; class ProdutosRequest extends Request { /** * Determine if the user is authorized * to make this request. 166 Casa do Código Capítulo 11. Validando os dados de entrada * * @return bool */ public function authorize() { return false; } /** * Get the validation rules that apply * to the request. * * @return array */ public function rules() { return [ // ]; } } Parece bem assustador, um bicho de sete cabeças! Mas no fim, você perceberá que ela é bem simples. Temos dois métodos, o primeiro, chamado authorize, é utilizado para informar se o usuário pode ou não fazer essa requisição. Ele é executado antes mesmo da validação, portanto se o deixarmos como false, ninguém vai conseguir adicionar produtos. Normalmente nesse método podemos verificar se o usuário tem permissões, fazer consultas no banco de dados etc., mas em nosso caso, sempre queremos executá-lo, portanto basta mudar seu valor para true. public function authorize() { return true; } Já no outro método, o rules, retornamos um array com as regras de 167 11.2. Validando com Form Requests Casa do Código validação, assim como estávamos fazendo no método make do Validator. Em nosso caso, será: public function rules() { return [ ’nome’ => ’required|max:100’, ’descricao’ => ’required|max:255’, ’valor’ => ’required|numeric’ ]; } Com isso, o código completo da classe ProdutosRequest ficará assim: <?php namespace estoque\Http\Requests; use estoque\Http\Requests\Request; class ProdutosRequest extends Request { /** * Determine if the user is authorized * to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply * to the request. * * @return array */ public function rules() { return [ 168 Casa do Código Capítulo 11. Validando os dados de entrada ’nome’ => ’required|max:100’, ’descricao’ => ’required|max:255’, ’valor’ => ’required|numeric’ ]; } } 11.3 Usando Form Request ao adicionar Agora que temos a classe especialista em fazer o trabalho, podemos tirar toda a complexidade das regras de validação de dentro do método do controller. Ele voltará a ficar assim: public function adiciona(){ Produto::create(Request::all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Mas de alguma forma precisamos ensinar ao Laravel que queremos aplicar as regras de validação do form request ao executar esse método e, para fazer isso, basta adicionar o ProdutosRequest como um argumento do método adiciona: public function adiciona(ProdutosRequest $request){ Produto::create(Request::all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Além disso, em vez de utilizar o Request:all() para pegar os parâmetros da requisição, usaremos o próprio form request. Veja que a mudança será mínima: 169 11.3. Usando Form Request ao adicionar Casa do Código Produto::create($request->all()); O código do método adiciona ficará assim: public function adiciona(ProdutosRequest $request){ Produto::create($request->all()); return redirect() ->action(’ProdutoController@lista’) ->withInput(Request::only(’nome’)); } Vale lembrar que precisamos adicionar o import da classe ProdutosRequest no início do nosso ProdutoController: <?php namespace estoque\Http\Controllers; use estoque\Produto; use Request; use estoque\Http\Requests\ProdutosRequest; class ProdutoController extends Controller { // restante do código Vamos testar? Abrimos o formulário novamente e tentamos adicionar um produto sem preencher nenhum dado. O resultado será: 170 Casa do Código Capítulo 11. Validando os dados de entrada Fig. 11.6: Formulário de adição sem mensagem de validação. Sem precisar configurar nada, automaticamente nossa requisição foi redirecionada de volta para o formulário! Claro, se você quiser ir pra outro lugar, bastaria adicionar um if com o método fails assim como fizemos anteriormente, mas por padrão, o form request já redireciona de volta para a página que fez a requisição. Legal, não é? Assim evitamos escrever aquele if chato. Mas ainda não está perfeito, afinal, cadê a mensagem de validação? O usuário precisa de um feedback para saber o que fez de errado. Agora sim podemos atacar esse requisito. 11.4 Exibindo errors de validação Exibir os erros é uma necessidade comum, e claro que o Laravel já pensou em uma forma de nos ajudar nisso. Você não precisa sempre adicionar as mensagens manualmente, depois de cada erro de validação. Isso é feito auto171 11.4. Exibindo errors de validação Casa do Código maticamente. Uma variável chamada errors estará disponível na view após um ou mais erros de validação. Ela tem diversos métodos, por exemplo o all, que retorna um array com todas as mensagens. Para testar, adicione a seguinte instrução antes do seu formulário de adicionar produtos: @foreach($errors->all() as $error) {{ $error }} @endforeach Agora basta tentar adicionar um produto vazio novamente: Fig. 11.7: Formulários com erros de validação. . Excelente! As mensagens de validação foram exibidas assim como esperado, mas ainda muito feio, sem nenhuma formatação e tudo na mesma linha. Podemos adicionar alguns estilos do bootstrap para deixar a aparência melhor: <div class="alert alert-danger"> <ul> 172 Casa do Código Capítulo 11. Validando os dados de entrada @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> O resultado está bem melhor! Fig. 11.8: Formulário com erros de validação. Só falta um último detalhe, que pode ser percebido ao recarregar a view do formulário: 173 11.4. Exibindo errors de validação Casa do Código Fig. 11.9: Div de erros sempre presente. Não queremos exibir a div de erros quando não existe nenhum erro de validação. Um simples if já resolverá esse problema: @if (count($errors) > 0) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif Perfeito! O código final do arquivo formulario.blade.php deve ficar assim: @extends(’layout.principal’) @section(’conteudo’) 174 Casa do Código Capítulo 11. Validando os dados de entrada <h1>Novo produto</h1> @if (count($errors) > 0) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="/produtos/adiciona" method="post"> <input type="hidden" name="_token" value="{{{ csrf_token() }}}" /> <div class="form-group"> <label>Nome</label> <input name="nome" class="form-control"/> </div> <div class="form-group"> <label>Descricao</label> <input name="descricao" class="form-control"/> </div> <div class="form-group"> <label>Valor</label> <input name="valor" class="form-control"/> </div> <div class="form-group"> <label>Quantidade</label> <input type="number" name="quantidade" class="form-control"/> </div> <button type="submit" class="btn btn-primary btn-block">Adicionar</button> </form> 175 11.5. Customizando as mensagens Casa do Código @stop 11.5 Customizando as mensagens Outro ponto importantíssimo relacionado às mensagens de validação é a flexibilidade de customizá-las de acordo com nossa vontade ou necessidade. Por exemplo, e se em vez de mostrar The nome field is required eu quiser mostrar um outro texto, ou mesmo uma mensagem em português, onde devo alterar? Existem diversas formas de fazer isso e, já que estamos usando form requests, adicionar um novo método chamado messages é uma delas. Quer ver como é simples? Vamos criar esse método dentro da classe ProdutosRequest. Ele deve retornar um array com a chave e valor que deve ser alterado. Por exemplo, se quisermos mudar a mensagem de required ele pode ficar assim: public function messages() { return [ ’required’ => ’The nome field can not be empty.’, ]; } Veja que a mudança foi mínima, antes era is required (é obrigatório) e agora mudamos para can not be empty (não pode ser vazio). Claro, se quiser, também poderia usar uma mensagem em português. Vamos testar? É só abrir o formulário novamente e tentar adicionar um novo produto sem nenhuma informação preenchida. 176 Casa do Código Capítulo 11. Validando os dados de entrada Fig. 11.10: Palavra ‘nome’ sempre aparecendo na mensagem de validação. Ops, aconteceu algo inesperado. A nova mensagem foi exibida com sucesso, mas agora ficou com o campo nome fixo. Tente adicionar um produto sem descrição, por exemplo, a mensagem ainda estará com o texto nome no lugar de descricao. Como corrigir? Em vez de utilizar o valor nome, experimente mudar para :attribute como a seguir: public function messages() { return [ ’required’ => ’The :attribute field can not be empty.’, ]; } Essa variável especial será substituída pelo valor do campo que está sendo validado. Quer ver? Tentamos acessar um produto em branco novamente e o 177 11.6. Customizando mensagens de um campo específico Casa do Código resultado foi: Fig. 11.11: Mensagem correta, com uso do attribute. Agora sim, os valores nome, descricao e valor foram exibidos corretamente. 11.6 Customizando mensagens de um campo específico Ao alterar a mensagem de required, dentro da classe ProdutosRequest, estamos aplicando isso para todos os campos obrigatórios desse formulário. Mas há momentos em que queremos customizar a mensagem apenas para um campo, como o nome. Para fazer isso, basta adicionar o prefixo nome. na chave da mensagem: 178 Casa do Código Capítulo 11. Validando os dados de entrada public function messages() { return [ ’nome.required’ => ’The :attribute field can not be empty.’, ]; } Agora essa regra será aplicada apenas para o nome! Fig. 11.12: Mensagem de validação do nome customizada. Mas ainda tem uma questão, que pode ou não ser um problema. E se eu quiser aplicar essa regra para todos os campos de nome da aplicação? Da forma como fizemos, seria necessário repetir o código em cada form request que tivermos. No lugar disso, se você quiser aplicar a alteração para a aplica179 11.7. Boa prática: experiência de uso Casa do Código ção toda, você pode adicionar a mensagem no array custom definido dentro do validation.php da pasta resources/lang/en. Ele já estará assim: ’custom’ => [ ’attribute-name’ => [ ’rule-name’ => ’custom-message’, ], ], Isso já nos dá uma dica de seu uso. Para o campo nome, por exemplo, o array ficaria assim: ’custom’ => [ ’nome’ => [ ’required’ => ’The :attribute field can not be empty.’, ], ], Legal, não é? Claro, existem diversas outras formas de se validar e customizar as mensagens do formulário. Se quiser, você pode ler um pouco mais sobre validação nessa página da documentação do framework: http://laravel.com/docs/validation 11.7 Boa prática: experiência de uso O problema do nosso código atual é que, após submeter o formulário com erros de validação, o usuário será redirecionado para o formulário novamente, porém perderá todos os dados que digitou! Quando temos poucos campos isso pode até passar despercebido, mas imagine como seria ruim ter que digitar novamente todos os valores de um formulário enorme toda vez que preencher um campo errado. A solução para isso é simples e muitíssimo recomendada. Tudo o que precisamos fazer para manter os dados da requisição anterior que deu um erro de validação preenchidos é adicionar o método old no atributo value dos nossos inputs. Um exemplo seria: <input name="nome" value="{{ old(’nome’) }}" /> 180 Casa do Código Faremos Capítulo 11. Validando os dados de entrada o mesmo com cada um dos inputs do arquivo formulario.blade.php. Ao final, nosso HTML pode ficar assim: @extends(’layout.principal’) @section(’conteudo’) <h1>Novo produto</h1> @if (count($errors) > 0) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="/produtos/adiciona" method="post"> <input type="hidden" name="_token" value="{{{ csrf_token() }}}" /> <div class="form-group"> <label>Nome</label> <input name="nome" class="form-control" value="{{ old(’nome’) }}" /> </div> <div class="form-group"> <label>Descricao</label> <input name="descricao" class="form-control" value="{{ old(’descricao’) }}"/> </div> <div class="form-group"> <label>Valor</label> <input name="valor" class="form-control" value="{{ old(’valor’) }}"/> </div> 181 11.7. Boa prática: experiência de uso Casa do Código <div class="form-group"> <label>Quantidade</label> <input type="number" name="quantidade" class="form-control" value="{{ old(’quantidade’) }}"/> </div> <button type="submit" class="btn btn-primary btn-block">Adicionar</button> </form> @stop 182 Capítulo 12 Autenticação e segurança Um ponto fundamental que ainda não exploramos no livro é quanto à autenticação e segurança de nossa aplicação. Da forma que está, qualquer um visualiza, cria ou apaga produtos, mas nem sempre podemos permitir que isso aconteça. Veremos nesse capítulo como o Laravel nos ajuda com esse trabalho, com uso de seus famosos middlewares e mecanismo de Auth. 12.1 Tela de login e autenticação A grande verdade é que tudo está pronto, em uma aplicação real você provavelmente precisará mudar um ou outro detalhe, mas boa parte do código será totalmente reutilizada em seus projetos. Para testar, precisaremos voltar aquelas linhas que apagamos do arquivo routes.php no primeiro capítulo. Não se preocupe se não lembrar, o importante é saber que quando você cria uma aplicação o seguinte conteúdo já 12.1. Tela de login e autenticação Casa do Código vem registrado nos arquivo de rotas: Route::get(’home’, ’HomeController@index’); Route::controllers([ ’auth’ => ’Auth\AuthController’, ’password’ => ’Auth\PasswordController’, ]); Copie novamente para lá. Seu routes.php pode ficar assim: Route::get(’/produtos’, ’ProdutoController@lista’); // outras rotas omitidas Route::get(’home’, ’HomeController@index’); Route::controllers([ ’auth’ => ’Auth\AuthController’, ’password’ => ’Auth\PasswordController’, ]); Ótimo! Veja que há uma rota /home apontando para o método index do HomeController, que também deve ter sido criado automaticamente com o projeto. Caso esse controller não exista em seu projeto, você pode criá-lo manualmente com o seguinte conteúdo: <?php namespace estoque\Http\Controllers; class HomeController extends Controller { public function __construct() { $this->middleware(’auth’); } public function index() { return view(’home’); } } 184 Casa do Código Capítulo 12. Autenticação e segurança Não se preocupe em entender o construtor dessa classe, pelo menos por enquanto. Em breve seu uso ficará bem claro. Além disso, veja que o método index retorna uma view chamada home. Crie também esse arquivo, chamado home.blade.php, com o seguinte conteúdo: @extends(’app’) @section(’content’) <div class="container"> <div class="row"> <div class="col-md-10 col-md-offset-1"> <div class="panel panel-default"> <div class="panel-heading">Home</div> <div class="panel-body"> You are logged in! </div> </div> </div> </div> </div> @endsection Vamos ver o que tem lá dentro? É só acessar: http://localhost:8000/home. Talvez para grande surpresa, o resultado ao tentar acessar essa URL será um redirecionamento para a tela de login. Isso mesmo, essa tela já existe e, por sinal, tudo que existe nela já funciona! 185 12.1. Tela de login e autenticação Casa do Código Fig. 12.1: View com formulário de login. Veja que, além do formulário de login, ela oferece uma opção de reset de senha e registro de novos usuários. Vamos testar? Como não temos nenhum usuário cadastrado, podemos criar um novo registro clicando na opção Register do canto direito do menu. 186 Casa do Código Capítulo 12. Autenticação e segurança Fig. 12.2: Formulário de registro de usuários. Preencha o formulário e clique no botão de submit para ver o resultado. O usuário será criado, e você será redirecionado para a tela /home, que estava protegida por login. Logo veremos essa parte de segurança, como limitar acesso aos recursos da aplicação e tudo mais, mas por enquanto, vamos entrar um pouco mais a fundo nessa funcionalidade de login. 187 12.2. Entendendo o login da aplicação Casa do Código Fig. 12.3: View padrão da URL /home. Repare que o nome de seu usuário estará disponível no menu principal e, ao ser clicado, exibirá a opção de logout. Mas onde tudo isso está definido? Como foi criado? Como modificar? 12.2 Entendendo o login da aplicação Vou responder essas perguntas por partes, ok? Primeiro vamos entender onde essas views foram definidas e alguns detalhes de sua implementação. 188 Casa do Código Capítulo 12. Autenticação e segurança Fig. 12.4: Diretório com arquivos de autenticação. Veja que a pasta resources/views/auth mantém todas as páginas relacionadas a essa lógica de autenticação. Podemos sim apagar tudo e recriar todas as views, formulários, validação etc. Mas e no próximo projeto? Apagamos e fazemos tudo de novo? Vai ser tudo muito parecido. Vale lembrar que a grande sacada do framework é essa, oferecer o que existe em comum entre todos os projetos, pra você gastar seu tempo no que realmente importa. Você pode e deve editar, adicionar novos campos e fazer qualquer adaptação que for necessário de acordo com as necessidades do seu projeto, mas com certeza isso será mais fácil agora que tudo já está pronto. Veja que a página home.blade.php é bem simples, tem apenas um painel que mostra a mensagem de que o usuário está logado: @extends(’app’) @section(’content’) <div class="container"> <div class="row"> <div class="col-md-10 col-md-offset-1"> <div class="panel panel-default"> <div class="panel-heading">Home</div> 189 12.2. Entendendo o login da aplicação Casa do Código <div class="panel-body"> You are logged in! </div> </div> </div> </div> </div> @endsection Um detalhe que quero que vejam nesse arquivo é que ele herda de app, que é o layout principal que já vem pronto no Laravel. Essa é a cereja do bolo, o lugar onde o mecanismo de autenticação do framework começa a aparecer. Abra o arquivo app.blade.php e veja que ele é muito parecido com nosso layout principal, o principal.blade.php. A grande diferença está no navbar, que faz um if verificando se o usuário está logado ou não: <ul class="nav navbar-nav navbar-right"> @if (Auth::guest()) <li><a href="/auth/login">Login</a></li> <li><a href="/auth/register">Register</a></li> @else <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> {{ Auth::user()->name }} <span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> <li><a href="/auth/logout">Logout</a></li> </ul> </li> @endif </ul> Há dois pontos importantíssimos nesse trecho de HTML e, em ambos, a interface Auth foi utilizada. Essa interface (facade) é quem nos ajuda com boa parte do trabalho de autenticação. Vamos entender como ela funciona? 190 Casa do Código Capítulo 12. Autenticação e segurança Veja que no if o método guest (convidado) é verificado. Esse método, como você já deve imaginar, determina se o usuário já está logado na aplicação. Ele é bastante útil em diferentes camadas do nosso projeto: Na view, para determinar se o nome do usuário ou link de login deve ser exibido, no controller ou qualquer camada intermediária de autorização para verificar se o usuário pode ou não acessar o conteúdo etc. Outro método interessante utilizado por essa view é o user, que retorna uma instância do usuário logado na aplicação. Repare que ele é utilizado para exibir o nome do nosso usuário no seguinte trecho do código: {{ Auth::user()->name }} Legal! Mas e quanto ao código de autenticação em si, usado para logar ou deslogar um usuário? Bem, em vez de apenas ficar na teoria, que tal criarmos o nosso próprio sistema de login só por diversão? O código será mais simples do que parece. 12.3 Criando uma lógica de autenticação Para começar, podemos criar um novo controller onde ficará toda essa lógica de autenticação. Vamos chamá-lo de LoginController e, lembrando, ele deverá ficar dentro de app/Http/Controllers. Antes de criar o arquivo e digitar tudo, que tal dessa vez usar o artisan? Assim como fizemos pra criar o modelo, também podemos usar o comando make:controller. php artisan make:controller LoginController --plain Esse --plain que passamos como argumento significa que não queremos que ele crie nenhum método. Deve ser um controller em branco. Sem essa opção, ele adicionará uma série de métodos comuns para as operações básicas de adicionar, listar, remover etc. O famoso CRUD. 191 12.3. Criando uma lógica de autenticação Casa do Código Fig. 12.5: Criando controller com Artisan. O resultado será uma mensagem de sucesso e pronto, podemos abrir o arquivo LoginController que já estará assim: <?php namespace estoque\Http\Controllers; use estoque\Http\Requests; use estoque\Http\Controllers\Controller; use Illuminate\Http\Request; class LoginController extends Controller { // } Vamos agora criar um novo método, chamado login: public function login() { // código vai aqui } Em um caso tradicional criaríamos também um método que envia para a view de formulário, que faria um POST com os dados para essa ação de login, mas como estamos apenas testando e não queremos reinventar a roda , passaremos o usuário e senha direto pela URL. No arquivo de rotas, vamos ensinar ao Laravel que ao acessar a URI /login nosso método de login deverá ser chamado: Route::get(’/login’, ’LoginController@login’); 192 Casa do Código Capítulo 12. Autenticação e segurança Quase tudo pronto, já podemos começar o código de autenticação. No método login, queremos pegar o email e password, verificar se ele é valido e, em caso de sucesso, retornar uma mensagem com o nome do usuário logado; caso contrário, um erro. Em resumo, o código ficará assim: public function login() { $credenciais = Request::only(’email’, ’password’); if( /*credenciais são válidas*/ ) { return "Usuário NOME logado com sucesso"; } return "As credencias não são válidas"; } Mas como saber se o usuário existe em nosso banco de dados? Poderíamos sim escrever um SQL que procura o usuário por e-mail e senha, mas o Auth já cuida desse trabalho para não termos que nos preocupar nem com isso. Quer ver como é simples? Basta passar o array com e-mail e senha para seu método attempt: public function login() { $credenciais = Request::only(’email’, ’password’); if(Auth::attempt($credenciais)) { return "Usuário NOME logado com sucesso"; } return "As credencias não são válidas"; } Além de e-mail e senha, você pode passar qualquer informação válida do usuário como parâmetro no array de credenciais. Vale lembrar que para usar o Auth dentro do controller precisaremos fazer o import. A classe LoginController completa deve ficar assim: 193 12.3. Criando uma lógica de autenticação Casa do Código <?php namespace estoque\Http\Controllers; use estoque\Http\Requests; use estoque\Http\Controllers\Controller; use Auth; use Request; class LoginController extends Controller { public function login() { $credenciais = Request::only(’email’, ’password’); if(Auth::attempt($credenciais)) { return "Usuário NOME logado com sucesso"; } return "As credencias não são válidas"; } } Vamos testar? Primeiro tente acessar a lógica de login com um usuário inválido. Um exemplo seria: http://localhost:8000/login?email=teste@teste. com.br&password=123456 Fig. 12.6: Mensagem de credencias inválidas. A mensagem As credencias não são válidas foi exibida como esperado. Experimente agora passar o login e senha que você registrou ante194 Casa do Código Capítulo 12. Autenticação e segurança riormente. Em meu caso, será: http://localhost:8000/login?email=rodrigo. turini@caelum.com.br&password=123456 Fig. 12.7: Mensagem de sucesso, porém com a palavra ‘nome’ fixo. Sucesso! Mas no lugar de NOME, queremos mostrar o nome do usuário autenticado. Lembra como se faz? É só utilizar o método user e acessar a propriedade que você quiser. O código ficará de seguinte forma: if(Auth::attempt($credenciais)) { return "Usuário ". Auth::user()->name ." logado com sucesso"; } Agora sim, ao acessar a URL novamente o resultado será: Fig. 12.8: Mensagem de confirmação de login. Há mais um monte de recursos interessantes no Auth. Por exemplo, se você quiser usar a função “lembrar a senha” na sua aplicação, você pode passar 195 12.3. Criando uma lógica de autenticação Casa do Código um boolean como segundo argumento para o método attempt. O usuário vai ficar autenticado por tempo indefinido ou até alguém manualmente fazer o logout. if(Auth::attempt($credenciais, true)) { //... } Por sinal, fazer o logout também é bem simples! Repare: Auth::logout(); Há ainda uma série de métodos que podem e com certeza serão úteis. Alguns deles são: // verifica apenas se as credenciais são // válidas, sem necessariamente se logar Auth::validate($credentials); // para logar um usuário de determinado id Auth::loginUsingId(7); // para ver se o usuário está logado Auth::check(); // ou então, verificar o próprio usuário Auth::user(); Legal, não é? Mas vale lembrar de que a parte de autenticação já está toda pronta, dificilmente vamos usar todos esses métodos. Agora que já conhecemos um pouco de como o Laravel cuida da autenticação, vamos entrar em outro ponto muito importante: a autorização. Não basta estar logado, é necessário estabelecer o que o usuário pode ou não acessar. Se quiser, aproveite para explorar um pouco mais antes de prosseguir. Que tal mudar o template das páginas de autenticação para usar o principal? Além disso, você pode adicionar o if nesse layout para verificar se o usuário está logado ou não, mostrando o nome ou os links de login assim como o 196 Casa do Código Capítulo 12. Autenticação e segurança app.blade.php faz. Fique à vontade para fazer essas e outras alterações no sistema, é uma excelente oportunidade para relembrar alguns recursos que vimos até aqui. 12.4 Autorização com Middlewares A questão do login foi explorada, sabemos que isso já está pronto, mas mesmo sem estarem logados os usuários da nossa aplicação ainda conseguem visualizar, adicionar e excluir produtos. Não queremos que isso aconteça, esses devem ser privilégios exclusivos para quem está autenticado. No início do capítulo, quando acessamos a URI /home pela primeira vez, fomos redirecionados para uma tela de login. Isso aconteceu porque, de alguma forma, foi configurado que para acessar essa lógica era necessário estar autenticado no projeto. Queremos o mesmo, mas onde configurar? Poderíamos fazer isso de diversas formas. Um exemplo seria validando no próprio método, como no exemplo a seguir do método remove: public function remove($id){ if (Auth:guest()) { return redirect(’/auth/login’); } $produto = Produto::find($id); $produto->delete(); return redirect()->action(’ProdutoController@lista’); } Veja que um simples if resolve o problema. Ao tentar remover um produto sem estar logado, o usuário será redirecionado para a tela de login. É exatamente o que precisamos! Porém, imagine só a quantidade de vezes que vamos precisar repetir esse if? Muitas. E quanto maior for o projeto, mas ifs repetidos. Seria inviável. É aí que entra o Middleware. Ele funciona como um filtro, por onde passam todas as requisições HTTP antes de chegar em nossos controllers. O nome pode parecer intimidador, mas não há nada de muito complicado. Quer ver? Antes de sair usando os Middlewares existentes, vamos criar nossa própria regra de autenticação. 197 12.4. Autorização com Middlewares Casa do Código Em vez de criar a classe manualmente, podemos usar o comando make:middleware do Artisan para fazer esse trabalho por nós. Veja como é simples: php artisan make:middleware AutorizacaoMiddleware Fig. 12.9: Middleware de autorização[[ Agora basta abrir o arquivo AutorizacaoMiddleware dentro do diretório app/Http/Middleware e a seguinte estrutura estará pronta: <?php namespace estoque\Http\Middleware; use Closure; class AutorizacaoMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { return $next($request); } } 198 Casa do Código Capítulo 12. Autenticação e segurança Todo o trabalho acontece nesse método chamado handle, que é executado sempre que uma nova requisição é feita em nossa aplicação. Repare que ele recebe como argumento a $request com os dados da requisição atual e também uma Closure chamada $next. Esse segundo argumento é o responsável por determinar ou não se nosso controller será chamado. Se o método handle não retornar $next($request) da forma que está, a aplicação ficará parada nesse ponto. De forma prática, podemos fazer algo como: public function handle($request, Closure $next) { if(/* não pode acessar */) { return redirect(’/auth/login’); } return $next($request); } Veja que, nesse caso, quando o usuário não tiver acesso a uma determinada lógica ou URI, redirecionamos para a página de autenticação. Caso contrário, retornamos o $next($request) que continuará com a execução do nosso código normalmente. Simples, não é? Mas o que colocar nesse if? A resposta é: depende da nossa necessidade. Você pode fazer desde consultas no banco de dados para verificar o privilégio do usuário e de acordo com ele permitir o acesso ou não, até um simples Auth:guest para verificar se o usuário está logado, que é justamente o que precisamos neste momento. O código ficará assim: public function handle($request, Closure $next) { if(\Auth::guest()) { return redirect(’/auth/login’); } return $next($request); } Excelente, mas agora que o middleware está criado, todas as requisições vão passar por ele? Nós somos quem decidimos. Se quisermos fazer isso, 199 12.4. Autorização com Middlewares Casa do Código bastaria adicionar o nome dessa nossa classe no atributo $middleware de uma classe chamada Kernel, presente no diretório app/Http. Ao abrir essa classe, ela estará parecida com: <?php namespace estoque\Http; class Kernel extends HttpKernel { /** * The application’s global HTTP middleware stack. * * @var array */ protected $middleware = [ // outros registros omitidos ’Illuminate\Session\Middleware\StartSession’, ’Illuminate\View\Middleware\ShareErrorsFromSession’, ’estoque\Http\Middleware\VerifyCsrfToken’, ’estoque\Http\Middleware\AutorizacaoMiddleware’, ]; /** * The application’s route middleware. * * @var array */ protected $routeMiddleware = [ ’auth’ => ’estoque\Http\Middleware\Authenticate’, ’auth.basic’ => ’Illuminate\Auth\Middleware \AuthenticateWithBasicAuth’, ’guest’ => ’estoque\Http\Middleware \RedirectIfAuthenticated’, ]; } Observe que essa classe possui dois arrays, chamados $middleware e $routeMiddleware. O primeiro é utilizado sempre que quisermos que um middleware seja executado em todas as requisições. Bastaria adicionar nossa classe nesse array e pronto, mas isso seria um grande problema. 200 Casa do Código Capítulo 12. Autenticação e segurança O nosso código sempre verifica se o usuário está logado e, caso não esteja, redireciona para a tela de login. Como toda requisição passará pelo middleware, ele novamente verificará se está logado, e redirecionará outra vez para a tela de login. O código entrará em looping de redirect. Fig. 12.10: Página em redirect loop. E agora? A solução é simples, bastaria adicionar uma condição no if que verifique que a request acessada não é a de auth/login: public function handle($request, Closure $next) { if(!$request->is(’auth/login’) && \Auth::guest()) { return redirect(’/auth/login’); } return $next($request); } Excelente, problema do loop resolvido. Faça as alterações para testar, ao acessar qualquer URL enquanto estiver deslogado, você será redirecionado para a tela de login. 201 12.5. Registrando o middleware para rotas específicas Casa do Código 12.5 Registrando o middleware para rotas específicas Imagine agora que queremos bloquear apenas algumas rotas, como a de adicionar e remover produto, mas as demais devem ser liberadas. O usuário não precisará estar logado para acessar a listagem, por exemplo. Da mesma forma como fizemos com a URI de login, podemos ir adicionando condições para liberar as páginas de listagem, detalhes do produto ou qualquer outra que quisermos, mas o problema dessa solução é que a todo momento que uma nova página que não deve ser bloqueada for criada, precisaremos lembrar de adicionar uma nova condição no middleware. Além disso, o código ficaria bem feito, cheio de ifs ou com um único if enorme! No lugar disso, o Laravel nos oferece a possibilidade de ativar um middleware para rotas específicas. Lembra que a classe Kernel tinha dois arrays, o middleware e o $routeMiddleware? Pois bem, esse segundo atributo serve justamente para isso. Vamos remover nossa classe do array middleware e, em vez disso, adicioná-la ao $routeMiddleware como a seguir: protected $routeMiddleware = [ ’auth’ => ’estoque\Http\Middleware \Authenticate’, ’auth.basic’ => ’Illuminate\Auth\Middleware \AuthenticateWithBasicAuth’, ’guest’ => ’estoque\Http\Middleware \RedirectIfAuthenticated’, ’nosso-middleware’ => ’estoque\Http \Middleware\AutorizacaoMiddleware’, ]; Veja que já existem outros middlewares registrados nesse array também, logo falaremos sobre eles. O importante agora é perceber que nesse caso associamos a chave nosso-middleware ao nosso AutorizacaoMiddleware. 202 Casa do Código Capítulo 12. Autenticação e segurança Associando middlewares com a rota Agora que já está registrado, precisamos associar o nosso-middleware às rotas que devem ser filtradas. Existem diversas formas de fazer isso, e uma delas é pelo arquivo routes.php. Route::get(’/produtos/remove/{id}’, [ ’middleware’ => ’nosso-middleware’, ’uses’ => ’ProdutoController@remove’ ]); O problema dessa abordagem é que além de poluir bastante o nosso arquivo de rotas, ainda nos obriga a repetir essas linhas do middleware em cada uma delas. Associando middlewares ao controller Em vez disso, podemos partir para um caminho bem mais interessante, que é registrar o middleware no construtor do nosso controller. Veja como é simples: <?php namespace estoque\Http\Controllers; use estoque\Produto; use Request; use estoque\Http\Requests\ProdutosRequest; class ProdutoController extends Controller { public function __construct() { $this->middleware(’nosso-middleware’); } // restante do código omitido } Mas ainda não é o que queremos, pois isso fará com que todos os métodos do controller passem pelo filtro do middleware, ou seja, ao tentar acessar a listagem novamente seremos redirecionados para a tela de login. 203 12.5. Registrando o middleware para rotas específicas Casa do Código Para evitar isso, podemos passar como segundo argumento apenas os métodos em que queremos aplicar esse middleware: public function __construct() { $this->middleware(’nosso-middleware’, [’only’ => [’adiciona’, ’remove’]]); } Neste caso, apenas o método adiciona e remove serão filtrados, todos os demais funcionarão normalmente mesmo sem o usuário estar logado. Além do only, você também pode utilizar a chave except para dizer quais métodos não devem ser filtrados. Vamos testar as alterações? Primeiro acessando a listagem de produtos. Tudo deve funcionar: Fig. 12.11: Listagem de produtos. Agora tente remover um dos produtos: 204 Casa do Código Capítulo 12. Autenticação e segurança Fig. 12.12: Página de login. Sucesso! Fomos redirecionados para a tela de login. 12.6 Utilizando o middleware padrão Vimos como criar e registrar nosso próprio middleware, afinal, é um recurso importantíssimo. Mas, na realidade, para um caso simples como esse o código todo já está pronto, portanto não precisaríamos desse trabalho todo. Experimente mudar o construtor do controller para que utilize um middleware chamado auth: <?php namespace estoque\Http\Controllers; use estoque\Produto; use Request; use estoque\Http\Requests\ProdutosRequest; class ProdutoController extends Controller { public function __construct() { $this->middleware(’auth’, 205 12.6. Utilizando o middleware padrão Casa do Código [’only’ => [’adiciona’, ’remove’]]); } // restante do código omitido } O efeito será o mesmo. Legal, não é? O Laravel nos entrega de mão beijada toda a parte de autenticação e autorização. 206 Capítulo 13 Mais produtividade com Artisan Durante o livro, vimos um recurso muito atrativo do Laravel, o Artisan. Essa ferramenta de linha de comando nos torna ainda mais produtivos, pois cuida de boa parte do código de boilerplate. Ao criar um novo modelo, por exemplo, precisamos lembrar de adicionar o namespace, dizer que ele herda de Model, adicionar o import para a superclasse etc. Muito trabalho, não é? E é sempre a mesma coisa, só muda o nome da classe. É aí que o Artisan entra, um simples comando e pronto, toda essa rotina será feita para nós. Mas a ferramenta não para por aí. Além de criar modelos, controllers, form requests, entre outros, ela também nos oferece diversos recursos como o serve, utilizado para rodar a aplicação no servidor de desenvolvimento do Laravel. Muito conveniente! O Artisan é um verdadeiro cinto de utilidades do programador produtivo. 13.1. Como lembrar de todos os comandos? 13.1 Casa do Código Como lembrar de todos os comandos? É fundamental ter em mente que quanto mais comandos você conhecer, mais proveito você vai tirar da ferramenta. Mas, levando em consideração que existem diversos, como seria possível decorar todos? A resposta é simples: não decore! O único comando de que você precisa se lembrar é o list, que retorna uma lista com todas as opções disponíveis. Sabendo ler e interpretar a saída desse comando, você consegue usar qualquer outro. Vamos pôr as mãos na massa? Abra o terminal e, de dentro da pasta de seu projeto, execute o comando: php artisan list Fig. 13.1: Listagem de comandos do Artisan. Uma lista nem um pouco pequena de comandos disponíveis será exibida. Aqui vai um resumo sobre os principais deles e também alguns exemplos práticos. Você pode e deve ir testando todos os comandos que quiser conhecer melhor. • down: coloca a aplicação em modo de manutenção. Para testar, experimente rodar php artisan down e acessar qualquer URL da sua aplicação. O resultado será parecido com: 208 Casa do Código Capítulo 13. Mais produtividade com Artisan Fig. 13.2: Aplicação do modo de manutenção. • up: tira a aplicação do modo de manutenção. Se você fez o teste anterior, basta rodar php artisan up para que tudo volte ao normal. Fig. 13.3: Comandos down e up pelo terminal. • help: exibe informações de ajuda para um comando. Por exemplo, se eu quero saber como o comando php artisan list funciona, basta digitar php artisan help list. Os possíveis argumentos, opções e alguns exemplos serão exibidos como a seguir: 209 13.1. Como lembrar de todos os comandos? Casa do Código Fig. 13.4: Informações de ajuda do comando list. • tinker: interage com sua aplicação. Esse comando é muito útil e poderoso. É possível executar querys no banco de dados, utilizar qualquer classe do projeto etc. 210 Casa do Código Capítulo 13. Mais produtividade com Artisan Fig. 13.5: Acessando dados do banco pelo Artisan Tinker. • list: lista todos os comandos. • optimize: otimiza a performance do framework. • serve: sobe o servidor de desenvolvimento do PHP. • app:name: adiciona o namespace da aplicação. Os comandos a seguir, que inclusive já utilizamos, têm como propósito a criação de classes. • make:console: cria um novo comando do Artisan. • make:controller: cria um novo controller. • make:middleware: cria um novo middleware. • make:model: cria um novo modelo do Eloquent. 211 13.1. Como lembrar de todos os comandos? Casa do Código • make:request: cria um novo form request. Há ainda opções interessantes para controle de cache e otimizações. • cache:clear: limpa o cache da aplicação. • config:cache: cria um arquivo de cache deixando a consulta de configurações mais rápida. • config:clear: remove o arquivo de cache de configurações. Por fim, alguns que nos ajudam com rotas: • route:cache: cria um arquivo de cache para deixar o registro de rotas mais rápido. • route:clear: remove o arquivo de cache de rotas. • route:list: lista todas as rotas registradas. Este comando é especialmente útil! Experimente executar php artisan route:list para ver o resultado. Além de exibir as URIs, os métodos HTTP, actions e middleware associados a eles serão detalhados. Esses são apenas alguns dos muitos comandos existentes, aproveite para explorar todas essas funcionalidades. Conhecer recursos que os frameworks nos oferecem é um grande diferencial, pois torna nosso dia a dia muito mais produtivo e divertido. 212 Capítulo 14 Os próximos passos Existem diversas formas para você continuar seus estudos. Eu recomendo fortemente que você exercite todos os códigos escritos nos capítulos desse livro, que podem ser encontrados no repositório: https://github.com/Turini/estoque-laravel Para criar uma intimidade ainda maior com o framework, experimente passear por sua documentação e testar cenários além dos aqui sugeridos. Coloque a mão na massa. A partir de agora, descobrir novos recursos, utilizar novos frameworks etc. passa a ser seu trabalho do dia a dia. Bons estudos!