特性有时被描述为“编译器辅助的复制和粘贴”;使用 Trait 的结果始终可以以其本身的方式写为有效的类。因此不存在这样的概念parent
在 Trait 中,因为一旦应用了 Trait,它的方法就与类本身定义的方法没有区别,或者同时从其他 Trait 导入。
同样,如PHP 文档说 http://php.net/traits:
如果两个 Traits 插入一个同名的方法,并且没有显式解决冲突,则会产生致命错误。
因此,它们不太适合您想要混合同一行为的多个变体的情况,因为基本功能和混合功能无法以通用方式相互通信。
根据我的理解,您实际上想要解决的问题是:
- 将自定义访问器和修改器添加到 Eloquent 模型类
- 将其他项目添加到受保护的项目中
$appends
匹配这些方法的数组
一种方法是继续使用 Traits,并使用反射 http://php.net/reflection动态发现添加了哪些方法。但是,请注意,反射因速度相当慢而闻名。
为此,我们首先实现一个带有循环的构造函数,我们可以通过以特定方式命名方法来挂钩该循环。这可以放入它自己的 Trait 中(或者,您可以将 Eloquent 进行子类化)Model
类与您自己的增强版本):
trait AppendingGlue {
public function __construct() {
// parent refers not to the class being mixed into, but its parent
parent::__construct();
// Find and execute all methods beginning 'extraConstruct'
$mirror = new ReflectionClass($this);
foreach ( $mirror->getMethods() as $method ) {
if ( strpos($method->getName(), 'extraConstruct') === 0 ) {
$method->invoke($this);
}
}
}
}
然后任意数量的 Traits 实现不同的名称extraConstruct
方法:
trait AwesomeSauce {
public function extraConstructAwesomeSauce() {
$this->appends[] = 'awesome_sauce';
}
public function doAwesomeSauceStuff() {
}
}
trait ChocolateSprinkles {
public function extraConstructChocolateSprinkles() {
$this->appends[] = 'chocolate_sprinkles';
}
public function doChocolateSprinklesStuff() {
}
}
最后,我们将所有特征混合到一个简单的模型中,并检查结果:
class BaseModel {
protected $appends = array('base');
public function __construct() {
echo "Base constructor run OK.\n";
}
public function getAppends() {
return $this->appends;
}
}
class DecoratedModel extends BaseModel {
use AppendingGlue, AwesomeSauce, ChocolateSprinkles;
}
$dm = new DecoratedModel;
print_r($dm->getAppends());
我们可以设置初始内容$appends
在装饰模型本身内部,它将取代BaseModel
定义,但不中断其他 Traits:
class ReDecoratedModel extends BaseModel {
use AppendingGlue, AwesomeSauce, ChocolateSprinkles;
protected $appends = ['switched_base'];
}
但是,如果您在混合的同时重写构造函数AppendingGlue
,你确实需要做一些额外的工作,因为在之前的回答中讨论过 https://stackoverflow.com/a/12583603/157957。与调用类似parent::__construct
在继承情况下,但您必须为特征的构造函数添加别名才能访问它:
class ReConstructedModel extends BaseModel {
use AppendingGlue { __construct as private appendingGlueConstructor; }
use AwesomeSauce, ChocolateSprinkles;
public function __construct() {
// Call the mixed-in constructor explicitly, like you would the parent
// Note that it will call the real parent as well, as though it was a grand-parent
$this->appendingGlueConstructor();
echo "New constructor executed!\n";
}
}
这可以通过继承一个存在的类来避免AppendingGlue
特征,或者已经使用它:
class GluedModel extends BaseModel {
use AppendingGlue;
}
class ReConstructedGluedModel extends GluedModel {
use AwesomeSauce, ChocolateSprinkles;
public function __construct() {
// Standard call to the parent constructor
parent::__construct();
echo "New constructor executed!\n";
}
}
这是一个所有这些的现场演示 http://viper-7.com/S2ecJ5.