Я полагаю, что большинство веб-разработчиков сталкивались с проблемой “жирных” моделей в какой-то момент своей карьеры. Даже хорошие разработчики, следующие SOLID принципам, сохраняя правила проверки и другую бизнес-логику вне моделей, иногда сталкиваются с этой проблемой.
Автор блога не разделяет позицию автора статьи.
К счастью, есть глупо простое решение, которое я никогда раньше не видел ни в одном проекте Laravel. Вот почему я решил написать эту короткую статью.
Типичная жирная модель
Начнем с примера “жирной” модели:
<?php namespace MyApp\Models;
class User extends BaseModel
{
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function orders()
{
return $this->hasMany('Order');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function projects()
{
return $this->belongsToMany('Project')
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function articles()
{
return $this->hasMany('Article');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function comments()
{
return $this->hasMany('Comment');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function role()
{
return $this->belongsTo('Role');
}
/**
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param int $roleId
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeRoleId($builder, $roleId = 0)
{
if (!$roleId)
{
return $builder;
}
return $builder->where('role_id', '=', $roleId);
}
}
Как вы видите, отношения и области действия раздувают код и делают его трудным для чтения.
Трейты спешат на помощь!
Трейты позволяют разработчику использовать наборы методов в нескольких независимых классах.
И это именно то, что мы ищем!
Давайте посмотрим, как будет выглядеть код, если мы заменим отношение “orders”на трейт.
<?php namespace MyApp\Models\Relations\HasMany;
trait Orders
{
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function orders()
{
return $this->hasMany('Order');
}
}
Я предпочитаю помещать отношения в отдельный Relations
каталог внутри Models
каталога (обратите внимание на пространство имен).
А вот и класс после нашего небольшого рефакторинга:
<?php namespace MyApp\Models;
class User extends BaseModel
{
// а вот и трейт
use Relations\HasMany\Orders;
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function projects()
{
return $this->belongsToMany('Project')
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function articles()
{
return $this->hasMany('Article');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function comments()
{
return $this->hasMany('Comment');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function role()
{
return $this->belongsTo('Role');
}
/**
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param int $roleId
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeRoleId($builder, $roleId = 0)
{
if (!$roleId)
{
return $builder;
}
return $builder->where('role_id', '=', $roleId);
}
}
Разница не очень очевидна, но позвольте мне показать вам тот же самый класс после того как я разделил все релейшены и скоупы на отдельные трейты:
<?php namespace MyApp\Models;
class User extends BaseModel
{
// Has Many Relations
use Relations\HasMany\Orders;
use Relations\HasMany\Articles;
use Relations\HasMany\Comments;
// Belongs To Relations
use Relations\BelongsTo\Role;
// Belongs To Many Relations
use Relations\BelongsToMany\Projects;
// Scopes
use Scopes\RoleId;
}
Надеюсь, теперь вы убедились, что это гораздо лучше и легче читать. У нашей бывшей жирной модели есть хороший шанс стать одним из новых Ангелов Victoria’s Secret
Выводы
Пожалуйста, не извлекайте каждое отдельное отношение в отдельный трейт. Если вам нужно расшифровать отношение, которое не следует соглашениям об именовании по умолчанию Laravel, было бы лучше не извлекать его, поскольку оно дает больше информации человеку, который читает код. Но если ваше отношение простое и следует условностям, то перенос его на трейт-хорошая идея. Его можно даже повторно использовать в ситуациях, когда, например, две модели имеют сходные belongsTo
отношения.
Несмотря на то, что они доступны начиная с PHP 5.4, трейты не очень широко распространены в сообществе PHP, и эта короткая статья показывает хороший пример преимуществ, которые вы можете получить, используя их в аналогичных сценариях.