Использование трейтов в Laravel

Испольщование трейтов в проектах Laravel

Недавно я проводил рефакторинг одного из своих проектов и обнаружил, что постоянно применяю одни и те же методы в eloquent-моделях для построения их отношений с классом Account. К сведению, я предпочитаю использовать геттеры и сеттеры, а не магию.

Допустим, у нас есть модель Post, которая выглядит примерно так:

namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Post
 *
 * @package App
 */
class Post extends Model
{
    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->getAttribute('title');
    }

    /**
     * @param string $title
     * @return $this
     */
    public function setTitle(string $title)
    {
        $this->setAttribute('title', $title);

        return $this;
    }

    /**
     * @return string
     */
    public function getPost()
    {
        return $this->getAttribute('post');
    }

    /**
     * @param string $post
     * @return $this
     */
    public function setPost(string $post)
    {
        $this->setAttribute('post', $post);

        return $this;
    }

    /**
     * @param Account $account
     * @return $this
     */
    public function setAccount(Account $account)
    {
        $this->account()->associate($account);

        return $this;
    }

    /**
     * @return Account|null;
     */
    public function getAccount()
    {
        return $this->getAttribute('account');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function account()
    {
        return $this->belongsTo(Account::class, 'account_id', 'id');
    }
}

Здесь у нас несколько методов для свойств модели Post и методы отношений с Account.

Если мы захотим добавить еще одну модель, которая также имеет отношения с Account, то нам нужно будет добавить те же самые методы. Это займет время, и если вы когда-нибудь захотите изменить эти методы, то вам придется проделать это во всех моделях, имеющих эти отношения.

Использование Traits

Начнём с определения из документации php.net:

Трейт — это механизм обеспечения повторного использования кода в языках с поддержкой только одиночного наследования, таких как PHP. Трейт предназначен для уменьшения некоторых ограничений одиночного наследования, позволяя разработчику повторно использовать наборы методов свободно, в нескольких независимых классах и реализованных с использованием разных архитектур построения классов. Семантика комбинации трейтов и классов определена таким образом, чтобы снизить уровень сложности, а также избежать типичных проблем, связанных с множественным наследованием и смешиванием (mixins).

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

Это идеальная причина для использования трейтов. Мы создадим трейт под названием HasAccountTrait, который будет содержать все методы для отношения с Account:

namespace App;

/**
 * Class HasAccountTrait
 *
 * @package App
 */
trait HasAccountTrait
{
    /**
     * @param Account $account
     * @return $this
     */
    public function setAccount(Account $account)
    {
        $this->account()->associate($account);

        return $this;
    }

    /**
     * @return Account|null;
     */
    public function getAccount()
    {
        return $this->getAttribute('account');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function account()
    {
        return $this->belongsTo(Account::class, 'account_id', 'id');
    }
}

Теперь мы можем навести порядок в нашей модели Post и использовать этот трейт. Выглядеть это будет примерно так:

namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Post
 *
 * @package App
 */
class Post extends Model
{
    use HasAccountTrait;
    
    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->getAttribute('title');
    }

    /**
     * @param string $title
     * @return $this
     */
    public function setTitle(string $title)
    {
        $this->setAttribute('title', $title);

        return $this;
    }

    /**
     * @return string
     */
    public function getPost()
    {
        return $this->getAttribute('post');
    }

    /**
     * @param string $post
     * @return $this
     */
    public function setPost(string $post)
    {
        $this->setAttribute('post', $post);

        return $this;
    }
}

Вывод

Использование трейтов подразумевает, что мы создаем DRY код (Don’t repeat yourself — не повторяйся) при создании отношений к одному и тому же источнику. Это может ускорить разработку, в зависимости от того, сколько отношений используется в вашем приложении.

Автор: Tom Ellis
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.