Upload file with doctrine in Symfony2

Publié le par Jordan Samouh

This Article is going to explain quickly how to manage an upload file in an Entity in Symfony2
I do not give you the best practice but a simple practice to do it

1)First of All create your entity :

namespace XXX;

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

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class Article
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

      /**
     * @var string $image
     * @Assert\File( maxSize = "1024k", mimeTypesMessage = "Please upload a valid Image")
     * @ORM\Column(name="image", type="string", length=255)
     */
    private $image;

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

    /**
     * Set image
     *
     * @param string $image
     */
    public function setImage($image)
    {
        $this->image = $image;
    }

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

}

2) build your form: So we will then create a simple form type for this Article entity in order to fit into the other forms: ArticleType.php


use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class ArticleType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('image')
            ->add('...')

        ;
    }

    public function getName()
    {
        return 'xxx_articletype';
    }
}

3)Create the controller: The controller below shows you how to manage the whole process: ArticleController.php:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

/**
 * Article controller.
 *
 */
class ArticleController extends Controller
{

    /**
     * Finds and displays a Article entity.
     *
     */
    public function showAction($id)
    {

        $em = $this->getDoctrine()->getEntityManager();
        $entity = $em->getRepository('XXXBundle:Article')->find($id);
        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Article entity.');
        }
    return $this->render('XXXBundle:Article:show.html.twig', array(
            'entity'      => $entity,

        ));
    }
    /**
     * Displays a form to create a new Article entity.
     *
     */
    public function newAction()
    {
        $entity = new Article();
        //$entity = $em->getRepository('CliniqueGynecoBundle:Article');
        $form   = $this->createForm(new ArticleType(), $entity);

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    }

    /**
     * Creates a new Article entity.
     *
     */
    public function createAction()
    {
        $entity  = new Article();
        $request = $this->getRequest();
        $form    = $this->createForm(new ArticleType(), $entity);
        $form->bindRequest($request);

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

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

        }

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    }

    private function createDeleteForm($id)
    {
        return $this->createFormBuilder(array('id' => $id))
            ->add('id', 'hidden')
            ->getForm()
        ;
    }
}

4) layout for the upload form: new.html.twig

<form action="{{ path('basearticle_create') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <p>
        <button class="btn btn-primary" type="submit">Create</button>
    </p>
</form>

5) Display layout: show.html.twig

<table>
 <tr>
      <td align="center" valign="top"><img src="{{ asset('upload/' ~ entity.id ~'/' ~ entity.image)}}" alt="" height="525" width="666" /></td>
</tr>
</table>

6) Use the « Lifecycle Callbacks » hooking the entity in a « Lifecycle callbacks »: « @ ORM \ HasLifecycleCallbacks »

/**
 *
 * @ORM\Table()
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Article
{
....

7) Added methods to download files:

class Article
{
   ....................................

    public function getFullImagePath() {
        return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
    }

    protected function getUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        return $this->getTmpUploadRootDir().$this->getId()."/";
    }

    protected function getTmpUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        return __DIR__ . '/../../../../web/upload/';
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function uploadImage() {
        // the file property can be empty if the field is not required
        if (null === $this->image) {
            return;
        }
        if(!$this->id){
            $this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
        }else{
            $this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
        }
        $this->setImage($this->image->getClientOriginalName());
    }

    /**
     * @ORM\PostPersist()
     */
    public function moveImage()
    {
        if (null === $this->image) {
            return;
        }
        if(!is_dir($this->getUploadRootDir())){
            mkdir($this->getUploadRootDir());
        }
        copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
        unlink($this->getTmpUploadRootDir().$this->image);
    }

    /**
     * @ORM\PreRemove()
     */
    public function removeImage()
    {
        unlink($this->getFullImagePath());
        rmdir($this->getUploadRootDir());
    }

The class is now all you need: it generates a unique file name before persistent, move the file after persistence, and deletes the file if the entity is already deleted.

The @ ORM \ PrePersist () and @ ORM \ PostPersist () event reminders are triggered before and after the entity is stored in the database. On the other hand, the ORM @ \ PreUpdate () and @ ORM \ PostUpdate () event callbacks are called when the entity is updated.

The PreUpdate and reminders are triggered PostUpdate if there is a change in one of the fields of the entity that are retained. This means that, by default, only if you change the file ownership, these events will not be triggered, as the property itself is not directly persisted via Doctrine. One solution would be to use an updated field that has persisted to the doctrine, and change manually when you edit the file.

Cette entrée a été publiée dans Symfony 2 Utils. Vous pouvez la mettre en favoris avec ce permalien.



2 réponses à Upload file with doctrine in Symfony2

  1. Sylvaon dit :

    Hi,

    Lots of article draw our intension on the upload for file. Im actualy working on this uploads(and it works). But i found nothing for the Edition of file. For exemple i upload 3 photos photo1 photo2 photo3, and i want to Edit photo1 to put photo4. When i create my form, the photos are not « linked » in the file input.

    How can i do that ?

    Thanks a lot,

    Sylvain

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*


nine - 6 =

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>