Черты с PHP и Laravel

Я использую 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); } 

Solutions Collecting From Web of "Черты с PHP и Laravel"

Черты иногда описываются как «копирование и вставка с помощью компилятора»; результат использования Trait всегда может быть выписан как действительный класс в своем собственном праве. Поэтому в представлении нет понятия parent , потому что, как только Признак был применен, его методы неотличимы от тех, которые определены в самом классе или импортированы из других признаков одновременно.

Аналогично, как говорят документы PHP :

Если две черты вставляют метод с тем же именем, возникает фатальная ошибка, если конфликт явно не разрешен.

Таким образом, они не очень подходят для ситуаций, когда вы хотите смешивать несколько вариантов одного и того же поведения, потому что нет возможности для базовых функциональных возможностей и смешанных функциональных возможностей для общего общения друг с другом.

В моем понимании проблема, которую вы на самом деле пытаетесь решить, такова:

  • добавьте пользовательские 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 реализующих по-разному называемые методы 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);