Ответ 1
Черты иногда описываются как "копирование и вставка с помощью компилятора"; результат использования Trait всегда может быть выписан как действительный класс в своем собственном праве. Поэтому в значении нет понятия parent
, потому что, как только Признак был применен, его методы неотличимы от тех, которые определены в самом классе или импортированы из других признаков одновременно.
Аналогично, документы в формате PDF:
Если две черты вставляют метод с тем же именем, возникает фатальная ошибка, если конфликт явно не разрешен.
Как таковые, они не очень подходят для ситуаций, когда вы хотите смешивать несколько вариантов одного и того же поведения, потому что нет возможности для базовых функциональных возможностей и смешанных функциональных возможностей для общего общения друг с другом.
В моем понимании проблема, которую вы на самом деле пытаетесь решить, такова:
- добавить пользовательские Accessors и Mutators в класс модели Eloquent
- добавить дополнительные элементы в защищенный массив
$appends
, соответствующий этим методам.
Одним из подходов было бы продолжать использовать Черты и использовать Reflection, чтобы динамически обнаружить, какие методы были добавлены. Однако будьте осторожны, что Reflection имеет репутацию довольно медленного.
Чтобы сделать это, мы сначала реализуем конструктор с циклом, который мы можем связать только путем наименования метода определенным образом. Это может быть помещено в собственный признак (альтернативно, вы можете подклассифицировать класс 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);
}
}
}
}
Затем любое количество признаков, реализующих по-разному называемые методы 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
, но не прерывает другие черты:
class ReDecoratedModel extends BaseModel {
use AppendingGlue, AwesomeSauce, ChocolateSprinkles;
protected $appends = ['switched_base'];
}
Однако, если вы перетаскиваете конструктор одновременно с перемещением в AppendingGlue
, вам нужно сделать немного дополнительной работы, поскольку обсуждается в этом предыдущем ответить. Он похож на вызов 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";
}
}