Использование кастомных кастов в Laravel 7

Пользовательские приведения типов в Laravel 7

Custom Eloquent Cast (Кастомные касты) это пользовательские приведения типов. Исторически вы были ограничены дефолтным набором кастов, предоставленных Laravel, охватывающие только базовые типы плюс даты. И, хотя существуют пакеты, реализующие кастомные касты, но у них есть существенный недостаток. Поскольку трейтами они переопределяют методы setAttribute и getAttribute, то их нельзя использовать с другими пакетами, которые также переопределяют эти методы.

Но теперь, когда Laravel 7 начал это поддерживать, проблем совместимости между библиотеками не будет!

Как работают кастомные касты

Любой объект, реализующий новый контракт CastsAttributes, теперь можно использовать в вашей модели в свойстве $casts. При доступе к свойствам модели, перед тем как передать его нам, Eloquent сначала проверяет, есть ли кастомный каст для преобразования значения.

Имейте в виду, что ваш каст будет вызываться при каждой get— и set-операции, поэтому рассмотрите возможность кэширования интенсивных операций.

Создание кастомных кастов

Один из популярных запросов фич для Laravel была возможность выборочного шифрования свойств модели — с помощью кастомных кастов это реализуется супер-просто.

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class EncryptCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        return encrypt($value);
    }

    public function set($model, $key, $value, $attributes)
    {
        return decrypt($value);
    }
}

В вашей модели вы можете назначить свойству только что созданный каст

class TestModel extends Model
{
    /**
     * Атрубут, который должен быть приведен к родному типу
     *
     * @var array
     */
    protected $casts = [
        'secret' => EncryptCast::class,
    ];
}

Теперь, когда мы всё настроили — давайте проверим! Поскольку функции encrypt/decrypt сериализуют ввод, то вы можете хранить практически все что угодно (но всё же, вероятно, следует придерживаться простых встроенных типов).

$model = new TestModel();

$model->secret = 'Hello World'; // Простое текстовое значение

// Зашифрованное значение (которое будет храниться в базе данных)
// Сырое значение: eyJpdiI6InV4Q25ZN0FZUW5YSEZkRCtZSGlVXC9BPT0iLCJ2Y...
$model->getAttributes()['secret'];

$model->save(); // Сохрнаяем и перезагружаем модель из БД
$model->fresh();

echo $model->secret; // Hello World

Поскольку кастомные касты это просто объекты, то они могут быть как и очень простыми, так и очень сложными, все зависит от требований. В реализации есть несколько дополнительных функций, в том числе «Входящие» (Inbound) касты (которые приводят к типу только при операции set) и возможность принимать переменные при приведении типов в моделях.

Например, мы можем обрезать строки до настраиваемой длины, но только при установке значений (поэтому любые существующие значения останутся той же длины).

class LimitCaster implements CastsInboundAttributes
{
    public function __construct($length = 25)
    {
        $this->length = $length;
    }

    public function set($model, $key, $value, $attributes)
    {
        return [$key => Str::limit((string) $value, $this->length)];
    }
}

В модели разделите имя класса каста и параметры двоеточием.

class TestModel extends Model
{
    protected $casts = [
        'limited_str' => LimitCaster::class . ':10', // Обрезаем до 10 символов
    ];
}

Выводы

Я с нетерпением хочу увидеть потрясающие касты, которые придумает Laravel-сообщество. Публиковать кастомные касты пакетами очень легко и я надеюсь мы вскоре увидим кучу пакетов с ними.

Публикую свой пакет кастов DateInterval/CarbonInterval, на случай, если кто-то еще столкнется с такой же проблемой.

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

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