Я использую Laravel 5.1 и хотел бы получить доступ к массиву на модели из Trait, когда модель до модели использует массив appends
.
Я хотел бы добавить некоторые элементы в массив appends, если он существует из моего свойства. Я не хочу редактировать модель, чтобы достичь этого. Являются ли черты полезными в этом сценарии или я должен использовать наследование?
array_push($this->appends, 'saucedByCurrentUser');
Вот как работает моя текущая установка.
Черта
<?php namespace App; trait AwesomeSauceTrait { /** * Collection of the sauce on this record */ public function awesomeSauced() { return $this->morphMany('App\AwesomeSauce', 'sauceable')->latest(); } public function getSaucedByCurrentUserAttribute() { if(\Auth::guest()){ return false; } $i = $this->awesomeSauced()->whereUserId(\Auth::user()->id)->count(); if ($i > 0){ return true; } return false; } }
Модель
<?php namespace App; use App\AwesomeSauceTrait; use Illuminate\Database\Eloquent\Model; class FairlyBlandModel extends Model { use AwesomeSauceTrait; protected $appends = array('age','saucedByCurrentUser'); }
То, что я хотел бы сделать, – это нечто такое же, как и расширение класса. У меня есть несколько похожих черт, поэтому использование наследования становится несколько уродливым.
trait AwesomeSauceTrait { function __construct() { parent::__construct(); array_push($this->appends, 'saucedByCurrentUser'); } }
Я видел некоторые обходные пути для этого , но ни один из них не выглядит лучше / чище, чем просто добавлять элемент в массив вручную. Любые идеи приветствуются.
Обновить
Я обнаружил этот способ выполнения того, что мне нужно для одного признака, но он работает только по одному признаку, и я не вижу преимущества использования этого над наследованием.
черта
protected $awesomeSauceAppends = ['sauced_by_current_user']; protected function getArrayableAppends() { array_merge($this->appends, $this->awesomeSauceAppends); parent::getArrayableAppends(); }
Как я сейчас обрабатываю свою модель, для чего она стоит.
модель
public function __construct() { array_merge($this->appends, $this->awesomeSauceAppends); }
Черты иногда описываются как «копирование и вставка с помощью компилятора»; результат использования Trait всегда может быть выписан как действительный класс в своем собственном праве. Поэтому в представлении нет понятия parent
, потому что, как только Признак был применен, его методы неотличимы от тех, которые определены в самом классе или импортированы из других признаков одновременно.
Аналогично, как говорят документы PHP :
Если две черты вставляют метод с тем же именем, возникает фатальная ошибка, если конфликт явно не разрешен.
Таким образом, они не очень подходят для ситуаций, когда вы хотите смешивать несколько вариантов одного и того же поведения, потому что нет возможности для базовых функциональных возможностей и смешанных функциональных возможностей для общего общения друг с другом.
В моем понимании проблема, которую вы на самом деле пытаетесь решить, такова:
$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
реализующих по-разному называемые методы 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
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"; } }
Вот живая демонстрация всего этого .
ПОЦЕЛУЙ:
Я не вижу причин, по которым вы должны использовать черту, когда вы просто добавляете атрибуты.
Я бы рекомендовал использовать черту без конструктора, как вы делали, только если вы модель становится довольно громоздкой, и вы хотите сгладить вещи.
Также обратите внимание, что это не правильный способ добавления атрибута
protected $appends = array('age','saucedByCurrentUser');
Вы можете сделать это:
protected $appends = array('age','sauced_by_current_user');
Добавляет имена атрибутов, если snake_case его метода Name
Отредактировано:
Идея добавления заключается в том, чтобы динамически добавлять поля, которые не существуют в вашей таблице базы данных, в вашу модель, поэтому после этого вы можете:
$model = FairlyBlandModel ::find(1); dd($model->sauced_by_current_user);