Много-к-одному, самоссылка с помощью наследования

У меня есть некоторые люди с общими отношениями и атрибутами. Итак, я хочу упростить мою схему, используя отображение наследования.

Я создал сопоставленный базовый суперкласс BaseData и заставил другие сущности расширить его. Этот класс BaseData имеет общие отношения, которые мне нужны в каждой сущности.

Он работает с отношением многие-к-одному, как

/**
 * @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? И, кстати, это вообще имеет смысл?

Спасибо заранее!

Обновить

Вот ответчик:

Я определил protected $parent а также protected $children в моем BaseData mappedSuperClass, как и планировалось. Я снабдил их необходимой информацией. например:

/**
 * @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;

Затем я добавляю отношение ORM к событию 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);
        }
    }
}

Зарегистрируйте событие и все.

Теперь каждый класс, который расширяет суперкласс BaseData, получает отношение. Например, 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;
}

1 ответ

Решение

Вы можете удалить отображение @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 о событиях и Как зарегистрировать прослушиватель событий в symfony2

Другие вопросы по тегам