{{TOC}} {{DOCVER 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11}} == Введение == Система объектно-реляционного отображения ([[ВП:ORM]]) Eloquent - красивая и простая реализация шаблона ((ВП:ActiveRecord)) в Laravel для работы с базами данных. Каждая таблица имеет соответствующий класс-модель, который используется для работы с этой таблицей. Модели позволяют запрашивать данные из таблиц, а также вставлять в них новые записи. Прежде чем начать, настройте ваше соединение с БД в файле %%(t)config/database.php%%. Подробнее о настройке БД читайте в ((/docs/v5/database#настройка документации)). == Определение моделей == Для начала создадим модель Eloquent. Модели обычно располагаются в папке %%(t)app%%, но вы можете поместить их в любое место, в котором работает автозагрузчик в соответствии с вашим файлом %%(t)composer.json%%. Все модели Eloquent наследуют класс %%(t)Illuminate\Database\Eloquent\Model%%. Простейший способ создать экземпляр модели - с помощью ((/docs/v5/artisan Artisan-команды)) %%(sh)make:model%%: %%(sh) php artisan make:model User %% Если вы хотите создать ((/docs/v5/migrations миграцию БД)) при создании модели, используйте параметр %%(sh)--migration%% или %%(sh)-m%%: %%(sh) php artisan make:model User --migration php artisan make:model User -m %% === Условия для моделей Eloquent === Теперь давайте посмотрим на пример модели %%(t)Flight%%, который мы будем использовать для получения и хранения информации из таблицы %%(t)flights%%: %% find(1); ~%% Если вы используете ((/docs/v5/database#чтениезапись соединения для чтения/записи)), вы можете заставить запрос использовать соединение "записи" с помощью следующего метода: ~%% $user = User::onWriteConnection()->find(1); ~%% %% == Получение моделей == После создания модели и ((/docs/v5/migrations#создание_миграций_1 связанной с ней таблицы)), вы можете начать получать данные из вашей БД. Каждая модель Eloquent представляет собой мощный ((/docs/v5/queries конструктор запросов)), позволяющий удобно выполнять запросы к связанной таблице. Например: %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) ~%% name; } ~%% %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) ~%% $flights]); } } ~%% **Доступ к значениям столбцов** Если у вас есть экземпляр модели Eloquent, вы можете обращаться к значениям столбцов модели, обращаясь к соответствующим свойствам. Например, давайте пройдёмся по каждому экземпляру %%(t)Flight%%, возвращённому нашим запросом, и выведем значение столбца %%(t)name%%: ~%% foreach ($flights as $flight) { echo $flight->name; } ~%% %% **Добавление дополнительных ограничений** Метод %%all()%% в Eloquent возвращает все результаты из таблицы модели. Поскольку модели Eloquent работают как ((/docs/v5/queries конструктор запросов)), вы можете также добавить ограничения в запрос, а затем использовать метод %%get()%% для получения результатов: %% $flights = App\Flight::where('active', 1) ->orderBy('name', 'desc') ->take(10) ->get(); %% .(alert) Все методы, доступные в ((docs/v5/queries конструкторе запросов)), также доступны при работе с моделями Eloquent. Вы можете использовать любой из них в запросах Eloquent. === Коллекции === Такие методы Eloquent, как %%all()%% и %%get()%%, которые получают несколько результатов, возвращают экземпляр %%(t)Illuminate\Database\Eloquent\Collection%%. Класс %%(t)Collection%% предоставляет ((/docs/v5/eloquent-collections#доступные множество полезных методов)) для работы с результатами Eloquent. %% $flights = $flights->reject(function ($flight) { return $flight->cancelled; }); %% Само собой, вы также можете просто перебирать такую коллекцию в цикле как массив: %% foreach ($flights as $flight) { echo $flight->name; } %% === Разделение результата на блоки === Если вам нужно обработать тысячи записей Eloquent, используйте команду %%chunk()%% (!!(tl_note) блок - //прим. пер.//!!). Метод %%chunk()%% получает модель Eloquent частями, передавая их в замыкание для обработки. Использование этого метода уменьшает используемый объём оперативной памяти: %% Flight::chunk(200, function ($flights) { foreach ($flights as $flight) { // } }); %% Первый передаваемый в метод аргумент - число записей, получаемых в одном блоке. Передаваемая в качестве второго аргумента функция-замыкание будет вызываться для каждого блока, получаемого из БД. %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) Для получения каждого блока записей, передаваемого в замыкание, будет выполнен запрос к базе данных. **Использование курсоров** Метод %%cursor()%% позволяет проходить по записям базы данных, используя курсор, который выполняет только один запрос. При обработке больших объёмов данных метод %%cursor()%% может значительно уменьшить расходование памяти: ~%% foreach (Flight::where('foo', 'bar')->cursor() as $flight) { // } ~%% %% == Получение одиночных моделей / агрегатные функции == Разумеется, кроме получения всех записей указанной таблицы вы можете также получить конкретные записи с помощью %%find()%% и %%first()%%. Вместо коллекции моделей эти методы возвращают один экземпляр модели: %% // Получение модели по её первичному ключу... $flight = App\Flight::find(1); // Получение первой модели, удовлетворяющей условиям... $flight = App\Flight::where('active', 1)->first(); %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) Также вы можете вызвать метод %%find()%% с массивом первичных ключей, который вернёт коллекцию подходящих записей: ~%% $flights = App\Flight::find([1, 2, 3]); ~%% %% **Исключения "не найдено"** Иногда вам нужно возбудить исключение, если определённая модель не была найдена. Это удобно в маршрутах и контроллерах. Методы %%findOrFail()%% и %%firstOrFail()%% получают первый результат запроса. А если результатов не найдено, происходит исключение %%(t)Illuminate\Database\Eloquent\ModelNotFoundException%%: %% $model = App\Flight::findOrFail(1); $model = App\Flight::where('legs', '>', 100)->firstOrFail(); %% Если исключение не поймано, пользователю автоматически посылается HTTP-отклик %%(t)404%%. Нет необходимости писать явные проверки для возврата откликов %%(t)404%% при использовании этих методов: %% Route::get('/api/flights/{id}', function ($id) { return App\Flight::findOrFail($id); }); %% %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) Это позволит вам отловить исключение, занести его в журнал и вывести необходимую страницу ошибки. Чтобы поймать исключение %%ModelNotFoundException%%, добавьте какую-либо логику в ваш файл %%(t)app/Exceptions/Handler.php%%. ~%% use Illuminate\Database\Eloquent\ModelNotFoundException; class Handler extends ExceptionHandler { public function render($request, Exception $e) { if ($e instanceof ModelNotFoundException) { // Ваша логика для ненайденной модели... } return parent::render($request, $e); } } ~%% **Построение запросов в моделях Eloquent** ~%% $users = User::where('votes', '>', 100)->take(10)->get(); foreach ($users as $user) { var_dump($user->name); } ~%% %% === Агрегатные функции === Вам также доступны ((/docs/v5/queries#агрегатные агрегатные функции)) ((/docs/v5/queries конструктора запросов)), такие как %%count()%%, %%max()%%, %%sum()%% и др. Эти методы возвращают соответствующее скалярное значение вместо полного экземпляра модели: %% $count = App\Flight::where('active', 1)->count(); $max = App\Flight::where('active', 1)->max('price'); %% %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) Если у вас не получается создать нужный запрос с помощью конструктора, то можно использовать метод %%whereRaw()%%: ~%% $users = User::whereRaw('age > ? and votes = 100', [25])->get(); ~%% %% == Вставка и изменение моделей == === Вставки === Для создания новой записи в БД просто создайте экземпляр модели, задайте атрибуты модели и вызовите метод %%save()%%: %% name = $request->name; $flight->save(); } } %% В этом примере мы просто присвоили значение параметра %%(t)name%% из входящего HTTP-запроса атрибуту %%(t)name%% экземпляра модели %%(t)App\Flight%%. При вызове метода %%save()%% запись будет вставлена в таблицу. Отметки времени %%(t)created_at%% и %%updated_at(t)%% будут автоматически установлены при вызове %%save()%%, поэтому не надо задавать их вручную. %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) .(alert) Обычно ваши модели Eloquent содержат автоматические числовые ключи (//autoincrementing//). Однако если вы хотите использовать собственные ключи, установите свойство %%$incrementing%% класса модели в значение %%false%%. %% === Изменения === Метод %%save()%% можно использовать и для изменения существующей модели в БД. Для изменения модели вам нужно получить её, изменить необходимые атрибуты и вызвать метод %%save()%%. Отметка времени %%updated_at(t)%% будет установлена автоматически, поэтому не надо задавать её вручную: %% $flight = App\Flight::find(1); $flight->name = 'New Flight Name'; $flight->save(); %% **Массовые изменения** Изменения можно выполнить для нескольких моделей, которые соответствуют указанному запросу. В этом примере все рейсы, которые отмечены как %%(t)active%% и имеют %%(t)destination%% равное %%(t)San Diego%%, будут отмечены как %%(t)delayed%%: %% App\Flight::where('active', 1) ->where('destination', 'San Diego') ->update(['delayed' => 1]); %% Метод %%update()%% ожидает массив пар столбец/значение, обозначающий, какие столбцы необходимо изменить. .(alert) При использовании массовых изменений Eloquent для изменяемых моделей не будут возникать события %%(t)saved%% и %%(t)updated%%. Это происходит потому, что на самом деле модели вообще не извлекаются при массовом изменении. === Массовое заполнение === Вы также можете использовать метод %%create()%% для создания и сохранения модели одной строкой. Метод вернёт добавленную модель. Однако перед этим вам нужно определить либо свойство %%$fillable%%, либо %%$guarded%% в классе модели, так как все модели Eloquent изначально защищены от массового заполнения. Уязвимость массового заполнения проявляется, когда пользователь передаёт с помощью запроса неподходящий HTTP-параметр, и вы не ожидаете, что этот параметр изменит столбец в вашей БД. Например, злоумышленник может послать в HTTP-запросе параметр %%(t)is_admin%%, который затем передаётся в метод %%create()%% вашей модели, позволяя пользователю повысить свои привилегии до администратора. Поэтому, для начала надо определить, для каких атрибутов разрешить массовое назначение. Это делается с помощью свойства модели %%$fillable%%. Например, давайте разрешим массовое назначение атрибута %%(t)name%% нашей модели %%(t)Flight%%: %% 'Flight 10']); %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) Если у вас уже есть экземпляр модели, вы можете заполнить его массивом атрибутов с помощью метода %%fill()%%: ~%% $flight->fill(['name' => 'Flight 22']); ~%% %% **Защитные атрибуты** Параметр %%$fillable%% служит "белым списком" атрибутов, для которых разрешено массовое назначение. А параметр %%$guarded%% служит "чёрным списком". Параметр %%$guarded%% должен содержать массив атрибутов, для которых будет запрещено массовое назначение. Атрибутам, не вошедшим в этот массив, будет разрешено массовое назначение. Само собой, вы должны использовать только один из этих параметров. В данном примере всем атрибутам **кроме %%(t)price%%** разрешено массовое заполнение: %% 'Flight 10']); // Получить рейс по атрибутам, или создать новый экземпляр... $flight = App\Flight::firstOrNew(['name' => 'Flight 10']); %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) **updateOrCreate** Ещё вы можете столкнуться с ситуациями, когда надо обновить существующую модель или создать новую, если её пока нет. Laravel предоставляет метод %%updateOrCreate()%% для выполняет этой задачи за один шаг. Подобно методу %%firstOrCreate()%%, метод %%updateOrCreate()%% сохраняет модель, поэтому не надо вызывать метод %%save()%%: ~%% // Если есть рейс из Oakland в San Diego, установить стоимость = $99. // Если подходящей модели нет, создать новую. $flight = App\Flight::updateOrCreate( ['departure' => 'Oakland', 'destination' => 'San Diego'], ['price' => 99] ); ~%% %% %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) После сохранения или создания новой модели, использующей автоматические (//autoincrementing//) ID, вы можете получать ID объектов, обращаясь к их атрибуту %%id%%: ~%% $insertedId = $user->id; ~%% **Сохранение модели и её отношений** Иногда вам может быть нужно сохранить не только модель, но и все её ((#отношения)). Для этого просто используйте метод %%push()%%: ~%% $user->push(); ~%% Вы также можете выполнять обновления в виде запросов к набору моделей: ~%% $affectedRows = User::where('votes', '>', 100)->update(['status' => 2]); ~%% .(alert) При обновлении набора моделей с помощью конструктора запросов Eloquent никакие события моделей не срабатывают. %% == Удаление моделей == Для удаления модели вызовите метод %%delete()%% на её экземпляре: %% $flight = App\Flight::find(1); $flight->delete(); %% **Удаление модели по ключу** В предыдущем примере мы получили модель из БД перед вызовом метода %%delete()%%. Но если вы знаете первичный ключ модели, вы можете удалить модель, не получая её. Для этого вызовите метод %%destroy()%%: %% App\Flight::destroy(1); App\Flight::destroy([1, 2, 3]); App\Flight::destroy(1, 2, 3); %% **Удаление модели запросом** Конечно, вы также можете выполнить оператор удаления на наборе моделей. В этом примере мы удалим все рейсы, отмеченные неактивными. Подобно массовому обновлению, массовое удаление не вызовет никаких событий для удаляемых моделей: %% $deletedRows = App\Flight::where('active', 0)->delete(); %% .(alert) При использовании массового удаления Eloquent для удаляемых моделей не будут возникать события %%(t)deleting%% и %%(t)deleted%%. Это происходит потому, что на самом деле модели вообще не извлекаются при выполнении оператора удаления. %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) **Обновление только времени изменения модели** Если вам нужно просто обновить время изменения записи - используйте метод %%touch()%%: ~%% $user->touch(); ~%% %% ((#soft-deleting)) === Мягкое удаление === Кроме обычного удаления записей из БД Eloquent также может "мягко удалять" модели. Когда вы "мягко" удаляете модель, она на самом деле остаётся в базе данных, но в БД устанавливается её атрибут %%(t)deleted_at%%. Если у модели ненулевое значение %%(t)deleted_at%%, значит модель мягко удалена. Для включения мягкого удаления для модели используйте для неё типаж %%Illuminate\Database\Eloquent\SoftDeletes%% и добавьте столбец %%(t)deleted_at%% в свойство %%$dates%%: %% softDeletes(); }); %% Теперь когда вы вызовите метод %%delete()%%, поле %%(t)deleted_at%% будет установлено в значение текущей даты и времени. При запросе моделей, использующих мягкое удаление, "удалённые" модели не будут включены в результат запроса. Для определения того, удалён ли экземпляр модели, используйте метод %%trashed()%%: %% if ($flight->trashed()) { // } %% === Запрос мягко удалённых моделей === **Включение удалённых моделей в результат выборки** Как было сказано, мягко удалённые модели автоматически исключаются из результатов запроса. Для отображения всех моделей, в том числе удалённых, используйте метод %%withTrashed()%%: %% $flights = App\Flight::withTrashed() ->where('account_id', 1) ->get(); %% Метод %%withTrashed%% может быть использован в ((/docs/v5/eloquent-relationships отношениях)): %% $flight->history()->withTrashed()->get(); %% %%(DOCNEW 5.1=f60f8b3697b3ffe381df4ddb7e2875ffce940643 1.04.2016 17:39:10, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) **Рекомендации для условия %%(t)WHERE%%** При добавлении в запрос мягко удалённых моделей условий %%(t)orWhere%% всегда используйте ((/docs/v5/queries#сложная сложные условия WHERE)) для их логической группировки. Например: ~%% User::where(function($query) { $query->where('name', '=', 'John') ->orWhere('votes', '>', 100); }) ->get(); ~%% Таким образом получится следующий SQL-запрос: %%(sql) select * from `users` where `users`.`deleted_at` is null and (`name` = 'John' or `votes` > 100) ~%% Если условия %%(t)orWhere%% не сгруппированы, то получится следующий SQL-запрос, который будет содержать мягко удалённые записи: %%(sql) select * from `users` where `users`.`deleted_at` is null and `name` = 'John' or `votes` > 100 ~%% %% **Получение только мягко удалённых моделей** Если вы хотите получить **только** мягко удалённые модели, вызовите метод %%onlyTrashed()%%: %% $flights = App\Flight::onlyTrashed() ->where('airline_id', 1) ->get(); %% **Восстановление мягко удалённых моделей** Иногда необходимо восстановить мягко удалённую модель. Для восстановления мягко удалённой модели в активное состояние используется метод %%restore()%%: %% $flight->restore(); %% Вы также можете использовать его в запросе для быстрого восстановления нескольких моделей. Подобно другим массовым операциям, это не вызовет никаких событий для восстанавливаемых моделей:: %% App\Flight::withTrashed() ->where('airline_id', 1) ->restore(); %% Как и метод %%withTrashed%%, метод %%restore()%% можно использовать и в ((/docs/v5/eloquent-relationships отношениях)): %% $flight->history()->restore(); %% **Полное удаление моделей** Если вы хотите полностью удалить модель из БД, используйте метод %%forceDelete()%%: %% // Принудительное удаление одного экземпляра модели... $flight->forceDelete(); // Принудительное удаление всех связанных моделей... $flight->history()->forceDelete(); %% == Заготовки запросов == %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) === Глобальные заготовки === Глобальные заготовки позволяют добавить ограничения во все запросы для данной модели. Собственная функция Laravel ((#soft-deleting мягкое удаление)) использует глобальные заготовки, чтобы получать из базы данных только "неудалённые" модели. Написание собственных глобальных заготовок обеспечивает удобный и простой способ наложить определённые ограничения на каждый запрос для конкретной модели. **Написание глобальных заготовок** Писать глобальные заготовки просто. Определите класс, реализующий интерфейс %%(t)Illuminate\Database\Eloquent\Scope%%. Этот интерфейс требует реализации одного метода: %%apply()%%. Метод %%apply()%% может добавить к запросу ограничение %%where%% при необходимости: ~%% where('age', '>', 200); } } ~%% .(alert) В Laravel-приложении по умолчанию нет определённой папки для хранения заготовок, поэтому вы можете создать свою папку %%(t)Scopes%% в папке %%(t)app%% вашего приложения. **Применение глобальных заготовок** Для назначения глобальной заготовки на модель вам надо переопределить метод %%boot()%% данной модели и использовать метод %%addGlobalScope()%%: ~%% 200 ~%% **Анонимные глобальные заготовки** Также Eloquent позволяет определять глобальные заготовки с помощью замыканий, что особенно удобно для простых заготовок, которым не нужен отдельный класс: ~%% where('age', '>', 200); }); } } ~%% Первый аргумент %%addGlobalScope()%% служит идентификатором для удаления заготовки: ~%% User::withoutGlobalScope('age')->get(); ~%% **Удаление глобальных заготовок** Если вы хотите удалить глобальную заготовку для данного запроса, то можете использовать метод %%withoutGlobalScope()%%. Этот метод принимает единственный аргумент - имя класса глобальной заготовки: ~%% User::withoutGlobalScope(AgeScope::class)->get(); ~%% Если вы хотите удалить несколько или все глобальные заготовки, то можете использовать метод %%withoutGlobalScopes()%%: ~%% // Удалить все глобальные заготовки... User::withoutGlobalScopes()->get(); // Удалить некоторые глобальные заготовки... User::withoutGlobalScopes([ FirstScope::class, SecondScope::class ])->get(); ~%% %% === Локальные заготовки === Заготовки позволяют вам повторно использовать логику запросов в моделях. Например, если вам часто требуется получать пользователей, которые сейчас "популярны". Для создания заготовки просто начните имя метода с префикса %%(t)scope%%: %% where('votes', '>', 100); } /** * Заготовка запроса активных пользователей. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('active', 1); } } %% **Использование локальной заготовки** Когда заготовка определена, вы можете вызывать методы заготовки при запросах к модели. Но теперь вам не нужно использовать префикс %%(t)scope%%. Вы можете даже сцеплять вызовы разных заготовок, например: %% $users = App\User::popular()->active()->orderBy('created_at')->get(); %% **Динамические заготовки** Иногда вам может потребоваться определить заготовку, которая принимает параметры. Для этого просто добавьте эти параметры в заготовку. Они должны быть определены после параметра %%$query%%: %% where('type', $type); } } %% А затем передайте их при вызове метода заготовки: %% $users = App\User::ofType('admin')->get(); %% %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) **Глобальные заготовки** Иногда вам требуется определить заготовку, которая будет применяться для всех выполняемых в модели запросов. По сути так и работает "мягкое удаление" в Eloquent. Глобальные заготовки определяются с помощью комбинации типажей PHP и реализации %%(t)Illuminate\Database\Eloquent\ScopeInterface%%. Сначала определим типаж. В этом примере мы будем использовать встроенный в Laravel %%SoftDeletes%%: ~%% trait SoftDeletes { /** * Загрузка типажа мягкого удаления для модели. * * @return void */ public static function bootSoftDeletes() { static::addGlobalScope(new SoftDeletingScope); } } ~%% Если в модели Eloquent используется типаж, содержащий соответствующий соглашению по названиям %%(t)bootNameOfTrait%% метод, тогда этот метод типажа будет вызываться при загрузке модели Eloquent. Это даёт вам возможность зарегистрировать глобальную заготовку, или сделать ещё что-либо необходимое. Заготовка должна реализовывать %%ScopeInterface%%, который содержит два метода: %%apply()%% и %%remove()%%. Метод %%apply()%% принимает объект конструктора запросов %%(t)Illuminate\Database\Eloquent\Builder%% и модель, к которой он применяется, и отвечает за добавление любых дополнительных операторов %%(t)where%%, которые необходимы заготовке. Метод %%remove()%% также принимает объект %%(t)Builder%% и модель, и отвечает за отмену действий, произведённых методом %%apply()%%. Другими словами, %%remove()%% должен удалить добавленные операторы %%(t)where%% (или любые другие). Поэтому для нашей %%SoftDeletingScope%% методы будут такими: ~%% /** * Применение заготовки к указанному конструктору запросов Eloquent. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $builder->whereNull($model->getQualifiedDeletedAtColumn()); $this->extend($builder); } /** * Удаление заготовки из указанного конструктора запросов Eloquent. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function remove(Builder $builder, Model $model) { $column = $model->getQualifiedDeletedAtColumn(); $query = $builder->getQuery(); foreach ((array) $query->wheres as $key => $where) { // Если оператор where ограничивает мягкое удаление данных, мы удалим его из // запроса и сбросим ключи в операторах where. Это позволит разработчику // включить удалённую модель в отношения результирующего набора, который загружается "лениво". if ($this->isSoftDeleteConstraint($where, $column)) { unset($query->wheres[$key]); $query->wheres = array_values($query->wheres); } } } ~%% %% == ((#отношения)) Отношения == .(tl_note) В документации Laravel 5.1+ данный раздел вынесен в отдельную статью - ((/docs/v5/eloquent-relationships Отношения)). - //прим. пер.// Конечно, ваши таблицы скорее всего как-то связаны с другими таблицами БД. Например, статья в блоге может иметь много комментариев, а заказ может быть связан с оставившим его пользователем. Eloquent упрощает работу и управление такими отношениями. Laravel поддерживает многие типы связей: 1. ((#oo Один к одному)) 2. ((#om Один ко многим)) 3. ((#mm Многие ко многим)) 4. ((#hmt Ко многим через)) 5. ((#pl Полиморфические связи)) 6. ((#mmp Полиморфические связи "многие ко многим")) === ((#oo)) Один к одному === **Создание связи "один к одному"** Связь вида "один к одному" является очень простой. К примеру, модель %%User%% может иметь один %%Phone%%. Мы можем определить такое отношение в Eloquent: %% class User extends Model { public function phone() { return $this->hasOne('App\Phone'); } } %% Первый параметр, передаваемый %%hasOne()%%, - имя связанной модели. Как только отношение установлено, вы можете получить к нему доступ через ((#динамические)) свойства Eloquent: %% $phone = User::find(1)->phone; %% Сгенерированный SQL имеет такой вид: %%(sql) select * from users where id = 1 select * from phones where user_id = 1 %% Заметьте, что Eloquent считает, что поле в таблице называется по имени модели плюс %%(t)_id%%. В данном случае предполагается, что это %%(t)user_id%%. Если вы хотите перекрыть стандартное имя, передайте второй параметр методу %%hasOne()%%. Кроме того вы можете передать в метод третий аргумент, чтобы указать, какие локальные столбцы следует использовать для объединения: %% return $this->hasOne('App\Phone', 'foreign_key'); return $this->hasOne('App\Phone', 'foreign_key', 'local_key'); %% **Создание обратного отношения** Для создания обратного отношения в модели %%Phone%% используйте метод %%belongsTo()%% ("принадлежит к"): %% class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } } %% В примере выше Eloquent будет искать поле %%(t)user_id%% в таблице %%(t)phones%%. Если вы хотите назвать внешний ключ по другому, передайте это имя вторым параметром в метод %%belongsTo()%%: %% class Phone extends Model { public function user() { return $this->belongsTo('App\User', 'local_key'); } } %% Кроме того, вы передаёте третий параметр, который определяет имя связанного столбца в родительской таблице: %% class Phone extends Model { public function user() { return $this->belongsTo('App\User', 'local_key', 'parent_key'); } } %% === ((#om)) Один ко многим === Примером отношения "один ко многим" является статья в блоге, которая имеет "много" комментариев. Вы можете смоделировать это отношение таким образом: %% class Post extends Model { public function comments() { return $this->hasMany('App\Comment'); } } %% Теперь мы можем получить все комментарии с помощью ((#динамическ+ие))ого свойства: %% $comments = Post::find(1)->comments; %% Если вам нужно добавить ограничения на получаемые комментарии, можно вызвать метод %%comments()%% и продолжить добавлять условия: %% $comments = Post::find(1)->comments()->where('title', '=', 'foo')->first(); %% И опять вы можете передать второй параметр в метод %%hasMany()%% для перекрытия стандартного имени ключа. И как и для отношения "hasOne" также может быть указан локальный столбец: %% return $this->hasMany('App\Comment', 'foreign_key'); return $this->hasMany('App\Comment', 'foreign_key', 'local_key'); %% **Определение обратного отношения** Для определения обратного отношения используйте метод %%belongsTo()%%: %% class Comment extends Model { public function post() { return $this->belongsTo('App\Post'); } } %% === ((#mm)) Многие ко многим === Отношения типа "многие ко многим" - более сложные, чем остальные виды ((#отношени+я))й. Примером может служить пользователь, имеющий много ролей, где роли также относятся ко многим пользователям. Например, один пользователь может иметь роль **admin**. Нужны три таблицы для этой связи: %%(t)users%%, %%(t)roles%% и %%(t)role_user%%. Название таблицы %%(t)role_user%% происходит от упорядоченных по алфавиту имён связанных моделей, она должна иметь поля %%(t)user_id%% и %%(t)role_id%%. Вы можете определить отношение "многие ко многим" через метод %%belongsToMany()%%: %% class User extends Model { public function roles() { return $this->belongsToMany('App\Role'); } } %% Теперь мы можем получить роли через модель %%User%%: %% $roles = User::find(1)->roles; %% Вы можете передать второй параметр к методу %%belongsToMany()%% с указанием имени связующей (//pivot//) таблицы вместо стандартной: %% return $this->belongsToMany('App\Role', 'user_roles'); %% Вы также можете перекрыть имена ключей по умолчанию: %% return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id'); %% Конечно, вы можете определить и обратное отношение на модели %%Role%%: %% class Role extends Model { public function users() { return $this->belongsToMany('App\User'); } } %% === ((#hmt)) Ко многим через === Связь "ко многим через" обеспечивает удобный короткий путь для доступа к удалённым отношениям через промежуточные. Например, модель %%(t)Country%% может иметь много %%(t)Post%% через модель %%(t)User%%. Таблицы для этих отношений будут выглядеть так: %%(conf) countries id - integer name - string users id - integer country_id - integer name - string posts id - integer user_id - integer title - string %% Несмотря на то, что таблица %%(t)posts%% не содержит столбца %%(t)country_id%%, отношение "hasManyThrough" позволит нам получить доступ к %%(t)posts%% через %%(t)country%% с помощью %%$country->posts%%. Давайте определим отношения: %% class Country extends Model { public function posts() { return $this->hasManyThrough('App\Post', 'App\User'); } } %% Если вы хотите указать ключи отношений вручную, вы можете передать их в качестве третьего и четвертого аргументов метода: %% class Country extends Model { public function posts() { return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id'); } } %% === ((#pl)) Полиморфические отношения === Полиморфические отношения позволяют модели быть связанной с более, чем одной моделью. Например, может быть модель %%Photo%%, содержащая записи, принадлежащие к моделям %%Staff%% (сотрудники) и %%Order%%. Мы можем создать такое отношение таким образом: %% class Photo extends Model { public function imageable() { return $this->morphTo(); } } class Staff extends Model { public function photos() { return $this->morphMany('App\Photo', 'imageable'); } } class Order extends Model { public function photos() { return $this->morphMany('App\Photo', 'imageable'); } } %% **Чтение полиморфической связи** Теперь мы можем получить фотографии и для сотрудника, и для заказа: %% $staff = Staff::find(1); foreach ($staff->photos as $photo) { // } %% Однако настоящая "магия" полиморфизма происходит при чтении связи на модели %%Photo%%: %% $photo = Photo::find(1); $imageable = $photo->imageable; %% Отношение %%imageable%% модели %%Photo%% вернёт либо объект %%Staff%%, либо объект %%Order%% в зависимости от типа модели, которой принадлежит фотография. **Структура таблиц полиморфической связи** Чтобы понять, как это работает, давайте изучим структуру БД для полиморфического отношения: %%(conf) staff id - integer name - string orders id - integer price - integer photos id - integer path - string imageable_id - integer imageable_type - string %% Главные поля, на которые нужно обратить внимание: **imageable_id** и **imageable_type** в таблице %%(t)photos%%. Первое содержит ID владельца, в нашем случае - заказа или персонала, а второе - имя класса-модели владельца. Это позволяет ORM определить, какой класс модели должен быть возвращён при использовании отношения %%imageable%%. === ((#mmp)) Полиморфические связи многие ко многим === **Структура таблиц полиморфической связи многие ко многим** В дополнение к традиционным полиморфическим связям вы можете также задать полиморфические связи многие ко многим. Например, модели блогов %%(t)Post%% и %%(t)Video%% могут разделять полиморфическую связь с моделью %%(t)Tag%%. Во-первых, давайте рассмотрим структуру таблиц: %%(conf) posts id - integer name - string videos id - integer name - string tags id - integer name - string taggables tag_id - integer taggable_id - integer taggable_type - string %% Далее, мы готовы к установке связи с моделью. Обе модели %%(t)Post%% и %%(t)Video%% будут иметь связь "morphToMany" через метод %%tags%%: %% class Post extends Model { public function tags() { return $this->morphToMany('App\Tag', 'taggable'); } } %% Модель %%(t)Tag%% может определить метод для каждого из своих отношений: %% class Tag extends Model { public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } public function videos() { return $this->morphedByMany('App\Video', 'taggable'); } } %% === Запросы к отношениям === **Проверка связей при ((docs/v5/queries#выборк+а))е** При чтении отношений модели вам может быть нужно ограничить результаты в зависимости от существования связи. Например, вы хотите получить все статьи в блоге, имеющие хотя бы один комментарий. Для этого можно использовать метод %%has()%%: %% $posts = Post::has('comments')->get(); %% Вы также можете указать оператор и число: %% $posts = Post::has('comments', '>=', 3)->get(); %% Можно конструировать вложенные операторы %%has%% с помощью точечной нотации: %% $posts = Post::has('comments.votes')->get(); %% Если вам нужно ещё больше возможностей, вы можете использовать методы %%whereHas%% и %%orWhereHas%%, чтобы поместить условия %%(t)"where"%% в ваши запросы %%(t)has%%: %% $posts = Post::whereHas('comments', function($q) { $q->where('content', 'like', 'foo%'); })->get(); %% === Динамические свойства === Eloquent позволяет вам читать ((#отношения)) через динамические свойства. Eloquent автоматически определит используемую связь и даже вызовет %%get()%% для связей "один ко многим" и %%first()%% - для связей "один к одному". Эта связь будет доступна через динамическое свойство с тем же именем. К примеру, для следующей модели %%$phone%%: %% class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } } $phone = Phone::find(1); %% Вместо того, чтобы получить e-mail пользователя так: %% echo $phone->user()->first()->email; %% ...вызов может быть сокращён до такого: %% echo $phone->user->email; %% .(alert) **Внимание:** Отношения, которые возвращают много результатов, вернут экземпляр класса %%Illuminate\Database\Eloquent\Collection%%. === Активная загрузка === Активная загрузка (//eager loading//) призвана устранить проблему запросов //N + 1//. Например, представьте, что у нас есть модель %%Book%% со связью к модели %%Author%%. Отношение определено как: %% class Book extends Model { public function author() { return $this->belongsTo('App\Author'); } } %% Теперь предположим, у нас есть такой код: %% foreach (Book::all() as $book) { echo $book->author->name; } %% Цикл выполнит один запрос для получения всех книг в таблице, а затем будет выполнять по одному запросу на каждую книгу для получения автора. Таким образом, если у нас 25 книг, то потребуется 26 запросов. К счастью, мы можем использовать активную загрузку для кардинального уменьшения числа запросов. Отношение будет активно загружено, если оно было указано при вызове метода %%with()%%: %% foreach (Book::with('author')->get() as $book) { echo $book->author->name; } %% В цикле выше будут выполнены всего два запроса: %%(sql) select * from books select * from authors where id in (1, 2, 3, 4, 5, ...) %% Разумное использование активной загрузки поможет сильно повысить производительность вашего приложения. Конечно, вы можете загрузить несколько отношений одновременно: %% $books = Book::with('author', 'publisher')->get(); %% Вы даже можете загрузить вложенные отношения: %% $books = Book::with('author.contacts')->get(); %% В примере выше связь %%author%% будет активно загружена вместе со связью %%contacts%% модели автора. === Ограничения активной загрузки === Иногда вам может быть нужно не только ((#активн+ая))о загрузить отношение, но также указать условие для его загрузки: %% $users = User::with(['posts' => function($query) { $query->where('title', 'like', '%первое%'); }])->get(); %% В этом примере мы загружаем сообщения пользователя, но только те, заголовок которых содержит подстроку "первое". Конечно, функции-замыкания активной загрузки не ограничиваются только условиями. Вы также можете применить упорядочивание: %% $users = User::with(['posts' => function($query) { $query->orderBy('created_at', 'desc'); }])->get(); %% === Ленивая активная загрузка === Можно активно загрузить связанные модели напрямую из уже созданного набора объектов моделей. Это может быть полезно при определении во время выполнения, требуется ли такая загрузка или нет, или в комбинации с кэшированием. %% $books = Book::all(); $books->load('author', 'publisher'); %% Вы можете передать замыкание, чтобы задать ограничения для запроса: %% $books->load(['author' => function($query) { $query->orderBy('published_date', 'asc'); }]); %% === Вставка связанных моделей === **Создание связанной модели** Часто вам нужно будет добавить связанную модель. Например, вы можете создать новый комментарий к сообщению. Вместо явного указания значения для поля %%(t)post_id%% вы можете вставить модель напрямую через её родителя - модели %%Post%%: %% $comment = new Comment(['message' => 'A new comment.']); $post = Post::find(1); $comment = $post->comments()->save($comment); %% В этом примере поле %%(t)post_id%% вставленного комментария автоматически получит значение ID своей статьи. Сохранить несколько связанных моделей можно так: %% $comments = [ new Comment(['message' => 'A new comment.']), new Comment(['message' => 'Another comment.']), new Comment(['message' => 'The latest comment.']) ]; $post = Post::find(1); $post->comments()->saveMany($comments); %% === Связывание моделей (//belongs to//) === При обновлении связей %%belongsTo%% ("принадлежит к") вы можете использовать метод %%associate()%%. Он установит внешний ключ на дочерней модели: %% $account = Account::find(10); $user->account()->associate($account); $user->save(); %% === Вставка связанных моделей (//многие ко многим//) === Вы также можете вставлять связанные модели при работе с ((#отношения+))ми ((#mm многие ко многим)). Продолжим использовать наши модели %%User%% и %%Role%% в качестве примеров. Вы можете легко привязать новые роли к пользователю методом %%attach()%%. **Связывание моделей "многие ко многим"** %% $user = User::find(1); $user->roles()->attach(1); %% Вы также можете передать массив атрибутов, которые должны быть сохранены в связующей (//pivot//) таблице для этого отношения: %% $user->roles()->attach(1, ['expires' => $expires]); %% Конечно, существует противоположность %%attach()%% - %%detach()%%: %% $user->roles()->detach(1); %% Оба метода %%attach()%% и %%detach()%% также принимают в качестве параметров массивы ID: %% $user = User::find(1); $user->roles()->detach([1, 2, 3]); $user->roles()->attach([1 => ['attribute1' => 'value1'], 2, 3]); %% **Использование %%sync()%% для привязки моделей "многие ко многим"** Вы также можете использовать метод %%sync()%% для привязки связанных моделей. Этот метод принимает массив ID, которые должны быть сохранены в связующей таблице. Когда операция завершится, только переданные ID будут существовать в промежуточной таблице для данной модели: %% $user->roles()->sync([1, 2, 3]); %% **Добавление данных для связующей таблицы при синхронизации** Вы также можете связать другие связующие таблицы с нужными ID: %% $user->roles()->sync([1 => ['expires' => true]]); %% Иногда вам может быть нужно создать новую связанную модель и добавить её одной командой. Для этого вы можете использовать метод %%save()%%: %% $role = new Role(['name' => 'Editor']); User::find(1)->roles()->save($role); %% В этом примере новая модель %%Role%% будет сохранена и привязана к модели %%User%%. Вы можете также передать массив атрибутов для помещения в связующую таблицу: %% User::find(1)->roles()->save($role, ['expires' => $expires]); %% === Обновление времени владельца === Когда модель принадлежит другой посредством %%belongsTo()%% - например, %%Comment%%, принадлежащий %%Post%% - иногда нужно обновить время изменения владельца при обновлении связанной модели. Например, при изменении модели %%Comment%% вы можете обновлять поле %%(t)updated_at%% её модели %%Post%%. Eloquent делает этот процесс простым - просто добавьте свойство %%$touches%%, содержащее имена всех отношений с моделями-потомками: %% class Comment extends Model { protected $touches = ['post']; public function post() { return $this->belongsTo('App\Post'); } } %% Теперь при обновлении %%Comment%% владелец %%Post%% также обновит своё поле %%(t)updated_at%%: %% $comment = Comment::find(1); $comment->text = 'Изменение этого комментария!'; $comment->save(); %% === Работа со связующими таблицами === Как вы уже узнали, работа ((#отношения)) ((#mm многие ко многим)) требует наличия промежуточной таблицы. Например, предположим, что наш объект %%User%% имеет множество связанных объектов %%Role%%. После чтения отношения мы можем прочитать таблицу %%pivot%% на обеих моделях: %% $user = User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } %% Заметьте, что каждая модель %%Role%% автоматически получила атрибут %%pivot%%. Этот атрибут содержит модель, представляющую промежуточную таблицу, и она может быть использована как любая другая модель Eloquent. По умолчанию, только ключи будут представлены в объекте %%pivot%%. Если ваша связующая таблица содержит другие поля, вы можете указать их при создании отношения: %% return $this->belongsToMany('App\Role')->withPivot('foo', 'bar'); %% Теперь атрибуты %%(t)foo%% и %%(t)bar%% будут также доступны на объекте %%pivot%% модели %%Role%%. Если вы хотите автоматически поддерживать поля %%(t)created_at%% и %%(t)updated_at%% актуальными, используйте метод %%withTimestamps()%% при создании отношения: %% return $this->belongsToMany('App\Role')->withTimestamps(); %% **Удаление всех связующих записей** Для удаления всех записей в связующей таблице можно использовать метод %%detach()%%: %% User::find(1)->roles()->detach(); %% Заметьте, что эта операция не удаляет записи из таблицы %%(t)roles%%, а только из связующей таблицы. **Обновление записи в связующей таблице** Иногда необходимо обновить связующую таблицу не отвязывая её. Для обновления вашей связующей таблицы на месте используйте метод %%updateExistingPivot()%%: %% User::find(1)->roles()->updateExistingPivot($roleId, $attributes); %% **Определение собственной связующей модели** Laravel также позволяет определять собственную связующую модель. Для этого сначала создайте свой класс "основной" модели, который наследует %%Eloquent%%. В остальных ваших моделях Eloquent наследуйте эту базовую модель вместо базового %%Eloquent%% по умолчанию. В вашу базовую модель добавьте следующую функцию, которая возвращает экземпляр вашей собственной связующей модели: %% public function newPivot(Model $parent, array $attributes, $table, $exists) { return new YourCustomPivot($parent, $attributes, $table, $exists); } %% == Коллекции == .(tl_note) В документации Laravel 5.1+ данный раздел вынесен в отдельную статью - ((/docs/v5/eloquent-collections Коллекции)). - //прим. пер.// Все методы Eloquent, возвращающие набор моделей - либо через %%get()%%, либо через отношения - возвращают объект-коллекцию. Этот объект реализует стандартный интерфейс PHP %%IteratorAggregate%%, что позволяет ему быть использованным в циклах как массив. Однако этот объект также имеет набор других полезных методов для работы с результатом запроса. **Проверка на существование ключа в коллекции** Например, мы можем выяснить, содержит ли результат запись с определённым первичным ключом, методом %%contains()%%: %% $roles = User::find(1)->roles; if ($roles->contains(2)) { // } %% Коллекции также могут быть преобразованы в массив или строку ((ВП:JSON)): %% $roles = User::find(1)->roles->toArray(); $roles = User::find(1)->roles->toJson(); %% Если коллекция преобразуется в строку, результатом будет JSON-выражение: %% $roles = (string) User::find(1)->roles; %% **Проход по элементам коллекции** Коллекции Eloquent имеют несколько полезных методов для прохода и фильтрации содержащихся в них элементов: %% $roles = $user->roles->each(function($role) { // }); %% **Фильтрация элементов коллекции** При фильтрации коллекций передаваемая функция будет использована как функция обратного вызова для ((php:array-filter array_filter)). %% $users = $users->filter(function($user) { return $user->isAdmin(); }); %% .(alert) **Внимание:** При фильтрации коллекций и конвертации их в JSON попробуйте сначала вызвать функцию %%values%% для сброса ключей массива. **Применение функции обратного вызова к каждому объекту коллекции** %% $roles = User::find(1)->roles; $roles->each(function($role) { // }); %% **Сортировка коллекции по значению** %% $roles = $roles->sortBy(function($role) { return $role->created_at; }); $roles = $roles->sortByDesc(function($role) { return $role->created_at; }); %% **Сортировка коллекции по значению** %% $roles = $roles->sortBy('created_at'); $roles = $roles->sortByDesc('created_at'); %% **Использование произвольного класса коллекции** Иногда вам может быть нужно получить собственный объект %%Collection%% со своими методами. Вы можете указать его при определении модели Eloquent, перекрыв метод %%newCollection()%%: %% class User extends Model { public function newCollection(array $models = []) { return new CustomCollection($models); } } %% == Читатели и преобразователи == .(tl_note) В документации Laravel 5.1+ данный раздел вынесен в отдельную статью - ((/docs/v5/eloquent-mutators Преобразователи)). - //прим. пер.// **Объявление читателя** Eloquent содержит мощный механизм для преобразования атрибутов модели при их чтении и записи. Просто объявите в её классе метод %%getFooAttribute()%%. Помните, что имя метода должно следовать соглашению //camelCase//, даже если поля таблицы используют соглашение //snake-case// (!!(tl_note)он же - "стиль Си", с подчёркиваниями - //прим. пер.//!!): %% class User extends Model { public function getFirstNameAttribute($value) { return ucfirst($value); } } %% В примере выше поле %%(t)first_name%% теперь имеет читателя (//accessor//). Заметьте, что оригинальное значение атрибута передаётся методу в виде параметра. **Объявление преобразователя** Преобразователи (//mutators//) объявляются подобным образом: %% class User extends Model { public function setFirstNameAttribute($value) { $this->attributes['first_name'] = strtolower($value); } } %% === Преобразователи дат === По умолчанию Eloquent преобразует поля %%(t)created_at%% и %%(t)updated_at%% в объекты ((https://github.com/briannesbitt/Carbon Carbon)), которые предоставляют множество полезных методов, расширяя стандартный класс PHP ((phpdoc:book.datetime DateTime)). Вы можете указать, какие поля будут автоматически преобразованы, и даже полностью отключить преобразование, перекрыв метод %%getDates()%% класса модели: %% public function getDates() { return ['created_at']; } %% Когда поле является датой, вы можете установить его в число-оттиск времени формата Unix (//timestamp//), строку даты в формате %%(t)Y-m-d%%, строку даты-времени и, конечно, экземпляр объекта %%DateTime%% или %%Carbon%%. Чтобы полностью отключить преобразование дат, просто верните пустой массив из метода %%getDates()%%: %% public function getDates() { return []; } %% === Изменение атрибутов === Если у вас есть несколько атрибутов, которые вы хотите всегда конвертировать в другой формат данных, вы можете добавить атрибут в свойство %%casts%% вашей модели. Иначе вам нужно будет определять преобразователь для каждого из атрибутов, а это может отнять много времени. %% /** * Атрибуты, которые нужно преобразовать в нативный тип. * * @var array */ protected $casts = [ 'is_admin' => 'boolean', ]; %% Теперь при каждом обращении атрибут %%is_admin%% будет преобразовываться в %%boolean%%, даже если базовое значение сохранено в базе данных как %%integer%%. Другие поддерживаемые типы для преобразования: %%integer%%, %%real%%, %%float%%, %%double%%, %%string%%, %%boolean%%, %%object%% и %%array%%. Преобразование типа %%array%% особенно полезно для работы с полями, которые сохранены как сериализованный JSON. Например, если у вашей базы данных есть поле типа TEXT, которое содержит сериализованный JSON, добавление преобразования в тип %%array%% к атрибуту автоматически десериализует атрибут в массив PHP при обращении к нему через модель Eloquent: %% /** * Атрибуты, которые нужно преобразовать в нативный тип. * * @var array */ protected $casts = [ 'options' => 'array', ]; %% Теперь, когда вы используете модель Eloquent: %% $user = User::find(1); // $options - массив... $options = $user->options; // options автоматически сериализуются обратно в JSON... $user->options = ['foo' => 'bar']; %% == События моделей == Модели Eloquent инициируют несколько событий, что позволяет вам добавить к ним свои обработчики с помощью следующих методов: %%creating()%%, %%created()%%, %%updating()%%, %%updated()%%, %%saving()%%, %%saved()%%, %%deleting()%%, %%deleted()%%, %%restoring()%%, %%restored()%%. События позволяют вам легко выполнять код при каждом сохранении или изменении класса конкретной модели в БД. Когда новая модель сохраняется первый раз, возникают события **creating** и **created**. Если модель уже существовала на момент вызова метода %%save()%%, вызываются события **updating** и **updated**. В обоих случаях также возникнут события **saving** и **saved**. Например, давайте определим слушателя событий Eloquent в ((/docs/v5/providers сервис-провайдере)). В нашем слушателе событий мы будем вызывать метод %%isValid()%% для данной модели, и возвращать %%false%%, если она не прошла проверку. Возврат %%false%% из слушателя событий Eloquent отменит операции **save**/**update**: %% isValid(); }); } /** * Регистрация сервис-провайдера. * * @return void */ public function register() { // } } %% %%(DOCNEW 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) **Отмена сохранения модели через события** Если обработчики %%creating%%, %%updating%%, %%saving%% или %%deleting%% вернут значение %%false%%, то действие будет отменено: ~%% User::creating(function($user) { if ( ! $user->isValid()) return false; }); ~%% %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) == Наблюдатели моделей == Если вы прослушиваете много событий для определённой модели, вы можете использовать наблюдателей (//observer//) для объединения всех слушателей в единый класс. В классах наблюдателей названия методов отражают те события Eloquent, которые вы хотите прослушивать. Каждый такой метод получает модель в качестве единственного аргумента. В Laravel нет стандартного каталога для наблюдателей, поэтому вы можете создать любой каталог для хранения классов ваших наблюдателей: ~%% id%% будет подставлено вместо строки-переменной %%{user}%% в сгенерированный URL. Но если вы хотите использовать другое свойств вместо ID, переопределите метод %%getRouteKey()%% в своей модели: ~%% public function getRouteKey() { return $this->slug; } ~%% %% == Преобразование в массивы и JSON == .(tl_note) В документации Laravel 5.1+ данный раздел вынесен в отдельную статью - ((/docs/v5/eloquent-serialization Сериализация)). - //прим. пер.// **Преобразование модели в массив** При создании JSON API вам часто потребуется преобразовывать модели и отношения к массивам или выражениям JSON. Eloquent содержит методы для выполнения этих задач. Для преобразования модели или загруженного ((#отношения)) в массив можно использовать метод %%toArray()%%: %% $user = User::with('roles')->first(); return $user->toArray(); %% Заметьте, что целая коллекция моделей также может быть преобразована в массив: %% return User::all()->toArray(); %% **Преобразование модели к JSON** Для преобразования модели к JSON вы можете использовать метод %%toJson()%%: %% return User::find(1)->toJson(); %% **Возврат модели из маршрута** Обратите внимание, что если модель преобразуется к строке, результатом также будет JSON, - это значит, что вы можете возвращать объекты Eloquent напрямую из ваших ((docs/v5/routing маршрутов))! %% Route::get('users', function() { return User::all(); }); %% **Скрытие атрибутов при преобразовании в массив или JSON** Иногда вам может быть нужно ограничить список атрибутов, включённых в преобразованный массив или JSON-строку - например, скрыть пароли. Для этого определите в классе модели свойство %%hidden%%: %% class User extends Model { protected $hidden = ['password']; } %% .(alert) **Внимание:** При скрытии отношений используйте имя %%method%% отношения, а не имя для динамического доступа. Вы также можете использовать атрибут %%$visible%% для указания разрешённых полей: %% protected $visible = ['first_name', 'last_name']; %% Иногда вам может быть нужно добавить поле, которое не существует в таблице. Для этого просто определите для него ((#читател+и))я: %% public function getIsAdminAttribute() { return $this->attributes['admin'] == 'yes'; } %% Когда вы создали читателя, просто добавьте значение к свойству-массиву %%appends%% класса модели: %% protected $appends = ['is_admin']; %% Как только атрибут был добавлен к списку %%appends%%, он будет включён в массивы и выражения JSON, образованные от этой модели. Атрибуты в массиве %%appends%% соответствуют настройкам модели %%visible%% и %%hidden%%.