Doctrine2 ManyToMany Self referencing - php

I got problems with persisting many to many self referencing relations.
I receive error:
The class 'Doctrine\ORM\Persisters\ManyToManyPersister' was not found in the chain configured namespaces
This happens when I remove all children form item saved with them.
Leaving at least one don't make error happen. Also if I initially save entity with no children everything works fine.
/**
* West\AlbumBundle\Entity\Album
*
* #ORM\Table(name="albums")
* #ORM\Entity(repositoryClass="West\AlbumBundle\Entity\AlbumRepository")
* #ORM\HasLifecycleCallbacks
*/
class Album extends Entity implements CrudEntity
{
/**
* #ORM\ManyToMany(targetEntity="Album")
* #ORM\JoinTable(name="albums_relations",
* joinColumns={#ORM\JoinColumn(name="album_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="related_album_id", referencedColumnName="id")}
* )
* #var ArrayCollection
*/
protected $related_albums;
}
If you're testing with Symfony2 forms remember to set
"by_reference" => false

I've found that the problem happens when the method UnitOfWork.scheduleCollectionDeletion is called, for example, from MergeDoctrineCollectionListener.onBind() and the PersistentCollection object has been cloned ( 'by_reference' = false )
A quick fix to this problem is to comment the following line in the MergeDoctrineCollectionListener class:
//$collection->clear();

Related

Symfony2: Doctrine does not load related entities in many-to-many relation

I have a many-to-many-relation, and when I load an entity that is on one side this relation, I expect to see as its property the ArrayCollection of related entities on another side. However, this does not happen - the ArrayCollection loaded has no elements in it, while in the database I can see the related entries. What could be the reason?
Here is my code:
One side of the relation, ConsolidatedReport class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
Another side of the relation, Response class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\ConsolidatedReport\ConsolidatedReport", mappedBy="responses")
*/
private $consolidatedReports;
Here is the function I run to get an instance of ConsolidatedReport. This function sits inside a service that is being called from container:
/**
* Picks the consolidated report with given id.
*
* #param string $id
*
* #return ConsolidatedReport
*
* #throws NonExistentConsolidatedReportException if the survey doesn't exist
*/
public function pick($id)
{
$report = $this->repository->findOneBy(array('id' => $id));
if (!$report) {
throw new NonExistentConsolidatedReportException($id);
}
return $report;
}'
In the database, there is "con_rprt_responses" table with two columns "consolidated_reports_id" and "response_id". However, in profiler I do not see any queries to that table.
What could go wrong here?
UPDATE:
Please see my answer to this question below, that worked for me.
I added fetch="EAGER" to the $responses property of ConsolidatedReport class, and it worked.
The code now looks like this:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports", fetch="EAGER")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
More info here:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
Still if someone knows why the collection of related entity would not load without explicitly specifying EAGER fetching - please share your knowledge, it is highly appreciated!
If you specify the joinColumns, does this solve your problem?
/**
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses",
* joinColumns={#ORM\JoinColumn(name="consolidated_reports_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="response_id", referencedColumnName="id")}
* )
*/
The *toMany properties have to be initialized with an ArrayCollection.
public function __construct() {
$this->responses = new \Doctrine\Common\Collections\ArrayCollection();
$this-> consolidatedReports = new \Doctrine\Common\Collections\ArrayCollection();
}
In case you have more then single query to fetch the same objects using Doctrine try to use:
$entityManager->clear();
in between, to fix "missing" entities. It isn't solution "as is", however can give you an idea something wrong in chain of your queries.

Symfony2.4 Doctrine Pull Attribute from a Table Other Than the Entity Table

Let's say I have object A.
/**
* #Table(name="a")
* repo class definition annotation here
**/
class A {
/**
* #Column(name="id")
* ... etc
**/
private $id;
/**
* this is pulled from another table. It is not an entity, just a decimal value
*/
private $otherThingFromAnotherTable;
}
How can I pull $otherThingFromAnotherTable from table other_table using ORM annotations? It's not an entity, it's just a decimal value.
I've got the following annotations, so far:
* #ORM\OneToOne(targetEntity="??")
* #ORM\JoinTable(name="other_table",
* joinColumns={
* #ORM\JoinColumn(name="offer_id", referencedColumnName="id")
* }
* )
Currently, I am just using raw queries in the repo class so I can use it through the entity manager. Unfortunately, that only returns an array, not the object. I'd like to be able to use just $this->em->repo->find($id) and have it pull everything automatically instead of a custom method.

ManyToMany with same tables in entity - Symfony, Doctrine

I have two ManyToMany relations in one entity.
First:
/**
* #ORM\ManyToMany(targetEntity="RFQItem")
* #ORM\JoinTable(name="rfqitem_to_rfq",
* joinColumns={#ORM\JoinColumn(name="rfqitem", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")}
* )
*/
protected $rfqitems;
Second:
/**
* #ORM\ManyToMany(targetEntity="RFQItem", cascade={"persist"})
* #ORM\JoinTable(name="new_rfqitem_to_rfq",
* joinColumns={#ORM\JoinColumn(name="rfqitem", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")}
* )
*/
protected $rfq_item;
Problem is that I can't generate my entities and update schema, because generating entities gives me dublicated methods:
/**
* Add rfq_item
*
* #param \RFQ\IronilBundle\Entity\RFQItem $rfqItem
* #return RFQ
*/
public function addRfqItem(\RFQ\IronilBundle\Entity\RFQItem $rfqItem)
{
$this->rfq_item[] = $rfqItem;
return $this;
}
which throws an error:
Fatal error: Cannot redeclare RFQ\IronilBundle\Entity\RFQ::addRfqitem()
I need these two relations in one entity, because one is for adding previously made RFQitems, but second is to create new RFQitems and this all is in one form.
What I can do resolve this?
Doctrine generate methods with names based on your fields. Your fields are very similar: Doctrine will omit ending "s" and underscores, convert strings to lowercase. Your fields will look like $rfqitem and $rfqitem. As you can see Doctrine will see your fields with two absolutely equal names. You need to rename one of your fields $rfqitems, $rfq_item.
Try something more semantic like: $oldRFQItems and $newRFQItems.

Doctrine - Symfony2 | Many to Many targeting the same entity

I am trying to create a many-to-many foreign key table relation targeting the same entity.
I have successfully created relations with other entities but I'm having trouble when targeting the same entity.
I read a few stack overflow questions and answers and saw what I was doing was possible..
I based my class of this example - the table unit_relations is empty when i add a parent, but works when I add a child to a unit. I'm assuming because the field has the inversedBy annotation.
What do I need to add to allow bi-drectional updating/inserting?
I tried swapping the joinColums like this answer - nothing...
--
/**
* Unit
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Entity\UnitRepository")
*/
class Unit
{
....
/**
* #ORM\ManyToMany(targetEntity="Unit", inversedBy="parents")
* #ORM\JoinTable(name="unit_relations",
* joinColumns={#ORM\JoinColumn(name="unit_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="child_unit_id", referencedColumnName="id")}
* )
*/
private $children;
/**
* #ORM\ManyToMany(targetEntity="Unit", mappedBy="children")
*/
private $parents;
}
is adding inversedBy instead of having mappedBy, with the join columns also swapped, the proper way to do it?
Can someone explain this?
/**
* #ORM\ManyToMany(targetEntity="Unit", inversedBy="parents")
* #ORM\JoinTable(name="unit_relations",
* joinColumns={#ORM\JoinColumn(name="unit_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="child_unit_id", referencedColumnName="id")}
* )
*/
private $children;
/**
* #ORM\ManyToMany(targetEntity="Unit", inversedBy="children")
* #ORM\JoinTable(name="unit_relations",
* joinColumns={#ORM\JoinColumn(name="child_unit_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="unit_id", referencedColumnName="id")}
* )
*/
private $parents;
EDIT
as requested here's the code showing how I create the relation. I just realized it may because the entity doesn't have an id yet since I'm adding the relation on its creation...
$unit = new \Acme\DemoBundle\Entity\Unit();
/* #var $parentUnit \Acme\DemoBundle\Entity\Unit */
$parentUnit = $this->getDoctrine()->getRepository('AcmeDemoBundle:Unit')->findById($request->get('unitId'));
$unit->addParent($parentUnit);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($unit);
$entityManager->flush();
It doesn't matter, that there is no ID, when you add a parent relations.
I cant in detail explain why this happens, but i think the main problem is, that in Many-To-Many Self-Referencing the attribute with JoinTable annotation is potentially the Master-Entity. You can say, that it "holds" all other relations to this Entity.
You can recieve the bi-directional updating/inserting while changing the function $unit->addParent($parent). Change it as follows:
public function addParent($parent)
{
$this->parents[] = $parent;
$parent->addChild($this); // Add the relation in the proper way
}
That should work fine!
Regards!

Accessing Related Entities in Symfony2

I have a View entity that references an associated entity called ViewVersion. But if I name the variable anything other than viewVersion, e.g. just simple version, then I get an error:
Neither the property "viewVersion" nor one of the methods "getViewVersion()", "isViewVersion()", "hasViewVersion()", "__get()" exist and have public access in class "Gutensite\CmsBundle\Entity\View\View".
All the getters and setters are created through php app/console doctrine:generate:entities but they are for getVersion() and not getViewVersion().
Question: So, is there some unspoken rule that associated entities MUST be named the same as their class name?
Entity Definition
/**
* #ORM\Entity
* #ORM\Table(name="view")
* #ORM\Entity(repositoryClass="Gutensite\CmsBundle\Entity\View\ViewRepository")
*/
class View extends Entity\Base {
/**
* #ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", inversedBy="view", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\JoinColumn(name="versionId", referencedColumnName="id")
*/
protected $version;
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $versionId = NULL;
}
FYI, the variables for associated entities can be whatever you want.
This was caused by a predefined formType still referencing "viewVersion". The first variable in a form $builder->add() is a reference to the specific variable in the entity. I had viewVersion listed there still, and when I audited my code, I assumed it was just a generic reference (without any requirement) or possibly a reference to the Entity class, so I didn't change it:
$builder->add('viewVersion', new ViewVersionType(), array(
'label' => false
));
The SOLUTION to this problem was to change viewVersion to version so that it references an actual variable on the entity. Obviously...
$builder->add('version', new ViewVersionType(), array(
'label' => false
));

Categories