Версія фреймворка: 8.x

Eloquent: Мутатори

Вступ

Аксесуари та мутатори дозволяють форматувати значення атрибутів Eloquent під час отримання або встановлення їх на екземплярах моделі. Наприклад, ви можете використовуватиШифрувач Laravelщоб зашифрувати значення, коли воно зберігається в базі даних, а потім автоматично розшифрувати атрибут при доступі до нього за моделлю Eloquent.

На додаток до спеціальних аксесуарів та мутаторів, Eloquent може також автоматично передавати поля датиВуглецьекземпляри або навітьперекинути текстові поля в JSON.

Аксесуари та мутатори

Визначення доступу

Щоб визначити аксесуар, створіть файлgetFooAttributeметод на вашій моделі деFoo- це "стовбурчаста" назва стовпця, до якого ви хочете отримати доступ. У цьому прикладі ми визначимо аксесуар дляfirst_nameатрибут. Eloquent автоматично намагається викликати аксесуар при спробі отримати значенняfirst_nameатрибут:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get the user's first name.
     *
     * @param  string  $value
     * @return string
     */
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }
}

Як бачите, вихідне значення стовпця передається користувачеві, що дозволяє маніпулювати та повертати значення. Щоб отримати доступ до значення доступу, ви можете отримати доступ доfirst_nameатрибут на екземплярі моделі:

$user = App\Models\User::find(1);

$firstName = $user->first_name;

Ви також можете використовувати засоби доступу для повернення нових, обчислених значень із існуючих атрибутів:

/**
 * Get the user's full name.
 *
 * @return string
 */
public function getFullNameAttribute()
{
    return "{$this->first_name} {$this->last_name}";
}
Якщо ви хочете, щоб ці обчислювані значення були додані до View масиву / JSON вашої моделі,вам потрібно буде їх додати.

Визначення мутатора

Щоб визначити мутатора, визначте asetFooAttributeметод на вашій моделі деFoo- це "стовбурчаста" назва стовпця, до якого ви хочете отримати доступ. Отже, знову давайте визначимо мутатора дляfirst_nameатрибут. Цей мутатор буде автоматично викликаний при спробі встановити значенняfirst_nameатрибут на моделі:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Set the user's first name.
     *
     * @param  string  $value
     * @return void
     */
    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = strtolower($value);
    }
}

Мутатор отримає значення, яке встановлюється в атрибуті, що дозволяє вам маніпулювати значенням і встановлювати маніпульоване значення на внутрішній внутрішній моделі Eloquent$attributesмайно. Так, наприклад, якщо ми намагаємося встановитиfirst_nameприписуватиSally:

$user = App\Models\User::find(1);

$user->first_name = 'Sally';

У цьому прикладіsetFirstNameAttributeфункція буде викликана зі значеннямSally. Потім мутатор застосуєstrtolowerфункцію до імені та встановіть її результуюче значення у внутрішній$attributesмасив.

Мутатори дати

За замовчуванням Eloquent конвертує файлcreated_atіupdated_at columns to instances of Вуглець, що розширює PHPDateTimeкласу та надає асортимент корисних методів. Ви можете додати додаткові атрибути дати, встановивши$datesвластивість вашої моделі:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = [
        'seen_at',
    ];
}
Ви можете вимкнути за замовчуваннямcreated_atіupdated_atпозначки часу, встановлюючи загальнодоступні$timestampsвластивість вашої моделі доfalse.

Коли стовпець вважається датою, ви можете встановити його значення як позначку часу UNIX, рядок дати (Y-m-d), рядок дати-часу або aDateTime/Carbonінстанції. Значення дати буде правильно перетворено та збережено у вашій базі даних:

$user = App\Models\User::find(1);

$user->deleted_at = now();

$user->save();

Як зазначалося вище, при отриманні атрибутів, перелічених у вашому$datesвласності, вони будуть автоматично передані вВуглецьекземплярів, що дозволяє використовувати будь-який із методів Carbon для своїх атрибутів:

$user = App\Models\User::find(1);

return $user->deleted_at->getTimestamp();

Формати дат

За замовчуванням мітки часу мають формат'Y-m-d H:i:s'. Якщо вам потрібно налаштувати формат позначки часу, встановіть$dateFormatвластивість на вашій моделі. Ця властивість визначає спосіб зберігання атрибутів дати в базі даних:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

Casting атрибутів

$castsвластивість на вашій моделі забезпечує зручний метод перетворення атрибутів у загальні типи даних.$castsвластивість має бути масивом, де ключ - це ім'я атрибута, що відтворюється, а значення - тип, до якого ви бажаєте привести стовпець. Підтримувані типи складання:integer,real,float,double,decimal:<digits>,string,boolean,object,array,collection,date,datetime,timestamp,encrypted,encrypted:object,encrypted:array, іencrypted:collection. При відливі доdecimal, ви повинні визначити кількість цифр (decimal:2).

Щоб продемонструвати Casting атрибутів, давайте додамоis_adminатрибут, який зберігається в нашій базі даних як ціле число (0або1) до логічного значення:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'is_admin' => 'boolean',
    ];
}

Теперis_adminатрибут завжди буде передано до логічного значення, коли ви отримаєте до нього доступ, навіть якщо базове значення зберігається в базі даних як ціле число:

$user = App\Models\User::find(1);

if ($user->is_admin) {
    //
}
Атрибути, які єnullне буде кинутий. Крім того, ви ніколи не повинні визначати склад (або атрибут), який має те саме ім'я, що і зв'язок.

Спеціальні Casts

Laravel має безліч вбудованих, корисних типів Casts; однак, іноді вам може знадобитися визначити власні типи акторського складу. Ви можете досягти цього, визначивши клас, який реалізуєCastsAttributesінтерфейс.

Класи, що реалізують цей інтерфейс, повинні визначати agetіsetметод.getМетод відповідає за перетворення вихідного значення з бази даних у додане значення, тоді якsetметод повинен перетворити значення відлиття у вихідне значення, яке можна зберегти в базі даних. Як приклад, ми повторно реалізуємо вбудованийjsonтип лиття як власний тип Casts:

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Json implements CastsAttributes
{
    /**
     * Cast the given value.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return array
     */
    public function get($model, $key, $value, $attributes)
    {
        return json_decode($value, true);
    }

    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  array  $value
     * @param  array  $attributes
     * @return string
     */
    public function set($model, $key, $value, $attributes)
    {
        return json_encode($value);
    }
}

Після того, як ви визначили власний тип лиття, ви можете додати його до атрибута моделі, використовуючи його назву класу:

<?php

namespace App\Models;

use App\Casts\Json;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'options' => Json::class,
    ];
}

Casting об’єкта значення

Ви не обмежуєтесь передачею значень до примітивних типів. Ви також можете передавати значення об'єктам. Визначення користувацьких приводів, які передають значення об'єктам, дуже схоже на приведення в примітивні типи; однак,setМетод повинен повертати масив пар ключ / значення, який буде використовуватися для встановлення необроблених значень, що зберігаються в моделі.

Як приклад, ми визначимо власний клас лиття, який відтворює кілька значень моделі в одинAddressоб'єкт значення. Ми припустимоAddressvalue має дві загальнодоступні властивості:lineOneіlineTwo:

<?php

namespace App\Casts;

use App\Models\Address as AddressModel;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use InvalidArgumentException;

class Address implements CastsAttributes
{
    /**
     * Cast the given value.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return \App\Models\Address
     */
    public function get($model, $key, $value, $attributes)
    {
        return new AddressModel(
            $attributes['address_line_one'],
            $attributes['address_line_two']
        );
    }

    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  \App\Models\Address  $value
     * @param  array  $attributes
     * @return array
     */
    public function set($model, $key, $value, $attributes)
    {
        if (! $value instanceof AddressModel) {
            throw new InvalidArgumentException('The given value is not an Address instance.');
        }

        return [
            'address_line_one' => $value->lineOne,
            'address_line_two' => $value->lineTwo,
        ];
    }
}

Під час Broadcast до об'єктів значення всі зміни, внесені до об'єкта значення, будуть автоматично синхронізовані з моделлю перед збереженням моделі:

$user = App\Models\User::find(1);

$user->address->lineOne = 'Updated Address Value';

$user->save();
Якщо ви плануєте серіалізувати ваші Eloquent моделі, що містять об'єкти значень, у форматі JSON або масиви, вам слід застосуватиIlluminate\Contracts\Support\ArrayableіJsonSerializableінтерфейси на об'єкті значення.

Серіалізація масиву / JSON

Коли модель Eloquent перетворюється на масив або JSON за допомогоюtoArrayМетод, ваші користувацькі об'єкти значення додавання, як правило, будуть серіалізовані, доки вони реалізуютьIlluminate\Contracts\Support\ArrayableіJsonSerializableінтерфейси. Однак при використанні об'єктів значення, наданих сторонніми бібліотеками, можливо, у вас не буде можливості додавати ці інтерфейси до об'єкта.

Таким чином, ви можете вказати, що ваш власний клас приведення буде відповідати за серіалізацію об'єкта значення. Для цього у вашому користувацькому складі класу має бути реалізованоIlluminate\Contracts\Database\Eloquent\SerializesCastableAttributesінтерфейс. Цей інтерфейс стверджує, що ваш клас повинен містити файлserializeметод, який повинен повернути серіалізовану форму вашого об’єкта значення:

/**
 * Get the serialized representation of the value.
 *
 * @param  \Illuminate\Database\Eloquent\Model  $model
 * @param  string  $key
 * @param  mixed  $value
 * @param  array  $attributes
 * @return mixed
 */
public function serialize($model, string $key, $value, array $attributes)
{
    return (string) $value;
}

Вхідний Casting

Іноді вам може знадобитися написати власний привід, який перетворює лише значення, встановлені в моделі, і не виконує жодних операцій, коли атрибути отримуються з моделі. Класичний приклад лише вхідного складу - це хешування. Тільки вхідні спеціальні зливки повинні реалізовуватиCastsInboundAttributesінтерфейс, який вимагає лишеsetметод, який слід визначити.

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;

class Hash implements CastsInboundAttributes
{
    /**
     * The hashing algorithm.
     *
     * @var string
     */
    protected $algorithm;

    /**
     * Create a new cast class instance.
     *
     * @param  string|null  $algorithm
     * @return void
     */
    public function __construct($algorithm = null)
    {
        $this->algorithm = $algorithm;
    }

    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  array  $value
     * @param  array  $attributes
     * @return string
     */
    public function set($model, $key, $value, $attributes)
    {
        return is_null($this->algorithm)
                    ? bcrypt($value)
                    : hash($this->algorithm, $value);
    }
}

Параметри лиття

Під час приєднання користувацького приведення до моделі параметри приведення можуть бути вказані, відокремлюючи їх від імені класу, використовуючи a:символом та кількома параметрами, що розмежовують комами. Параметри передаються конструктору класу Casts:

/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'secret' => Hash::class.':sha256',
];

Кастеблі

Замість того, щоб приєднувати спеціальний привід до вашої моделі, ви можете альтернативно приєднати клас, який реалізуєIlluminate\Contracts\Database\Eloquent\Castableінтерфейс:

protected $casts = [
    'address' => \App\Models\Address::class,
];

Об'єкти, що реалізуютьCastableінтерфейс повинен визначати acastUsingметод, який повертає ім'я класу спеціального класу заклинателя, який відповідає за Broadcast до та зCastableклас:

<?php

namespace App\Models;

use Illuminate\Contracts\Database\Eloquent\Castable;
use App\Casts\Address as AddressCast;

class Address implements Castable
{
    /**
     * Get the name of the caster class to use when casting from / to this cast target.
     *
     * @param  array  $arguments
     * @return string
     */
    public static function castUsing(array $arguments)
    {
        return AddressCast::class;
    }
}

При використанніCastableкласів, ви все ще можете наводити аргументи в$castsвизначення. Аргументи будуть передані безпосередньо до класу заклинача:

protected $casts = [
    'address' => \App\Models\Address::class.':argument',
];

Масив та JSON-Casting

arrayтип приведення особливо корисний при роботі зі стовпцями, які зберігаються як серіалізовані JSON. Наприклад, якщо у вашій базі даних є файлJSONабоTEXTтип поля, що містить серіалізований JSON, додаючиarrayприв'язка до цього атрибуту автоматично десеріалізує атрибут до масиву PHP, коли ви отримуєте доступ до нього на вашій Eloquentй моделі:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'options' => 'array',
    ];
}

Після визначення акторського складу ви можете отримати доступ доoptionsатрибут, і він буде автоматично десеріалізований з JSON у масив PHP. Коли ви встановлюєте значенняoptionsатрибут, даний масив буде автоматично серіалізований назад у JSON для зберігання:

$user = App\Models\User::find(1);

$options = $user->options;

$options['key'] = 'value';

$user->options = $options;

$user->save();

Щоб оновити одне поле атрибута JSON з більш стислим синтаксисом, ви можете використовувати файл->оператор:

$user = App\Models\User::find(1);

$user->update(['options->key' => 'value']);

Дата Casting

При використанніdate or datetimeтип касту, ви можете вказати формат дати. Цей формат буде використовуватися, колимодель серіалізується до масиву або JSON:

/**
 * The attributes that should be cast.
 *
 * @var array
 */
protected $casts = [
    'created_at' => 'datetime:Y-m-d',
];

Час запиту

Іноді вам може знадобитися застосувати закиди під час виконання запиту, наприклад, при виборі вихідного значення з таблиці. Наприклад, розглянемо такий запит:

use App\Models\Post;
use App\Models\User;

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
            ->whereColumn('user_id', 'users.id')
])->get();

last_posted_at attribute on the results of this query will be a raw string. It would be convenient if we could apply a dateприв'язка до цього атрибута під час виконання запиту. Для цього ми можемо використовуватиwithCastsметод:

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
            ->whereColumn('user_id', 'users.id')
])->withCasts([
    'last_posted_at' => 'datetime'
])->get();