1

trying to setup a file upload form attached to a Doctrine entity, according to this cookbook recipe:

http://symfony.com/doc/2.0/cookbook/doctrine/file_uploads.html

Here's my entity/model class:

<?php

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

/**
 * Invoice
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="TechPeople\InvoiceBundle\Entity\InvoiceRepository")
 * @ORM\HasLifecycleCallbacks
 */
class Invoice
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\ManyToOne(targetEntity="User", inversedBy="user_invoices")
     * @ORM\JoinColumn(name="vendor_id", referencedColumnName="id")
     */
    private $vendor;

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

    /**
     * @var integer
     *
     * @ORM\Column(name="year", type="smallint")
     */
    private $year;

    /**
     * @var boolean
     *
     * @ORM\Column(name="expenses", type="boolean")
     */
    private $expenses;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="due", type="date")
     */
    private $due;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="paid", type="date")
     */
    private $paid;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="created", type="datetime")
     */
    private $created;

    /**
     * @var float
     *
     * @ORM\Column(name="expense_amount", type="decimal")
     */
    private $expense_amount;

    /**
     * @var float
     *
     * @ORM\Column(name="total_amount", type="decimal")
     */
    private $total_amount;

    /**
     * @var UploadedFile
     *
     * @Assert\File(maxSize="6000000")
     */
    public $attachment;

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

    /**
     * @ORM\OneToMany(targetEntity="InvoiceItem", mappedBy="invoice")
     */
    protected $invoice_items;


    /**
     * Constructor for Invoice Entity class
     */
    public function __construct()
    {
        $this->products = new ArrayCollection();
    }

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

    /**
     * Set month
     *
     * @param string $month
     * @return Invoice
     */
    public function setMonth($month)
    {
        $this->month = $month;

        return $this;
    }

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

    /**
     * Set year
     *
     * @param integer $year
     * @return Invoice
     */
    public function setYear($year)
    {
        $this->year = $year;

        return $this;
    }

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

    /**
     * Set expenses
     *
     * @param boolean $expenses
     * @return Invoice
     */
    public function setExpenses($expenses)
    {
        $this->expenses = $expenses;

        return $this;
    }

    /**
     * Get expenses
     *
     * @return boolean 
     */
    public function getExpenses()
    {
        return $this->expenses;
    }

    /**
     * Set due
     *
     * @param \DateTime $due
     * @return Invoice
     */
    public function setDue($due)
    {
        $this->due = $due;

        return $this;
    }

    /**
     * Get due
     *
     * @return \DateTime 
     */
    public function getDue()
    {
        return $this->due;
    }

    /**
     * Set paid
     *
     * @param \DateTime $paid
     * @return Invoice
     */
    public function setPaid($paid)
    {
        $this->paid = $paid;

        return $this;
    }

    /**
     * Get paid
     *
     * @return \DateTime 
     */
    public function getPaid()
    {
        return $this->paid;
    }

    /**
     * Set created
     *
     * @param \DateTime $created
     * @return Invoice
     */
    public function setCreated($created)
    {
        $this->created = $created;

        return $this;
    }

    /**
     * Get created
     *
     * @return \DateTime 
     */
    public function getCreated()
    {
        return $this->created;
    }

    /**
     * Set expense_amount
     *
     * @param float $expenseAmount
     * @return Invoice
     */
    public function setExpenseAmount($expenseAmount)
    {
        $this->expense_amount = $expenseAmount;

        return $this;
    }

    /**
     * Get expense_amount
     *
     * @return float 
     */
    public function getExpenseAmount()
    {
        return $this->expense_amount;
    }

    /**
     * Set total_amount
     *
     * @param float $totalAmount
     * @return Invoice
     */
    public function setTotalAmount($totalAmount)
    {
        $this->total_amount = $totalAmount;

        return $this;
    }

    /**
     * Get total_amount
     *
     * @return float 
     */
    public function getTotalAmount()
    {
        return $this->total_amount;
    }

    /**
     * Set attachment
     *
     * @param string $attachment
     * @return Invoice
     */
    public function setAttachment($attachment)
    {
        $this->attachment = $attachment;

        return $this;
    }

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

    /**
     * Set vendor
     *
     * @param \TechPeople\InvoiceBundle\Entity\User $vendor
     * @return Invoice
     */
    public function setVendor(\TechPeople\InvoiceBundle\Entity\User $vendor = null)
    {
        $this->vendor = $vendor;

        return $this;
    }

    /**
     * Get vendor
     *
     * @return \TechPeople\InvoiceBundle\Entity\User 
     */
    public function getVendor()
    {
        return $this->vendor;
    }

    /**
     * Add invoice_items
     *
     * @param \TechPeople\InvoiceBundle\Entity\InvoiceItem $invoiceItems
     * @return Invoice
     */
    public function addInvoiceItem(\TechPeople\InvoiceBundle\Entity\InvoiceItem $invoiceItems)
    {
        $this->invoice_items[] = $invoiceItems;

        return $this;
    }

    /**
     * Remove invoice_items
     *
     * @param \TechPeople\InvoiceBundle\Entity\InvoiceItem $invoiceItems
     */
    public function removeInvoiceItem(\TechPeople\InvoiceBundle\Entity\InvoiceItem $invoiceItems)
    {
        $this->invoice_items->removeElement($invoiceItems);
    }

    /**
     * Get invoice_items
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getInvoiceItems()
    {
        return $this->invoice_items;
    }

    /**
     * Get path for attachment
     *
     * @param string $type Can return web path, or absolute path, web is default
     * @return null|string
     */
    public function getAttachmentPath($type='web')
    {
        if($type == 'absolute') {
            return null === $this->attachment_path
                ? null
                : $this->getUploadRootDir().'/'.$this->attachment_path;
        } else {
            return null === $this->attachment_path
                ? null
                : $this->getUploadDir().'/'.$this->attachment_path;
        }
    }

    protected function getUploadRootDir()
    {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir();
    }

    protected function getUploadDir()
    {
        // get rid of the __DIR__ so it doesn't screw up
        // when displaying uploaded doc/image in the view.
        return 'uploads/invoice/attachments';
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->attachment) {
            // do whatever you want to generate a unique name
            $filename = sha1(uniqid(mt_rand(), true));
            $this->attachment = $filename.'.'.$this->attachment->guessExtension();
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->attachment) {
            return;
        }

        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        $this->attachment->move($this->getUploadRootDir(), $this->path);

        unset($this->attachment);
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        if ($file = $this->getAttachmentPath('absolute')) {
            unlink($file);
        }
    }
}

I get the following error when I try to submit an edit form (the edit form was generated by doctrine:generate:crud):

Fatal error: Call to a member function move() on a non-object

Might be the same as:

Symfony forms. File upload

But that question isn't answered. I hope it doesn't violate etiquette to post a second question.

I'm pretty new to Symfony2, just trying to learn my way around, and this has me stumped. I can post any other info you need, just let me know what.

Here's the form class:

<?php

namespace TechPeople\InvoiceBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class InvoiceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('month')
            ->add('year')
            ->add('expenses')
            ->add('due')
            ->add('paid')
            ->add('created')
            ->add('expense_amount')
            ->add('total_amount')
            ->add('attachment')
            ->add('vendor')
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'TechPeople\InvoiceBundle\Entity\Invoice'
        ));
    }

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

And here's the controller:

<?php

namespace TechPeople\InvoiceBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use TechPeople\InvoiceBundle\Entity\Invoice;
use TechPeople\InvoiceBundle\Form\InvoiceType;

/**
 * Invoice controller.
 *
 */
class InvoiceController extends Controller
{
    /**
     * Lists all Invoice entities.
     *
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();

        $entities = $em->getRepository('TechPeopleInvoiceBundle:Invoice')->findAll();

        return $this->render('TechPeopleInvoiceBundle:Invoice:index.html.twig', array(
            'entities' => $entities,
        ));
    }

    /**
     * Finds and displays a Invoice entity.
     *
     */
    public function showAction($id)
    {
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('TechPeopleInvoiceBundle:Invoice')->find($id);

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

        $deleteForm = $this->createDeleteForm($id);

        return $this->render('TechPeopleInvoiceBundle:Invoice:show.html.twig', array(
            'entity'      => $entity,
            'delete_form' => $deleteForm->createView(),        ));
    }

    /**
     * Displays a form to create a new Invoice entity.
     *
     */
    public function newAction()
    {
        $entity = new Invoice();
        $form   = $this->createForm(new InvoiceType(), $entity);

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

    /**
     * Creates a new Invoice entity.
     *
     */
    public function createAction(Request $request)
    {
        $entity  = new Invoice();
        $form = $this->createForm(new InvoiceType(), $entity);
        $form->bind($request);

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

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

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

    /**
     * Displays a form to edit an existing Invoice entity.
     *
     */
    public function editAction($id)
    {
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('TechPeopleInvoiceBundle:Invoice')->find($id);

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

        $editForm = $this->createForm(new InvoiceType(), $entity);
        $deleteForm = $this->createDeleteForm($id);

        return $this->render('TechPeopleInvoiceBundle:Invoice:edit.html.twig', array(
            'entity'      => $entity,
            'edit_form'   => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        ));
    }

    /**
     * Edits an existing Invoice entity.
     *
     */
    public function updateAction(Request $request, $id)
    {
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('TechPeopleInvoiceBundle:Invoice')->find($id);

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

        $deleteForm = $this->createDeleteForm($id);
        $editForm = $this->createForm(new InvoiceType(), $entity);
        $editForm->bind($request);

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

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

        return $this->render('TechPeopleInvoiceBundle:Invoice:edit.html.twig', array(
            'entity'      => $entity,
            'edit_form'   => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        ));
    }

    /**
     * Deletes a Invoice entity.
     *
     */
    public function deleteAction(Request $request, $id)
    {
        $form = $this->createDeleteForm($id);
        $form->bind($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $entity = $em->getRepository('TechPeopleInvoiceBundle:Invoice')->find($id);

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

            $em->remove($entity);
            $em->flush();
        }

        return $this->redirect($this->generateUrl('invoice'));
    }

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

It's all pretty basic stuff, straight from the docs, because, as I said, I'm just learning Symfony. Any help much appreciated.

1 Answer 1

2

$this->attachment must be an UploadedFile object.

But in your method preUpload() you do a mistake and override it for a string:

$this->attachment = $filename.'.'.$this->attachment->guessExtension();

Then, upload() method is called and you check if $this->attachment if not null, that's true because it's a string:

if (null === $this->attachment) {
     return;
}

And executing ->move() on a string display an error ;)

So to answer, in preUpload() method replace this line:

$this->attachment= $filename.'.'.$this->attachment->guessExtension();

For:

$this->path = $filename.'.'.$this->attachment->guessExtension();
Sign up to request clarification or add additional context in comments.

1 Comment

Right on the money. Thanks Sybio!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.