Возможно тема более не актуальна, но я все же рискну поделиться своим опытом.
Если это не сложная логика формирования представления, такая как конкатенация или формирование ссылки, я для каждой модели создаю trait
Например есть модель Vehicle
Код: Выделить всё
class Vehicle extends ActiveRecord
{
use VehicleTrait;
const TYPE_CAR = 'car';
const TYPE_MOTORCYCLE = 'motorcycle';
const STATUS_ACTIVE = '1';
const STATUS_INACTIVE = '0';
// В модели храниться код, который непосредственно связан с AR, более никакой дополнительной логики
// Условия к запросам я люблю выносить в Query класс
}
Методы которые мне нужны для формирования, например URL на картинку или формирования полного имени автомобиля я размещаю в VehicleTrait'e. Главное, что бы эти методы были достаточно простыми, без какой-то сложной логики
Код: Выделить всё
trait VehicleTrait
{
use PictureTrait;
/**
* @return string
*/
public function getVehicleName()
{
return implode(' ', [$this->carBrand->name, $this->carModel->name, $this->name]);
}
/**
* @param null $size
* @return string
*/
public function getVehicleImage($size = null)
{
$picSize = is_null($size) ? self::$car_168x99 : $size;
if (is_null($this->photos)) {
return 'default.png';
}
return Html::encode($this->getPictureUrl($this->photos['0'], $picSize));
}
/**
* @return bool
*/
public function hasPictures()
{
return !is_null($this->photos);
}
}
Можно конечно вынести этот код в хелпер со статическими методами, но мне не нравится этот подход, возможно в некоторых задачах он себя оправдывает, но я решил использовать trait'ы. Так же это удобно, если у меня есть наследники от Vehicle - они автоматически получат доступ ко всем trait'ам основного класса, например
Код: Выделить всё
class Car extends Vehicle implements FeedProviderInterface, ChannelProviderInterface
{
const TYPE = self::TYPE_CAR;
public function init()
{
parent::init();
$this->setAttribute('vehicle_type', self::TYPE_CAR);
}
public static function find()
{
return new VehicleQuery(get_called_class(), ['type' => self::TYPE]);
}
public function rules()
{
$rules = parent::rules();
return $rules;
}
/**
* @return string
*/
public function getType()
{
return $this->vehicle_type;
}
/**
* @return int
*/
public function getTypeId()
{
return $this->id;
}
/**
* @return int
*/
public function getOwnerId()
{
return $this->user_id;
}
/**
* @return int
*/
public function getLanguageId()
{
return $this->language_id;
}
/**
* @return string
*/
public function getChannelModel()
{
return Channel::MODEL_CAR;
}
/**
* @return int
*/
public function getChannelKey()
{
return $this->getTypeId();
}
}
$car = new Car();
echo $car->getFullName(); // вызов метода trait'а который подключен в родительском классе
Главное - не увлекайтесь trait'ами.
Так же несколько слов о методе fields() - он очень удобен, если вы разрабатываете RESTful API. При использовании этого метода я так же люблю применять trait'ы
Без trait этот метод может выглядеть так
Код: Выделить всё
public function fields()
{
return [
'id',
'user_id',
'name',
'text',
'photos' => function () {
// Какой-то код
// В несколько строчек
// С использованием оператора сравнения
},
'photo' => function () {
// Какой-то код
// В несколько строчек
// С использованием оператора сравнения
},
'fullName' => function () {
return implode(' ', [$this->carBrand->name, $this->carModel->name, $this->name]);
},
}
использование trait'ов помогут немного привести в порядок данный код и инкапсулировать его
Код: Выделить всё
public function fields()
{
return [
'id',
'user_id',
'name',
'text',
'photos' => function (Vehicle $model) {
return $model->photos();
},
'photo' => function (Vehicle $model) {
return $model->mainPhoto();
},
'fullName' => function (Vehicle $model) {
return $model->fullName();
},
}
Для меня этот подход кажется более интересным чем статические помошники.
Есть разные мнения о трейтах, многие считают, что трейты нарушают принципы ООП, связь между объектами и это очень плохо и категорически против их использования, а многие находят в этом нововведении большую пользу.
Я отношусь ко второму лагерю, но считаю, что излишнее использование trait'ов может навредить проекту и рассматриваю трейты как инструмент, который помогает избавиться от дублирующего кода, главное в меру использовать трейты, а если вы решили писать код, который изменяет поведение родительского объекта, например __get или __set методы, документируйте это, что бы другие разработчики знали об этой особенности.
Лучше конечно документировать каждый метод или свойство, IDE нам в этом помогает.
К слову об IDE, последняя версия PHPStorm'а отлично поддерживает трейты. Ранее была проблема с контекстом $this, теперь эту проблему устранили.