多对一、自引用和继承映射
Many-To-One, Self-referencing with Inheritance Mapping
我有一些具有共同关系和属性的实体。所以,我想使用继承映射来简化我的架构。
我创建了一个 BaseData mappedsuperclass,并让我的其他实体扩展它。此 BaseData class 具有我在每个实体中需要的公共关系。
它适用于多对一关系,例如
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @ORM\ManyToOne(targetEntity="Service")
* @ORM\JoinColumn(name="service_id", referencedColumnName="id")
*/
protected $service;
但是自引用变得有点棘手。
例如,因为我想创建一个父引用,所以我试过了:
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @ORM\ManyToOne(targetEntity="BaseData")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
protected $parent;
显然,当我尝试查询此实体时,它会导致 TableNotFoundException:QLSTATE[42S02]: Base table or view not found: 1146 Table 'project.base_data' doesn't exist
.
所以,我尝试了 AssociationOverrides,但似乎不允许更改目标实体。
那么,有没有办法在 MappedSuperclass 上建立一些自引用?顺便说一下,它是否有意义?
非常感谢!
更新
这是答案:
我按计划在我的 BaseData mappedSuperClass 中定义了 protected $parent
和 protected $children
。我用我需要的其他信息对它们进行了注释。例如:
/**
* @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;
然后,我添加与事件 loadClassMetadata 的 ORM 关系。
/**
* @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);
}
}
}
注册活动,仅此而已。
现在,每个扩展 BaseData 超类的 class 都得到关系。例如,php app/console doctrine:generate:entities MyBundle
将在 SubClass 实体中生成以下代码:
/**
* 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;
}
您可以删除映射 @ORM\ManyToOne(targetEntity="BaseData")
并在事件 loadClassMetadata 上创建一个事件侦听器。 (我没有测试下面的代码,这只是一个起点)是这样的:
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);
}
}
需要注意的一件重要事情是侦听器将侦听您应用程序中的所有实体。
见Doctrine docs about events
并且 How to register event listener in symfony2
我有一些具有共同关系和属性的实体。所以,我想使用继承映射来简化我的架构。
我创建了一个 BaseData mappedsuperclass,并让我的其他实体扩展它。此 BaseData class 具有我在每个实体中需要的公共关系。
它适用于多对一关系,例如
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @ORM\ManyToOne(targetEntity="Service")
* @ORM\JoinColumn(name="service_id", referencedColumnName="id")
*/
protected $service;
但是自引用变得有点棘手。
例如,因为我想创建一个父引用,所以我试过了:
/**
* @ORM\MappedSuperclass
*/
class BaseData
{
/**
* @ORM\ManyToOne(targetEntity="BaseData")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
protected $parent;
显然,当我尝试查询此实体时,它会导致 TableNotFoundException:QLSTATE[42S02]: Base table or view not found: 1146 Table 'project.base_data' doesn't exist
.
所以,我尝试了 AssociationOverrides,但似乎不允许更改目标实体。
那么,有没有办法在 MappedSuperclass 上建立一些自引用?顺便说一下,它是否有意义?
非常感谢!
更新
这是答案:
我按计划在我的 BaseData mappedSuperClass 中定义了 protected $parent
和 protected $children
。我用我需要的其他信息对它们进行了注释。例如:
/**
* @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;
然后,我添加与事件 loadClassMetadata 的 ORM 关系。
/**
* @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);
}
}
}
注册活动,仅此而已。
现在,每个扩展 BaseData 超类的 class 都得到关系。例如,php app/console doctrine:generate:entities MyBundle
将在 SubClass 实体中生成以下代码:
/**
* 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;
}
您可以删除映射 @ORM\ManyToOne(targetEntity="BaseData")
并在事件 loadClassMetadata 上创建一个事件侦听器。 (我没有测试下面的代码,这只是一个起点)是这样的:
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);
}
}
需要注意的一件重要事情是侦听器将侦听您应用程序中的所有实体。
见Doctrine docs about events 并且 How to register event listener in symfony2