Upload de arquivos com Symfony
Um dos primeiros desafios de quem está começando na web (ou em algum framework) é realizar upload de arquivos. Além de ser uma dúvida bem comúm lá no forum da alura.
Como realizar o processo todo? Como guardar a imagem no banco? Como exibir? Nesse post vamos passar por todo o processo de upload de imagens com Symfony 4.
Criando o formulário
O primeiro passo de todo upload, seja lá qual for, sempre passará por uma requisição HTTP.
E, para enviar dados nada melhor que um formulário ou Ajax. A vantagem de formulários no Symfony é que temos como representar tudo no back-end com os FormTypes!
Ou seja, podemos criar um formulário que contenha nossa imagem. Algo como:
E renderizar na view todo o formulário:
Porém, se executarmos essa action temos na view apenas um campo de texto!
Especificando tipos no FormType
Por padrão, o Symfony sempre traz campos do tipo texto pra gente na view. Mas, no nosso caso, queriamos um campo que recebesse um arquivo!
Para isso, podemos dizer pro Symfony qual é tipo o campo que está sendo adicionado recebe, no segundo parametro do método add =)
No nosso caso, queremos o tipo Arquivo (FileType).
Agora, como na view estamos apenas chamando o formulário criado, nossa alteração ja deve ser propagada:
Além do FileType, existem muitos outros como MoneyType(para dinheiro), TextType, ChoiceType(para selects,checkboxes,etc), entre outros =)
Recebendo o arquivo na action
Perfeito, criamos o formulário renderizamos com o tipo certo. Mas, ao clickar no botão enviar, o que acontece? Nada!
Na nossa action, podemos validar os dados enviados no formulário e, ao mesmo tempo, verificar se ele foi enviado =)
Uma prática muito comúm no Symfony, com exemplos na própria documentação, é utilizar uma mesma rota para exibir e processar o formulário com a utilização de um método que verifica se o formulário foi enviado( isSubmited() ) naquela requisição:
Essa é uma prática bem questionável já que estamos acumulando muita responsabilidade na indexAction.
Como comentamos bastante nos cursos da caelum,o ideal sempre é quebrar as funcionalidades em rotas diferentes. Por isso, vamos dizer qual é a rota do formulário explicitamente:
E, finalmente, criamos uma action para lidar apenas com o envio da imagem:
Processando o upload
Perfeito, temos as responsabilidades isoladas!
E, caso o formulário esteja válido, entramos no if.
Mas, o que precisamos fazer ao entrar naquele caso? Se o formulário foi enviado e está válido precisamos pegar a imagem e guardar ela no banco de dados. Certo? Errado!
Guardar imagem no banco?
Guardar a imagem diretamente no banco de dados implica no processo de transformar essa imagem em texto! Esse texto normalmente é uma representação em binário.
Mas para guardar uma imagem em um binário vai muito zero e um. Por isso, o tipo reservado pra isso no Mysql costuma ser o BLOB (Binary Large Object).
Realmente precisamos guardar a imagem em algum lugar, mas podemos guardar na memória da máquina!
Guadar o caminho no banco!
Agora, no banco de dados, a única coisa que precisaremos guardar é o caminho da imagem dentro da máquina =)
Ou seja, precisamos pegar o arquivo que veio no upload, pra isso basta usar o método getData do form:
Como usamos o FileType do Symfony, o conteudo disponivel em foto é do tipo UploadedFile!
Uma classe que já vem com diversos métodos pra ajudar a gente! Como, por exemplo, um método que devolve o nome completo do arquivo getClientOriginalName():
E um método que move( move() ) o arquivo pro lugar que a gente quiser:
Para garantir, antes de passar o diretório public/uploads/img, passamos todo o caminho do projeto no sistema operacional. Assim, não importa aonde o sistema vai ser executado, o upload sempre vai funcionar=)
No nosso caso, estamos movendo o arquivo para a pasta uploads/img dentro da pasta public!
Essa pasta não vem por padrão no framework, mas é legal ter ela (ou alguma do tipo) disponivel.
Desse jeito, a gente evita um banco super populado e deixamos a responsabilidade de guardar arquivos pro sistema operacional!
E o banco de dados?
Aqui, a gente tem um sistema que recebe uma imagem e joga pra uma pasta funcionando. Mas, como encontrar essa imagem depois? Seria legal ter alguma forma de persistir essa imagem no banco de dados!
Para mandar qualquer coisa pro banco, precisamos do Entity Manager para persistir:
Mas, se executarmos a action agora, temos a exception:
Isso porque a classe UploadedFile que estamos manipulando não está no nosso sistema! Ela faz parte dos componentes do Symfony e por isso não pode ser persistida.
Se quisermos representar essa imagem que estamos fazendo upload no nosso sistema, precisamos criar uma classe pra ela:
Aqui, estamos dizendo que Imagem é, finalmente, uma entidade mapeada pelo Doctrine, além disso ela tem uma coluna com o caminho.
Agora, basta criarmos uma imagem, e definir o caminho dela. Assim, podemos passar nossa entidade imagem para o Doctrine persistir:
E, ao realizar clickar no botão enviar, conseguimos verificar nossa imagem na pasta uploads:
E, também é possivel verificar no banco de dados, com o caminho completo:
Considerações finals
Aqui, já temos tudo que precisamos para trabalhar com a imagem no nosso sistema.
Caso seja necessario imprimir alguma imagem, basta usarmos a entidade imagem e pegar o caminho. Algo como:
<img src="{{ imagem.caminho }}" alt="">
Também é possivel utilizar essa entidade em composição. Por exemplo,
<img src="{{ usuario.imagem.caminho }}" alt="">
Basta que a classe Usuario tenha um atributo do tipo Imagem =)
E ai, o que acharam do upload de imagens no Symfony? Conhece algum outro jeito? Compartilha aqui com a gente!