在Doctrine 2.6中覆盖特征属性的关联映射

10

先决条件:

  • PHP 7.1.8
  • Symfony 3.3.9
  • Doctrine 2.6.x-dev

我想知道是否可以覆盖从 trait 中获取的属性关联映射的 inversedBy 属性。

我使用作为具体用户实体占位符的接口:

ReusableBundle\ModelEntrantInterface.php

interface EntrantInterface
{
    public function getEmail();

    public function getFirstName();

    public function getLastName();
}
  1. 以下结构可以很好地工作(需要创建实现EntrantInterfaceUser实体以及所有其他从这些抽象类派生的实体在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

  1. 这就是我想要实现的-使UserAwareTrait可以在多个实体中重复使用:

ReusableBundle\Entity\Traits\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...
}

但如果我想让该实体使用 UserAwareTrait 并重写属性的关联映射...

/**
 * @ORM\MappedSuperclass
 * @ORM\AssociationOverrides({
 *     @ORM\AssociationOverride({name="user", inversedBy="entries"})
 * })
 */
abstract class Entry
{
    use UserAwareTrait;
    // code...
}

当我运行php bin/console doctrine:schema:validate时,我在控制台中看到了以下错误:

[Doctrine\ORM\Mapping\MappingException]
Invalid field override named 'user' for class 'ReusableBundle\Entity\Entry'.

是否有解决方法可以实现所需的结果?

  1. 使用trait来存储共享属性

  2. 覆盖使用该trait的类中的关联映射或(可能)属性映射


我有一个类似的实现,但在我的情况下,使用trait的类不是“MappedSuperclass”。你尝试删除abstract class Entry上的@ORM\MappedSuperclass行了吗?更重要的是:您尝试覆盖关系而没有指定它(在这种情况下为ManyToOne)。请尝试:@ORM\AssociationOverrides({ @ORM\AssociationOverride(name="user", manyToMany=@ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface", inversedBy="entities")) }) - goulashsoup
还有在你的最后一个代码块中,它是 inversedBy="entries",与原始代码相同,在你的倒数第二个代码块中,它是 inversedBy="entities"... - goulashsoup
3个回答

2

简而言之:你应该将访问修饰符从protected更改为private。不要忘记在子类中无法直接操作私有属性,需要使用getter。

异常出现是由于AnnotationDriver中的bug(我认为是),或行为怪异。

foreach ($class->getProperties() as $property) {
    if ($metadata->isMappedSuperclass && ! $property->isPrivate()
        ||
        ...) {
        continue;
    }

它跳过所有非私有属性,用于MappedSuperclass,让它们在子类解析时组成元数据。但是,当涉及到覆盖时,驱动程序试图在MappedSuperclass级别进行,它不记得该属性被跳过,在元数据中找不到它并引发异常。

我在问题中进行了详细的解释。您还可以在那里找到突出显示该情况的单元测试链接。


0

你需要在自己的代码中尝试一下,但这可能是可行的。

作为一个实验,我重写了类中的一个trait,然后使用class_uses()http://php.net/manual/en/function.class-uses.php检查了该trait。

<?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

然而,Doctrine Annotations 可能仍会从 trait 本身中获取 DocBlock 而不是被覆盖的方法,这就是为什么我无法给出明确的答案。你只需要尝试并查看结果!


谢谢你的回答,但这不是我需要的。我不想覆盖一个方法(我只会在每个类中定义它,然后逐个注释,而这正是我希望避免做的)。而且方法体中没有什么可以覆盖的,那些只是getter和setter。 - genesst

0

我曾经遇到过类似的问题,并通过覆盖属性本身来解决它:

use UserAwareTrait;
/**
 * @var EntrantInterface
 * @ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface"inversedBy="entries")
 */
protected $user;

1
请仔细阅读,我想在多个实体中重用trait,因此无法在trait中覆盖它,每个实体的inversedBy必须具有不同的值。 - genesst
1
是的,我的答案意味着在每个实体中覆盖特征属性,以设置适当的反向。 - Gladhon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接