I have some entites with common relations and attributes. So, I want to simplify my schema using inheritance mapping.
I created a BaseData mappedsuperclass, and make my other entities expand it. This BaseData class has the common relations I need in each entity.
It works with many-to-one relation, like
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @ORM\ManyToOne(targetEntity="Service")
* @ORM\JoinColumn(name="service_id", referencedColumnName="id")
*/
protected $service;
But it become a little bit more tricky with self-referencing.
For instance, since I want to create a parent reference, I tried that :
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @ORM\ManyToOne(targetEntity="BaseData")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
protected $parent;
Obviously, it lead to a TableNotFoundException when I try to query this entity : QLSTATE[42S02]: Base table or view not found: 1146 Table 'project.base_data' doesn't exist
.
So, I tried AssociationOverrides, but it seems that doesn't allow to change the Target Entity.
So, is there a way to build some self-reference on a MappedSuperclass ? And by the way, does it even make sense ?
Many thanks in advance !
Here is the anwser :
I defined the protected $parent
and protected $children
in my BaseData mappedSuperClass as planned. I annotated them with other information I need. eg :
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @Datagrid\Column(field="parent.id", title="datagrid.parent_id", visible=false, safe=false)
* @Serializer\Expose
* @Serializer\Groups({"foo"})
*/
protected $parent;
/**
* @Serializer\Expose
* @Serializer\Groups({"elastica"})
*/
protected $children;
Then, I add the ORM relation with the event loadClassMetadata.
/**
* @param LoadClassMetadataEventArgs $eventArgs
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
// the $metadata is all the mapping info for this class
$classMetadata = $eventArgs->getClassMetadata();
$reflObj = new \ReflectionClass($classMetadata->name);
if($reflObj) {
if ($reflObj->isSubclassOf('CoreBundle\Entity\BaseData')) {
$fieldMapping = array(
'targetEntity' => $classMetadata->name,
'fieldName' => 'parent',
'inversedBy' => 'children',
'JoinColumn' => array(
'name' => 'parent_id',
'referencedColumnName' => 'id',
'nullable' => true,
'onDelete' => 'SET NULL',
),
);
$classMetadata->mapManyToOne($fieldMapping);
$fieldMapping = array(
'fieldName' => 'children',
'targetEntity' => $classMetadata->name,
'mappedBy' => 'parent',
);
$classMetadata->mapOneToMany($fieldMapping);
}
}
}
Register the event, and that's it.
Now, every class which extends the BaseData superClass get the relation. For instance, php app/console doctrine:generate:entities MyBundle
will generates the following code inside the SubClass entity :
/**
* Set parent
*
* @param \MyBundle\Entity\Subclass $parent
*
* @return Subclass
*/
public function setParent(\MyBundle\Entity\Subclass $parent = null)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* @return \MyBundle\Entity\Subclass
*/
public function getParent()
{
return $this->parent;
}
/**
* Add child
*
* @param \MyBundle\Entity\Subclass $child
*
* @return Subclass
*/
public function addChild(\MyBundle\Entity\Subclass $child)
{
$this->children[] = $child;
return $this;
}
/**
* Remove child
*
* @param \MyBundle\Entity\Subclass $child
*/
public function removeChild(\MyBundle\Entity\Subclass $child)
{
$this->children->removeElement($child);
}
/**
* Get children
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getChildren()
{
return $this->children;
}
You can remove the mapping @ORM\ManyToOne(targetEntity="BaseData")
and create an event listener on the event loadClassMetadata. (I didn't test the following code, this is just a starting point) Something like this:
class TestEvent
{
public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
$class = $classMetadata->getName();
$fieldMapping = array(
'fieldName' => 'parent',
'targetEntity' => $class,
);
$classMetadata->mapManyToOne($fieldMapping);
}
}
One important thing to notice is that a listener will be listening for all entities in your application.
See Doctrine docs about events And How to register event listener in symfony2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With