Tableless

Busca Menu

Iniciando com Symfony 2 – Parte 07

Seja o primeiro a comentar por

No tutorial anterior, estilizamos nossas páginas, index e show, e incluímos um imagem para apresentar nossos posts no index, porém colocamos esta imagem pelo código fonte, agora vamos fazer algumas configurações, para que, na criação dos posts, tenha a opção de fazer o upload de uma imagem, para ser apresentada como capa de nossos posts.

Configurando a entidade Post

Para criarmos um upload de imagem, vamos usar o componente http-foundation do Symfony, e usar sua classe UploadedFile em nossa entidade Post.

Para isso, vamos entrar em nossa entidade Post, caminho: src/Tableless/ModelBundle/Entity/Post.php.
Com a entidade Post aberta vamos dar um use em UploadedFile, veja na linha 7:

<?php 

namespace Tableless\ModelBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Symfony\Component\Validator\Constraints as Assert; 
use Symfony\Component\HttpFoundation\File\UploadedFile; 


/** 
 * Post 
 * 
 * @ORM\Table(name="post")
…

Precisamos criar dois atributos privados, $cover, e $file , e inserir as annotations correspondentes, o atributo $cover, receberá o nome da imagem, e o $file o aquivo com um limite de tamanho, veja abaixo:

    /** 
     * @var string 
     * 
     * @ORM\Column(name="cover", type="string", length=255, nullable=true) 
     */ 
    private $cover; 
    
    /** 
     * @Assert\File(maxSize="1000000") 
     */ 
    private $file;

    ...

Vamos fazer os Getters and Setters desses atributos.
O setFile receberá um parâmetro $file, esse parâmetro será do tipo UploadedFile, e caso não passamos uma imagem, poderá ser nulo, veja os getters and setters criados:

     ...

    /** 
     * Get cover 
     * 
     * @return string 
     */ 
    public function getCover() 
    { 
        return $this->cover; 
    } 

    /** 
     * Set cover 
     * 
     * @param string $cover 
     * @return Image 
     */ 
    public function setCover($cover) 
    { 
        $this->cover = $cover; 
    } 

    /** 
     * Get file. 
     * 
     * @return UploadedFile 
     */ 
    public function getFile() 
    { 
        return $this->file; 
    } 

    /** 
     * Set file. 
     * 
     * @param UploadedFile $file 
     */ 
    public function setFile(UploadedFile $file = null) 
    { 
        $this->file = $file; 
    }

    ...

Precisamos obter o caminho relativo do upload, ou seja, a pasta para onde as imagens serão enviadas; para isso vamos criar o método protegido getUploadPath(), que nos retornará essa pasta. Veja abaixo:

    ...

    /** 
     * Relative path. 
     * Get web path to upload directory. 
     * 
     * @return string 
     */ 
    protected function getUploadPath() 
    { 
        return 'uploads/covers'; 
    }

    ...

Temos que obter o caminho absoluto, para fazer o upload de nossas imagens, que ficará na pasta web, para isso vamos criar o método protegido getUploadAbsolutePath(), que nos retornará o caminho absoluto, e para chegarmos na pasta “uploads/covers”, vamos concatenar com o método getUploadPath() criado acima, veja:

    ...

    /** 
     * Absolute path. 
     * Get absolute path to upload directory. 
     * 
     * @return string 
     */ 
    protected function getUploadAbsolutePath() 
    { 
        return __DIR__ . '/../../../../web/' . $this->getUploadPath(); 
    }

    ...

Agora precisamos apresentar o caminho de nossas imagens para as views, vamos criar o método público getCoverWeb(), caso tenhamos uma imagem, ou seja, caso a imagem não seja nula, apresentamos a imagem nas views, para isso usaremos o método getUploadPath(), concatenado com o nome de nossa imagem, ou seja o método getCover(), veja:

    ...

    /** 
     * Relative path. 
     * Get web path to a cover. 
     * 
     * @return null|string 
     */ 
    public function getCoverWeb() 
    { 
        return null === $this->getCover() 
            ? null 
            : $this->getUploadPath() . '/' . $this->getCover(); 
    }

    ...

Podemos precisar do caminho absoluto de nossa imagem, para isso vamos criar o método getCoverAbsolute(), para obtermos esse caminho quando precisarmos, veja:

    ...

    /** 
     * Get path on disk to a cover. 
     * 
     * @return null|string 
     *   Absolute path. 
     */ 
    public function getCoverAbsolute() 
    { 
        return null === $this->getCover() 
            ? null 
            : $this->getUploadAbsolutePath() . '/' . $this->getCover(); 
    }

    ...

Agora temos que criar um método que fará o upload da imagem, para isso criaremos um método como nome upload(), caso a imagem não seja nula, ele fará o upload usando alguns métodos prontos da classe UploadedFile, para mover a imagens, veja:

    ... 
  
    /** 
     * Upload a cover file. 
     */ 
    public function upload() 
    { 
        if (null === $this->getFile()) { 
            return; 
        } 
        $filename = $this->getFile()->getClientOriginalName(); 
        $this->getFile()->move($this->getUploadAbsolutePath(), $filename); 
        $this->setCover($filename); 
        $this->setFile(); 
    }


    ...

Pronto, nossa entidade Post, agora está recebendo um upload de imagem.
Veja o entidade Post pronta:

<?php

namespace Tableless\ModelBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;


/**
 * Post
 *
 * @ORM\Table(name="post")
 * @ORM\Entity(repositoryClass="Tableless\ModelBundle\Repository\PostRepository")
 */
class Post extends Timestampable
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255)
     * @Assert\NotBlank
     */
    private $title;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     * @Assert\NotBlank
     */
    private $content;

    /**
     * @var Author
     *
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="posts")
     * @ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=false)
     * @Assert\NotBlank
     */
    private $author;

    /**
     * @var string
     *
     * @ORM\Column(name="cover", type="string", length=255, nullable=true)
     */
    private $cover;

    /**
     * @Assert\File(maxSize="1000000")
     */
    private $file;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set title
     *
     * @param string $title
     * @return Post
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set content
     *
     * @param string $content
     * @return Post
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content
     *
     * @return string 
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Set author
     *
     * @param \Tableless\ModelBundle\Entity\Author $author
     * @return Post
     */
    public function setAuthor(\Tableless\ModelBundle\Entity\Author $author)
    {
        $this->author = $author;

        return $this;
    }

    /**
     * Get author
     *
     * @return \Tableless\ModelBundle\Entity\Author 
     */
    public function getAuthor()
    {
        return $this->author;
    }

    // métodos criados

    /**
     * Get cover
     *
     * @return string
     */
    public function getCover()
    {
        return $this->cover;
    }


    /**
     * Set cover
     *
     * @param string $cover
     * @return Image
     */
    public function setCover($cover)
    {
        $this->cover = $cover;
    }

    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
    }

    /**
     * Relative path.
     * Get web path to upload directory.
     *
     * @return string
     */
    protected function getUploadPath()
    {
        return 'uploads/covers';
    }

    /**
     * Absolute path.
     * Get absolute path to upload directory.
     *
     * @return string
     */
    protected function getUploadAbsolutePath()
    {
        return __DIR__ . '/../../../../web/' . $this->getUploadPath();
    }

    /**
     * Relative path.
     * Get web path to a cover.
     *
     * @return null|string
     */
    public function getCoverWeb()
    {
        return null === $this->getCover()
            ? null
            : $this->getUploadPath() . '/' . $this->getCover();
    }

    /**
     * Get path on disk to a cover.
     *
     * @return null|string
     *   Absolute path.
     */
    public function getCoverAbsolute()
    {
        return null === $this->getCover()
            ? null
            : $this->getUploadAbsolutePath() . '/' . $this->getCover();
    }

    /**
     * Upload a cover file.
     */
    public function upload()
    {
        if (null === $this->getFile()) {
            return;
        }
        $filename = $this->getFile()->getClientOriginalName();
        $this->getFile()->move($this->getUploadAbsolutePath(), $filename);
        $this->setCover($filename);
        $this->setFile();
    }
}

Configurando o controller

Para que nossos formulários de posts tenham acesso ao upload, temos que configurar o controller PostController.
Entre no PostController, caminho: src/Tableless/CoreBundle/Controller/PostController.php, e no método createAction, insira o código $entity->upload(); veja na linha 15:

    /** 
     * Creates a new Post entity. 
     * 
     * @Route("/", name="post_create") 
     * @Method("POST") 
     * @Template("TablelessCoreBundle:Post:new.html.twig") 
     */ 
    public function createAction(Request $request) 
    { 
        $entity = new Post(); 
        $form = $this->createCreateForm($entity); 
        $form->handleRequest($request); 

        if ($form->isValid()) { 
            $entity->upload(); 
            $em = $this->getDoctrine()->getManager(); 
            $em->persist($entity); 
            $em->flush(); 

            return $this->redirect($this->generateUrl('post_show', array('id' => $entity->getId()))); 
        } 

        return array( 
            'entity' => $entity, 
            'form'   => $form->createView(), 
        ); 
    }

Temos que fazer o mesmo procedimento com o método updateAction na linha 23, veja:

    /** 
     * Edits an existing Post entity. 
     * 
     * @Route("/{id}", name="post_update") 
     * @Method("PUT") 
     * @Template("TablelessModelBundle:Post:edit.html.twig") 
     */ 
    public function updateAction(Request $request, $id) 
    { 
        $em = $this->getDoctrine()->getManager(); 

        $entity = $em->getRepository('TablelessModelBundle:Post')->find($id); 

        if (!$entity) { 
            throw $this->createNotFoundException('Unable to find Post entity.'); 
        } 

        $deleteForm = $this->createDeleteForm($id); 
        $editForm = $this->createEditForm($entity); 
        $editForm->handleRequest($request); 

        if ($editForm->isValid()) { 
            $entity->upload(); 
            $em->flush(); 

            return $this->redirect($this->generateUrl('post_edit', array('id' => $id))); 
        } 

        return array( 
            'entity'      => $entity, 
            'edit_form'   => $editForm->createView(), 
            'delete_form' => $deleteForm->createView(), 
        ); 
    } 

Configurando os formulários

Pronto, nossa entidade e controller de posts, estão configurados para receberem o upload, porém temos que configurar nossos formulários. Entre no PostType.php, para fazermos as configurações necessárias, caminho: src/Tableless/ModelBundle/Form/PostType, e no método buildForm adicione o ‘file’, veja na linha 10.

  /** 
     * @param FormBuilderInterface $builder 
     * @param array $options 
     */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
        $builder 
            ->add('title') 
            ->add('content') 
            ->add('file') 
            ->add('author') 
        ; 
    }

Atualizando o banco de dados

Para vermos a mágica acontecer, só precisamos, atualizar nosso banco, para isso entre no terminal e digite:

$ php app/console doctrine:schema:update --force 

Pronto! Nosso upload de imagem, está pronto, veja a imagem:

Botão de upload no formulário no symfony

Configurando as views

Agora temos que configurar nossas views para que as mesmas apresentem as imagens. Entre na view index.html.twig, caminho: src/Tablesless/CoreBundle/Resources/views/IndexController/index.html.twig, mude a linha 21.

mude de:

<img class="img-responsive" src="{{ asset('logo-tableless.png') }}" alt="img" title="img"/>

para:

<img class="img-responsive" src="{{ asset(post.getCoverWeb) }}" alt="{{ post.cover }}" title="{{ post.cover }}"/>

Vamos entrar em nossa view show.html.twig, caminho: caminho: src/Tablesless/CoreBundle/Resources/views/IndexController/show.html.twig, e vamos acrescentar a mesma linha acima do título, ou onde acharem melhor, veja:

<article class="col-lg-12" >

<img class="img-responsive" src="{{ asset(post.getCoverWeb) }}" alt="{{ post.cover }}" title="{{ post.cover }}"/>

<h1>{{ post.title }}</h1>

Vamos fazer os testes, criando um novo post, e inserindo uma imagem.
Observe a imagem na pasta web/uploads/cover.

Pasta de upload no symfony

Conclusão

Pronto, nosso simples projeto está fazendo upload de imagens para cada post, no próximo tutorial vamos aprender a configurar um Bundle pronto, disponibilizado pela comunidade, onde faremos a paginação de resultados para nossa página index.

Links dos tutoriais anteriores:
Iniciando com Symfony 2 – Instalação
Iniciando com Symfony 2 – parte 02
Iniciando com Symfony 2 – parte 03
Iniciando com Symfony 2 – parte 04
Iniciando com Symfony 2 – parte 05
Iniciando com Symfony 2 – parte 06

O projeto encontra-se no GitHub!

Publicado no dia