Using doctrine's getReference() with fixtures

If you are implementing fixtures, and creating or updating a Doctrine entity, and setting relational data (ie. One2Many, Many2One, etc), you may run into problems.  Doctrine might attempt to insert relational data, when it should be updating relational data.

Suppose you are attempting to create an university Course entity, with a related university Department entity:

$em = $this->get('doctrine')->getEntityManager();

$course= new Course();
$course->setStartDate(20130101);
$course->setEndDate(20130401);

$dept= $this->get('university.department.service')->findOneById(SOME_DEPT_ID);
$course->setDepartment($dept);

$em->persist($course);
$em->flush();

The Problem That Can Arise

Doctrine is 'smart' and knows about Relationships. If configured to cascade the department data, it should save (INSERT or UPDATE) the department along with the course.

However, in the code example above, Doctrine will sometimes attempts to insert the related department, rather than update the department. This is a problem, and you will encounter an error message like this one:

ErrorException: Notice: Undefined index: 000000005118577900000000245708ca in /home/project/vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php line 2152

This error message is  complaining because it's attempting to insert a new Department, when it should be updating the Group. More specifically, the (inserted) Department is colliding with an existing department in the database.

The Solution: EntityManager's getReference()

$em = $this->get('doctrine')->getEntityManager();

$course= new Course();
$course->setStartDate(20000101);
$course->setEndDate(20010101);

$course->setDepartment($em->getReference('Namespace\Of\Department,LoadDepartmentData::SOME_DEPT_ID));
 
$em->persist($course);
$em->flush();

In the above example, there isn't an explicit DB fetch, and Doctrine is smart enough to avoid the MySQL read in this case. As a result, the second version is more efficient.

More Reading on this

It's important to read up and understand what Doctrine is doing here, and know how to best use Doctrine Reference Entities.

http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/configuration.html?highlight=getreference#reference-proxies

http://stackoverflow.com/questions/5382170/doctrine-2-inserting-an-entity-with-associations-is-there-a-way-to-just-use-th

http://stackoverflow.com/questions/5484983/is-there-a-way-of-using-reference-column-name-in-doctrine-2

https://gist.github.com/1338884

http://docs.doctrine-project.org/en/2.0.x/reference/working-with-associations.html#transitive-persistence-cascade-operations

No comments:

Post a Comment