Question

I'm testing cascade persist between Job entity and Category Entity in Symfony2 and Doctrin2. Here's my Category entity:

<?php
namespace Ibw\JobeetBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Ibw\JobeetBundle\Entity\Affiliate;
use Ibw\JobeetBundle\Entity\Job;
use Ibw\JobeetBundle\Utils\Jobeet;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="Ibw\JobeetBundle\Repository\CategoryRepository")
 * @ORM\Table(name="categories")
 * @ORM\HasLifecycleCallbacks
 */
class Category 
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     */
    protected $name;

    /**
     * @ORM\OneToMany(targetEntity="Job", mappedBy="category", cascade={"persist"})
     */
    protected $jobs;

    /**
     * @ORM\ManyToMany(targetEntity="Affiliate", mappedBy="categories")
     */
    protected $affiliates;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     */
    protected $slug;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->jobs = new ArrayCollection();
        $this->affiliates = new ArrayCollection();
    }

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

    /**
     * Set name
     *
     * @param string $name
     * @return Category
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * Add jobs
     *
     * @param Job $jobs
     * @return Category
     */
    public function addJob(Job $jobs)
    {
        $this->jobs[] = $jobs;

        return $this;
    }

    /**
     * Remove jobs
     *
     * @param Job $jobs
     */
    public function removeJob(Job $jobs)
    {
        $this->jobs->removeElement($jobs);
    }

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

    /**
     * Add affiliates
     *
     * @param Affiliate $affiliates
     * @return Category
     */
    public function addAffiliate(Affiliate $affiliates)
    {
        $this->affiliates[] = $affiliates;

        return $this;
    }

    /**
     * Remove affiliates
     *
     * @param Affiliate $affiliates
     */
    public function removeAffiliate(Affiliate $affiliates)
    {
        $this->affiliates->removeElement($affiliates);
    }

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

    /**
     * Set slug
     *
     * @param string $slug
     * @return Category
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

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

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function setSlugValue()
    {
        $this->setSlug(Jobeet::slugify($this->getName()));
    }
}

Here's the cascade persist part in it:

/**
 * @ORM\OneToMany(targetEntity="Job", mappedBy="category", cascade={"persist"})
 */
protected $jobs;

Now when i try to test it using this test :

public function testAddJobToCategory()
    {
        $job = new Job();
         $job->setType('flexible-time');
         $job->setCompany('Sensio Labs');
         $job->setLogo('sensio-labs.gif');
         $job->setUrl('http://www.sensiolabs.com/');
         $job->setPosition('Web Developer');
         $job->setLocation('Paris, France');
         $job->setDescription('You\'ve already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available.');
         $job->setHowToApply('Send your resume to fabien.potencier [at] sensio.com');
         $job->setIsPublic(true);
         $job->setIsActivated(true);
         $job->setToken('job');
         $job->setEmail('job@example.com');
         $job->setExpiresAt(new \DateTime('+30 days'));

         $category = $this->em->createQueryBuilder('c')
                        ->select('c')
                        ->from('IbwJobeetBundle:Category', 'c')
                        ->where('c.id = 1')
                        ->getQuery()
                        ->getSingleResult();

        $category->addJob($job);
        $this->em->persist($category);
        $this->em->flush();

        $jobFromQuery = $this->em->createQueryBuilder('j')
                    ->select('j')
                    ->from('IbwJobeetBundle:Job', 'j')
                    ->where('j.type = :type')
                    ->setParameter('type', 'flexible-time')
                    ->setMaxResults(1)
                    ->setFirstResult(1)
                    ->getQuery()
                    ->getSingleResult();


        $this->assertEquals(1, $jobFromQuery->getCategory()->getId());
    }

When i run this test, I get this error :

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

So i think it's in the $jobFromQuery->getCategory()->getId() part. getCategory() being not set or something.

Was it helpful?

Solution

This doctrine doc http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html says:

Doctrine will only check the owning side of an association for changes.

To fully understand this, remember how bidirectional associations are maintained in the object world. There are 2 references on each side of the association and these 2 references both represent the same association but can change independently of one another. Of course, in a correct application the semantics of the bidirectional association are properly maintained by the application developer (that’s his responsibility). Doctrine needs to know which of these two in-memory references is the one that should be persisted and which not. This is what the owning/inverse concept is mainly used for.

Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine’s point of view)

The owning side of a bidirectional association is the side Doctrine “looks at” when determining the state of the association, and consequently whether there is anything to do to update the association in the database

On your code you just do $category->addJob($job); so you're setting the association on the inverse side. As the docs that's ignored so you should at least do $job->setCategory($category) first.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top