覆盖Doctrine Trait属性

39

我知道你可以通过在你的类中声明来覆盖trait 方法,我很想知道是否可以以同样的方式覆盖trait 属性。这样做安全吗?文档中没有这个说明,所以我不敢实现它。

根据文档:

来自基类的继承成员被Trait插入的成员覆盖。优先顺序是当前类中的成员覆盖Trait方法,然后是继承的方法。

http://php.net/manual/zh/language.oop5.traits.php


1
一个成员可以是属性或方法,所以我认为是可以的。Doctrine在查看实体属性时使用DocBlock,因此我认为它会被覆盖。最好的做法是尝试一下。创建一个使用traits的实体,使用Doctrine的schema工具查看SQL会是什么,然后使用不同的DocBlock进行重写,看看会发生什么(如果使用缓存,请不要忘记清除缓存)。 - DanielM
4个回答

66

在使用trait的类中,您无法覆盖trait的属性。但是,在扩展使用trait的类的类中,您可以覆盖trait的属性。例如:

trait ExampleTrait
{
    protected $someProperty = 'foo';
}

abstract class ParentClass
{
    use ExampleTrait;
}

class ChildClass extends ParentClass
{
    protected $someProperty = 'bar';
}

31
好的,我会尽力进行翻译。以下是需要翻译的内容:我很好奇,这背后的逻辑是什么?“儿子不因父亲的罪孽受到惩罚。” - Teoman Tıngır
不是问题,但很烦人:在PHP5.6中,它会触发一个STRICT [E_STRICT][2048]通知。该通知将解释类和Trait正在定义相同的属性。它还会说:“这可能不兼容,为了提高可维护性,请考虑在Trait中使用访问器方法。类已被组合。”这很有趣,因为它恰恰与PHP现在告诉我们如何处理它的方式相反。 :) - Julio Marchi
17
有时候我觉得PHP的创建者在恶意戏弄我。 - keyboardSmasher
8
如果有人仍然在想,那么原因就在于特征只是复制粘贴。在一个类中不能定义相同的属性两次,但是可以在子类中重写它——每当你想到特征奇怪之处时,想一想如果将“use Trait”直接替换为代码,它就会变得清晰明了。其实没什么别的。 - Moritz Friedrich
3
@MoritzFriedrich 这是有意义的,但它并不正确。"traits只是复制粘贴" - 正确,但它们允许将_methods_粘贴到类的具体方法上,并提供解决冲突的机制。为什么属性不能享有同样的特权呢?我倾向于早期评论中的巨魔理论。 - cautionbug

40

我的解决方案是使用构造函数,例如:

trait ExampleTrait
{
    protected $someProperty = 'foo';
}

class MyClass
{
    use ExampleTrait;

    public function __construct()
    {
         $this->someProperty = 'OtherValue';
    }
}

@siannone 不要使用静态属性,这是一种不好的设计模式。 - Yevgeniy Afanasyev
1
使用构造函数是一个非常有效的解决方案,这对我很有效。 - mchljams
不幸的是,这对静态属性没有帮助。在一个实例中,我不得不定义一个自定义方法__include()来更改静态特性属性,并在访问类时在autoloader内调用它。 - akinuri

8
在这种情况下,可以使用属性updatable作为替代解决方案。当该属性仅在trait的方法中需要时,我会使用它...
trait MyTrait
{
    public function getUpdatableProperty()
    {
        return isset($this->my_trait_updatable) ?
            $this->my_trait_updatable:
            'default';
    }
}

...并在类中使用该特性。

class MyClass
{
    use MyTrait;

    /**
     * If you need to override the default value, define it here...
     */
    protected $my_trait_updatable = 'overridden';
}

-8
你可以在一个类中声明一个 trait 属性,但必须保持与 trait 相同的定义。无法用不同的定义覆盖它。因此,既然你已经可以从类中访问 trait 属性,就没有必要重新定义了。可以把 trait 看作是复制粘贴代码的效果。
<?php
trait FooTrait 
{
    protected $same       = '123';
    protected $mismatch  = 'trait';
}

class FooClass 
{
    protected $same      = '123';

    // This override property produces: 
    // PHP Fatal error:  FooClass and FooTrait define the same property
    // ($mismatchValue) in the composition of FooClass. However, the definition
    // differs and is considered incompatible
    protected $mismatch  = 'class';

    use FooTrait;
}

8
糟糕的回答,首先存在一些矛盾,“你可以覆盖,但你又不能”。请明确你的想法。其次,该示例并没有解释任何我们不知道的内容,也没有给出实际解决方案。更好的答案在这里:https://dev59.com/GmIj5IYBdhLWcg3waEZe#20437742 - goyote
@goyote 感谢提供链接。我已经将单词“override”替换为“declare”,并明确说明它不能被不同的定义所覆盖。希望这能帮到你。 - martinezdelariva
@martinezdelariva,抱歉,这并没有帮助。 - Yevgeniy Afanasyev
3
这个答案完全正确。问题不在于答案本身。请看:https://www.php.net/manual/zh/language.oop5.traits.php:“如果一个 trait 定义了一个属性,那么一个类就不能定义一个同名属性,除非它兼容(即具有相同的可见性和初始值),否则会发生致命错误。” - Zoltán Fekete

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