Переопределить ассоциативное сопоставление свойства объекта в Doctrine 2.6
Предпосылки:
- PHP 7.1.8
- Symfony 3.3.9
- Doctrine 2.6.x-dev
Интересно, возможно ли переопределить атрибут inversedBy
сопоставления ассоциаций свойств, взятый из признака.
Интерфейс, который я использую в качестве заполнителя конкретного пользователя:
ReusableBundle\ModelEntrantInterface.php
interface EntrantInterface
{
public function getEmail();
public function getFirstName();
public function getLastName();
}
- Следующая архитектура работает очень хорошо (нужно создать объект
User
, который реализует EntrantInterface
и все другие объекты, которые получены из этих абстрактных классов в AppBundle
):
ReusableBundle\Entity\Entry.php
/**
* @ORM\MappedSuperclass
*/
abstract class Entry
{
/**
* @var EntrantInterface
*
* @ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface", inversedBy="entries")
* @ORM\JoinColumn(name="user_id")
*/
protected $user;
// getters/setters...
}
ReusableBundle\Entity\Timestamp.php
/**
* @ORM\MappedSuperclass
*/
abstract class Timestamp
{
/**
* @var EntrantInterface
*
* @ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface", inversedBy="timestamps")
* @ORM\JoinColumn(name="user_id")
*/
protected $user;
// getters/setters...
}
И еще пару объектов с аналогичной структурой, которые используют EntranInterface
- И это то, чего я хочу достичь -
UserAwareTrait
для повторного использования для нескольких объектов:
ReusableBundle\Entity\Черты характера \UserAwareTrait.php
trait UserAwareTrait
{
/**
* @var EntrantInterface
*
* @ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface")
* @ORM\JoinColumn(name="user_id")
*/
protected $user;
// getter/setter...
}
В Doctrine 2.6, если бы я использовал суперкласс и хотел переопределить его свойство, я бы сделал это:
/**
* @ORM\MappedSuperclass
* @ORM\AssociationOverrides({
* @ORM\AssociationOverride({name="property", inversedBy="entities"})
* })
*/
abstract class Entity extends SuperEntity
{
// code...
}
Но если я хочу, чтобы Entity использовал UserAwareTrait
и переопределил ассоциативное сопоставление свойства...
/**
* @ORM\MappedSuperclass
* @ORM\AssociationOverrides({
* @ORM\AssociationOverride({name="user", inversedBy="entries"})
* })
*/
abstract class Entry
{
use UserAwareTrait;
// code...
}
... и запустите php bin/console doctrine:schema:validate
Я вижу эту ошибку в консоли:
[Учение\ORM\Mapping\MappingException]
Недопустимое переопределение поля с именем 'user' для класса 'ReusableBundle\Entity\Entry'.
Есть ли способ обхода, который я мог бы выполнить для достижения желаемого результата?
-
Использовать свойство для хранения общих свойств
-
Отменить сопоставление ассоциаций или (возможно) сопоставление атрибутов в классе, который использует этот признак
Ответы
Ответ 1
TL; DR Вы должны изменить модификатор доступа с protected
на private
. Не забывайте, что вы не сможете напрямую манипулировать частной собственностью в подклассе и вам понадобится getter.
Исключение появляется из-за ошибки (я считаю, или причуды поведения) в AnnotationDriver
.
foreach ($class->getProperties() as $property) {
if ($metadata->isMappedSuperclass && ! $property->isPrivate()
||
...) {
continue;
}
Он пропускает все неличные свойства для MappedSuperclass
, позволяя им составлять метаданные при разборе подкласса. Но когда дело доходит до того, что драйвер пытается сделать это на уровне MappedSuperclass
, он не помнит, что свойство было пропущено, не удалось найти его в метаданных и создать исключение.
Я подробно объяснил вопрос. Вы также можете найти ссылку на блок-тесты, которые подчеркивают случай.
Ответ 2
Вам нужно попробовать это в своем собственном коде, чтобы увидеть, но возможно может.
В качестве эксперимента я переопределил признак в классе, а затем проверил его с помощью class_uses()
http://php.net/manual/en/function.class-uses.php
<?php
trait CanWhatever
{
public function doStuff()
{
return 'result!';
}
}
class X
{
use CanWhatever;
public function doStuff()
{
return 'overridden!';
}
}
$x = new X();
echo $x->doStuff();
echo "\n\$x has ";
echo (class_uses($x, 'CanWhatever')) ? 'the trait' : 'no trait';
Выводится:
overridden!
$x has the trait
Что вы можете увидеть здесь https://3v4l.org/Vin2H
Тем не менее, доктрина Аннотации могут по-прежнему собирать DocBlock из свойства, а не переопределенного метода, поэтому я не могу дать вам окончательный ответ. Вам просто нужно попробовать и посмотреть!
Ответ 3
У меня была аналогичная проблема и решить ее, переопределив ее свойство:
use UserAwareTrait;
/**
* @var EntrantInterface
* @ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface"inversedBy="entries")
*/
protected $user;