Injeção de dependências e ORM com Symfony
Um dos primeiros problemas a ser resolvido em qualquer sistema é a comunicação com o banco de dados. Nosso sistema TikTok® já possui um modelo de Funcionário mas ainda não conseguimos efetivamente criar e guardar funcionários.
Nesse post vamos aprofundar em como persistir instâncias de uma classe no banco de dados com Symfony.
Renderizando formulários
Para gente conseguir criar um Funcionario, precisamos saber qual é o nome e a data de nascimento dele. Normalmente, quando precisamos receber dados do usuário, criamos um formulário com os campos necessários.
Para mostrar esse formulário no navegador, vamos criar uma rota responsável por chamar o Twig:
Na própria rota podemos especificar os métodos HTTP aceitos! No nosso caso, como queremos apenas mostrar o formulário utilizamos o método GET.
E, finalmente, um Twig com o formulário que receba os dados:
Acessando a url /funcionario/novo temos a tela:
Recebendo os dados do formulário
Ao clickar no botão criar, precisamos enviar os dados do funcionário para a rota /funcionario/novo. Mas, dessa vez, o método HTTP ideal é o POST.
Para isso, podemos criar uma outra rota, com o mesmo nome, mas que receba post. Assim cada rota fica com uma responsabilidade bem definida.
Uma carrega o formulário e a outra persiste os dados. Com isso, teremos duas rotas:
Acessando o request
Perfeito, criamos as duas rotas, cada uma com sua responsabilidade. Mas, precisamos criar um novo funcionário com todos os dados e ainda mandar ele pro banco!
Criar um funcionário é facil, basta dar new. Mas, como pegar os dados do formulário?
Todos os dados que enviamos, estão no Request. Graças a injeção de dependências do Symfony, conseguimos receber o Request inteiro como parâmetro na própria action!
Injeção de dependência?
O processo parece bem mágico, certo? Quem criou esse request? como ele foi parar ai? A magía toda acontece por baixo dos panos.
Quando acessamos uma action, o Symfony verifica o tipo de cada parâmetro e da new pra gente. O Request é fácil porque esse o framework já tem na mão. Afinal, é o framework quem processa todas as requisições.
Mas, a injeção de dependências vai muito além. Podemos configurar o framework para que injete qualquer classe nossa também!
Persistência de entidades no banco
Tendo o Request em mãos, podemos pegar qualquer dado dentro dele a partir do método get
Perfeito, temos o funcionário pronto. Mas, ainda falta mandar pro banco.
Conectando com o banco
Normalmente, quando falamos de conexão com banco de dados a primeira coisa que vem na cabeça é: mysqli ou PDO?
Aqui, poderíamos criar uma conexão (tanto com mysqli quanto com PDO), ir no banco, criar a tabela funcionário, escrever uma query para inserir um funcionário pela action, jogar os dados no banco e correr pro abraço.
Porém, o processo de escrever querys, criar tabelas e gerenciar o banco de dados costuma ocupar muito tempo do nosso dia-a-dia.
Doctrine
Para resolver toda essa parte de: criar as tabelas necessárias, os campos e gerar a gigantesca maioria das queries que precisaríamos escrever existem os ORMs (Object Relacional Mapper).
O Symfony trabalha muito bem com o Doctrine, um dos ORMs mais famosos para php, que na ultima versão veio bastante influenciado pela JPA, uma especificação bem famosa (e muito boa) do mundo Java para persistência de dados.
Como de costume, para adicionar o Doctrine no nosso sistema, basta pedir pro composer
Configurando o Doctrine
Ao terminar a instalação, ja temos nosso arquivo de configuração:
Aqui vão todas as configurações relacionadas a banco! Inclusive, a url de conexão definida em env(DATABASE_URL), que por padrão vem vazia.
É nessa url que vamos indicar pro doctrine aonde se conectar, com qual usuário e senha. Algo como:
mysql://usuario_do_banco:senha@ipDoBanco:3306/nomeDaBase
Como essa variável é uma variável de ambiente(environment), podemos sobrescreve-la no arquivo .env, disponível na raiz do projeto:
No nosso caso, vamos conectar local mesmo com usuário root e senha root e nosso banco se chamará Tiktok
Criando o banco
Tendo tudo configurado, basta pedirmos para o doctrine criar o banco pra gente através do comando bin/console doctrine:database:create
E pronto! Sem precisar de uma linha de SQL, criamos nosso banco de dados =)
Mapeando entidades
Tendo o banco criado, precisamos indicar pro Doctrine qual classe vai virar uma tabela. Para isso, basta utilizarmos a anotação Entity, da classe Doctrine\ORM\Mapping:
Perfeito, já indicamos a tabela. Mas, e as colunas?
É comum que os atributos da classe acabem virando uma coluna, por isso precisamos indicar o tipo de cada coluna, com a anotação Column
Pra finalizar, precisamos apenas indicar quem será o identificar único(mais conhecido como id) da tabela. Isso significa que, agora, nosso funcionário deve ter um campo id:
Criando a tabela
Agora que indicamos tudo pro doctrine, falta mandar ele atualizar o banco de dados criando nossa tabela com as colunas. Para isso, podemos utilizar o comando doctrine:schema:update
Persistindo entidades
Temos tabela, entidade, ORM, formulário mas ainda não mandamos nada pro banco de dados!
Agora que temos tudo configurado é bem simples salvar nosso funcionário. Basta chamar o Doctrine pelo próprio controller e persistir
E, por fim, basta redirecionar para a tela que mostra usuarios
return $this->redirect("/funcionario/mostra/".$funcionario->getNome());
Assim, ele já vem com o nome do funcionário que acabamos de criar!
Melhorando a action mostra
Agora que temos o Doctrine integrado no nosso sistema, podemos receber o identificador do funcionário na action mostra. Assim, a gente consegue buscar o funcionário no banco de dados e devolver todos os dados de forma dinâmica na view:
Bem melhor, certo? Pode ficar melhor ainda. Ao invés de termos que buscar o funcionário na mão, podemos pedir para que o Symfony injete o funcionário na nossa action!
Por baixo dos panos, o que o Symfony faz é exatamente o que a gente fez. Chama o Doctrine, busca pelo id que foi passado na rota e injeta na nossa action.
Próximos passos
Agora que temos o banco de dados integrado pelo Doctrine no nosso sistema, vamos olhar com um pouco mais de carinho para as Views. Adicionar um pouco de estilo com Bootstrap e aprofundar mais nos Twigs.
E ai, o que você achou da injeção de dependências do Symfony? E do Doctrine? Trabalhar com ORMs pode reduzir muito nosso tempo de desenvolvimento no dia-a-dia.
Ah! O código pronto desse post você pode encontrar versionado no meu git!