{{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}} == Введение == Ваши таблицы скорее всего как-то связаны с другими таблицами БД. Например, статья в блоге может иметь много комментариев, а заказ может быть связан с оставившим его пользователем. Eloquent упрощает работу и управление такими отношениями. Laravel поддерживает многие типы связей: 1. ((#oo Один к одному)) 2. ((#om Один ко многим)) 3. ((#mm Многие ко многим)) 4. ((#hmt Ко многим через)) 5. ((#pl Полиморфные связи)) 6. ((#mmp Полиморфные связи "многие ко многим")) == Определение отношений == Eloquent отношения определены как функции в ваших классах модели Eloquent. Как и сами модели Eloquent, отношения являются мощными ((/docs/v5/queries конструкторами запросов)), которые определяют отношения как функции, и обеспечивают мощную сцепку методов и возможности для запросов. Например, мы можем прицепить дополнительные ограничения к отношению этих %%(t)posts%%: %% $user->posts()->where('active', 1)->get(); %% Но прежде чем погрузиться в использование отношений, давайте узнаем, как определяется каждый тип отношений. === ((#oo)) Один к одному === Связь вида "один к одному" является очень простой. К примеру, модель %%(t)User%% может иметь один %%(t)Phone%%. Чтобы определить такое отношение, мы помещаем метод %%phone()%% в модель %%(t)User%%. Метод %%phone()%% должен вызвать метод %%hasOne()%% и вернуть его результат: %% hasOne('App\Phone'); } } %% Первый параметр, передаваемый %%hasOne()%%, - имя связанной модели. Как только отношение установлено, вы можете получить к нему доступ через динамические свойства Eloquent. Динамические свойства позволяют вам получить доступ к функциям отношений, если бы они были свойствами модели: %% $phone = User::find(1)->phone; %% Eloquent определяет внешний ключ отношения по имени модели. В данном случае предполагается, что это %%(t)user_id%%. Если вы хотите перекрыть стандартное имя, передайте второй параметр методу %%hasOne()%%: %% return $this->hasOne('App\Phone', 'foreign_key'); %% Также Eloquent подразумевает, что внешний ключ должен иметь значение, привязанное к родительскому столбцу %%(t)id%% (или другому %%(t)$primaryKey%%). Другими словами, Eloquent будет искать значение столбца %%(t)id%% пользователя в столбце %%(t)user_id%% записи %%(t)Phone%%. Кроме того вы можете передать в метод третий аргумент, чтобы указать свой столбец для объединения: %% return $this->hasOne('App\Phone', 'foreign_key', 'local_key'); %% **Создание обратного отношения** Итак, у нас есть доступ к модели %%(t)Phone%% из нашего %%(t)User%%. Теперь давайте определим отношение для модели %%(t)Phone%%, которое будет иметь доступ к %%(t)User%%, владеющего этим телефоном. Для создания обратного отношения в модели %%(t)Phone%% используйте метод %%belongsTo()%%: %% belongsTo('App\User'); } } %% В примере выше Eloquent будет искать связь между %%(t)user_id%% в модели %%(t)Phone%% и %%(t)id%% в модели %%(t)User%%. По умолчанию Eloquent определяет имя внешнего ключа по имени метода отношения, добавляя суффикс %%(t)_id%%. Однако, если имя внешнего ключа модели %%(t)Phone%% не %%(t)user_id%%, передайте это имя вторым параметром в метод %%belongsTo()%%: %% /** * Получить пользователя, владеющего данным телефоном. */ public function user() { return $this->belongsTo('App\User', 'foreign_key'); } %% Если ваша родительская модель не использует %%(t)id%% в качестве первичного ключа, или вам бы хотелось присоединить дочернюю модель к другому столбцу, вы можете передать третий параметр в метод %%belongsTo()%%, который определяет имя связанного столбца в родительской таблице: %% /** * Получить пользователя, владеющего данным телефоном. */ public function user() { return $this->belongsTo('App\User', 'foreign_key', 'other_key'); } %% === ((#om)) Один ко многим === Отношение "один ко многим" используется для определения отношений, где одна модель владеет некоторым количеством других моделей. Примером отношения "один ко многим" является статья в блоге, которая имеет "много" комментариев. Как и другие отношения Eloquent вы можете смоделировать это отношение таким образом: %% hasMany('App\Comment'); } } %% Помните, что Eloquent автоматически определяет столбец внешнего ключа в модели %%(t)Comment%%. По соглашению, Eloquent возьмёт "snake case" названия владеющей модели плюс %%(t)_id%%. Таким образом, для данного примера, Eloquent предполагает, что внешним ключом для модели %%(t)Comment%% будет %%(t)post_id%%. После определения отношения мы можем получить доступ к коллекции комментариев, обратившись к свойству %%(t)comments%%. Помните, что поскольку Eloquent поддерживает "динамические свойства", мы можем обращаться к функциям отношений, как если бы они были определены свойством модели: %% $comments = App\Post::find(1)->comments; foreach ($comments as $comment) { // } %% Конечно, так как отношения служат и в качестве конструкторов запросов, вы можете добавлять дополнительные условия к тем комментариям, которые получены вызовом метода %%comments()%%: %% $comments = App\Post::find(1)->comments()->where('title', 'foo')->first(); %% Как и для метода %%hasOne()%% вы можете указать внешний и локальный ключи, передав дополнительные параметры в метод %%hasMany()%%: %% return $this->hasMany('App\Comment', 'foreign_key'); return $this->hasMany('App\Comment', 'foreign_key', 'local_key'); %% === Один ко многим (Обратное отношение) === После получения доступа ко всем комментариям статьи давайте определим отношение, которое позволит комментарию получить доступ к его статье. Чтобы определить обратное отношение %%hasMany()%%, определим функцию отношения на дочерней модели, которая вызывает метод %%belongsTo()%%: %% belongsTo('App\Post'); } } %% После определения отношений мы можем получить модель %%(t)Post%% для %%(t)Comment%%, обратившись к динамическому свойству %%(t)post%%: %% $comment = App\Comment::find(1); echo $comment->post->title; %% В примере выше Eloquent пробует связать %%(t)post_id%% из модели %%(t)Comment%% с %%(t)id%% модели %%(t)Post%%. По умолчанию Eloquent определяет внешний ключ по имени метода отношения плюс %%(t)_id%%. Однако, если внешний ключ для модели %%(t)Comment%% не %%(t)post_id%%, вы можете передать своё имя вторым параметром в метод %%belongsTo()%%: %% /** * Получить статью данного комментария. */ public function post() { return $this->belongsTo('App\Post', 'foreign_key'); } %% Если ваша родительская модель не использует %%(t)id%% в качестве первичного ключа, или вам бы хотелось присоединить дочернюю модель к другому столбцу, вы можете передать третий параметр в метод %%belongsTo()%%, который определяет имя связанного столбца в родительской таблице: %% /** * Получить статью данного комментария. */ public function post() { return $this->belongsTo('App\Post', 'foreign_key', 'other_key'); } %% === ((#mm)) Многие ко многим === Отношения типа "многие ко многим" сложнее отношений %%hasOne()%% и %%hasMany()%%. Примером может служить пользователь, имеющий много ролей, где роли также относятся ко многим пользователям. Например, несколько пользователей могут иметь роль "Admin". Нужны три таблицы для этой связи: %%(t)users%%, %%(t)roles%% и %%(t)role_user%%. Имя таблицы %%(t)role_user%% получается из упорядоченных по алфавиту имён связанных моделей, она должна иметь поля %%(t)user_id%% и %%(t)role_id%%. Вы можете определить отношение "многие ко многим", написав метод, возвращающий результат метода %%belongsToMany()%%. Давайте определим метод %%roles()%% для модели %%(t)User%%: %% belongsToMany('App\Role'); } } %% Теперь мы можем получить роли пользователя через динамическое свойство %%(t)roles%%: %% $user = App\User::find(1); foreach ($user->roles as $role) { // } %% Естественно, как и для других типов отношений, вы можете вызвать метод %%roles()%%, продолжив конструировать запрос для отношения: %% $roles = App\User::find(1)->roles()->orderBy('name')->get(); %% Как уже упоминалось ранее, чтобы определить имя для таблицы присоединения отношений, Eloquent соединит два названия взаимосвязанных моделей в алфавитном порядке. Тем не менее, вы можете переопределить имя, передав второй параметр методу %%belongsToMany()%%: %% return $this->belongsToMany('App\Role', 'role_user'); %% В дополнение к заданию имени соединительной таблицы, вы можете также задать имена столбцов ключей в таблице, передав дополнительные параметры методу %%belongsToMany()%%. Третий аргумент - это имя внешнего ключа модели, на которой вы определяете отношения, в то время как четвертый аргумент - это внешний ключ модели, с которой вы собираетесь связаться: %% return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id'); %% Чтобы определить обратное отношение "многие-ко-многим", просто поместите другой вызов %%belongsToMany()%% на вашу модель. Чтобы продолжить пример с ролями пользователя, давайте определим метод %%users()%% для модели %%(t)Role%%: %% belongsToMany('App\User'); } } %% Как вы можете видеть, соотношение определяется точно так же, как и его для %%(t)User%%, за исключением ссылки %%(t)App\User%%. Так как мы повторно используем метод %%belongsToMany()%%, все обычные таблицы и параметры настройки ключей доступны при определении обратного отношения многих-ко-многим. **Получение промежуточных столбцов таблицы** Как вы уже запомнили, работа с отношением "многие-ко-многим" требует наличия промежуточной таблицы. Eloquent предоставляет некоторые очень полезные способы взаимодействия с такой таблицей. Например, давайте предположим, что наш объект %%(t)User%% имеет много связанных с ним объектов %%(t)Role%%. После получения доступа к этому отношению мы можем получить доступ к промежуточной таблице с помощью %%(t)pivot%% атрибута модели: %% $user = App\User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } %% Обратите внимание на то, что каждой полученной модели %%(t)Role%% автоматически присваивается атрибут %%(t)pivot%%. Этот атрибут содержит модель, представляющую промежуточную таблицу, и может быть использован, как и любая другая модель Eloquent. По умолчанию, только ключи модели будут представлять %%(t)pivot%% объект. Если ваша "pivot" таблица содержит дополнительные атрибуты, вам необходимо указать их при определении отношения: %% return $this->belongsToMany('App\Role')->withPivot('column1', 'column2'); %% Если вы хотите, чтобы ваша "pivot" таблица автоматически поддерживала временные метки %%(t)created_at%% и %%(t)updated_at%%, используйте метод %%withTimestamps()%% при определении отношений: %% return $this->belongsToMany('App\Role')->withTimestamps(); %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) **Фильтрация отношений через столбцы промежуточной таблицы** Вы также можете отфильтровать результаты, возвращённые методом %%belongsToMany()%%, с помощью методов %%wherePivot()%% и %%wherePivotIn()%% при определении отношения ~%% return $this->belongsToMany('App\Role')->wherePivot('approved', 1); return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]); ~%% %% === ((#hmt)) Ко многим через === Связь "ко многим через" обеспечивает удобный короткий путь для доступа к удалённым отношениям через промежуточные. Например, модель %%(t)Country%% может иметь много %%(t)Post%% через модель %%(t)User%%. В данном примере вы можете просто собрать все статьи для заданной %%(t)country%%. Таблицы для этих отношений будут выглядеть так: %%(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%%, отношение "ко многим через" позволит нам получить доступ к %%(t)posts%% через %%(t)country%% с помощью %%$country->posts%%. Для выполнения этого запроса Eloquent ищет %%(t)country_id%% в промежуточной таблице %%(t)users%%. После нахождения совпадающих ID пользователей они используются в запросе к таблице %%(t)posts%%. Теперь, когда мы рассмотрели структуру таблицы для отношений, давайте определим отношения для модели %%(t)Country%%: %% hasManyThrough('App\Post', 'App\User'); } } %% Первый параметр, переданный в метод %%hasManyThrough()%% является именем конечной модели, которую мы получаем, а второй параметр - это имя промежуточной модели. Обычные соглашения для внешнего ключа Eloquent будут использоваться при выполнении запросов отношения. Если вы хотите настроить ключи отношения, вы можете передать их третьим и четвертым параметрами методу %%hasManyThrough()%%. Третий параметр - имя внешнего ключа для промежуточной модели, четвертый параметр - имя внешнего ключа для конечной модели, а пятый аргумент (для версии 5.2 и выше) - локальный ключ: %% class Country extends Model { public function posts() { return $this->hasManyThrough( 'App\Post', 'App\User', 'country_id', 'user_id', 'id' ); //для версии 5.1: //return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id'); } } %% === ((#pl)) Полиморфные отношения === **Структура таблицы** Полиморфные отношения позволяют модели быть связанной с более чем одной моделью. Например, предположим, пользователи вашего приложения могут "комментировать" и статьи, и видео. Используя полиморфные отношения, вы можете использовать единственную таблицу %%(t)comments%% для обоих этих сценариев. Во-первых, давайте посмотрим на структуру таблицы, необходимую для таких отношений: %%(conf) posts id - integer title - string body - text videos id - integer title - string url - string comments id - integer body - text commentable_id - integer commentable_type - string %% Главные поля, на которые нужно обратить внимание: %%(t)commentable_id%% и %%(t)commentable_type%% в таблице %%(t)comments%%. Первое содержит ID статьи или видео, а второе - имя класса-модели владельца. Это позволяет ORM определить, какой класс модели должен быть возвращён при использовании отношения %%(t)commentable%%. **Структура модели** Теперь давайте рассмотрим, какие определения для модели нам нужны, чтобы построить её отношения: %% morphTo(); } } class Post extends Model { /** * Получить все комментарии статьи. */ public function comments() { return $this->morphMany('App\Comment', 'commentable'); } } class Video extends Model { /** * Получить все комментарии видео. */ public function comments() { return $this->morphMany('App\Comment', 'commentable'); } } %% **Чтение полиморфного отношения** После определения моделей и таблиц вы можете получить доступ к отношениям через модели. Например, чтобы получить все комментарии статьи, просто используйте динамической свойство %%(t)comments%%: %% $post = App\Post::find(1); foreach ($post->comments as $comment) { // } %% Вы можете также получить владельца полиморфного отношения от полиморфной модели, получив доступ к имени метода, который вызывает %%morphTo()%%. В нашем случае это метод %%commentable()%% для модели %%(t)Comment%%. Так мы получим доступ к этому методу как к динамическому свойству: %% $comment = App\Comment::find(1); $commentable = $comment->commentable; %% Отношение %%commentable()%% модели %%(t)Comment%% вернёт либо объект %%(t)Post%%, либо объект %%(t)Video%% в зависимости от типа модели, которой принадлежит комментарий. **Пользовательские полиморфные типы** По умолчанию Laravel будет использовать полностью определённое имя класса для хранения типа связанной модели. Например, учитывая пример выше, где %%(t)Comment%% может принадлежать %%(t)Post%% или %%(t)Video%%, значение по умолчанию для %%(t)commentable_type%% было бы или %%(t)App\Post%% или %%(t)App\Video%%, соответственно. Однако вы можете захотеть отделить свою базу данных от внутренней структуры вашего приложения. В этом случае вы можете определить отношение %%morph map()%%, чтобы дать команду Eloquent использовать заданное имя для каждой модели вместо имени класса: %% use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ 'posts' => 'App\Post', 'videos' => 'App\Video', ]); %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01) Или вы можете определить свою строку для ассоциации с моделью: ~%% use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ 'posts' => App\Post::class, 'comments' => App\Comment::class, ]); ~%% %% Вы можете зарегистрировать %%morphMap()%% в функции %%boot()%% или в своём %%(t)AppServiceProvider%% или создать отдельный сервис-провайдер. === ((#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()%% в базовом классе Eloquent через метод %%tags()%%: %% morphToMany('App\Tag', 'taggable'); } } %% **Определение обратного отношения** Теперь для модели %%(t)Tag%% вы должны определить метод для каждой из моделей отношения. Для нашего примера мы определим метод %%posts()%% и метод %%videos()%%: %% morphedByMany('App\Post', 'taggable'); } /** * Получить все видео, связанные с тегом. */ public function videos() { return $this->morphedByMany('App\Video', 'taggable'); } } %% **Получение отношений** Как только ваша таблица и модели определены, вы можете получить доступ к отношениям через свои модели. Например, чтобы получить доступ ко всем тегам для сообщения, вы можете просто использовать динамическое свойство %%(t)tag%%: %% $post = App\Post::find(1); foreach ($post->tags as $tag) { // } %% Вы можете также получить владельца полиморфного отношения от полиморфной модели, получив доступ к имени метода, который выполняет вызов %%morphedByMany()%%. В нашем случае, это метод %%posts()%% или %%videos()%% для модели %%(t)Tag%%. Так вы получите доступ к этим методам как к динамическим свойствам: %% $tag = App\Tag::find(1); foreach ($tag->videos as $video) { // } %% === Запросы к отношениям === Поскольку все отношения определены функциями, мы можем вызывать эти функции, чтобы получить экземпляр отношения, фактически не выполняя запросы отношения. Кроме того, все типы Eloquent отношений также являются ((/docs/v5/queries конструкторами запросов)), позволяя вам сцеплять условия запроса отношения перед выполнением SQL-запроса к вашей базе данных. Например, представьте систему блогов, в которой модель %%(t)User%% имеет множество связей с моделью %%(t)Post%%: %% hasMany('App\Post'); } } %% Вы можете запросить отношения %%posts()%% и добавить дополнительные условия к запросу отношения так: %% $user = App\User::find(1); $user->posts()->where('active', 1)->get(); %% Вы можете использовать любой из методов ((/docs/v5/queries конструктора запросов)) на отношении, поэтому не забудьте изучить документацию по конструктору запросов, где описаны все доступные вам методы. === Методы отношений или динамические свойства === Если вам не нужно добавлять дополнительные ограничения к Eloquent запросу отношения, вы можете просто получить доступ к отношению, как будто это свойство. Например, продолжая использовать наши модели %%(t)User%% и %%(t)Post%% в качестве примера, мы можем получить доступ ко всем сообщениям пользователя так: %% $user = App\User::find(1); foreach ($user->posts as $post) { // } %% Динамические свойства поддерживают "ленивую загрузку". Это означает, что они загрузят свои данные для отношения только в момент обращения к ним. Из-за этого разработчики часто используют ((#нетерпеливая нетерпеливую загрузку)), чтобы предварительно загрузить отношения, для которых они знают, что доступ будет получен после загрузки модели. Нетерпеливая загрузка обеспечивает значительное сокращение SQL-запросов, которые должны быть выполнены, чтобы загрузить отношения модели. === Проверка существования связей при ((docs/v5/queries#выборк+а))е === При чтении отношений модели вам может быть нужно ограничить результаты в зависимости от существования отношения. Например, вы хотите получить все статьи в блоге, имеющие хотя бы один комментарий. Для этого можно использовать метод %%has()%%: %% // Получить все статьи в блоге, имеющие хотя бы один комментарий... $posts = App\Post::has('comments')->get(); %% Вы также можете указать оператор и число: %% // Получить все статьи в блоге, имеющие три и более комментариев... $posts = Post::has('comments', '>=', 3)->get(); %% Можно конструировать вложенные операторы %%has()%% с помощью точечной нотации. Например, вы можете получить все статьи, которые имеют хотя бы один комментарий и голос: %% // Получить все статьи, которые имеют хотя бы один комментарий и голос... $posts = Post::has('comments.votes')->get(); %% Если вам нужно ещё больше возможностей, вы можете использовать методы %%whereHas()%% и %%orWhereHas()%%, чтобы поместить условия "where" в ваши запросы %%has()%%. Эти методы позволяют вам добавить свои ограничения в отношения, такие как проверку содержимого комментария: %% // Получить все статьи с хотя бы одним комментарием, имеющим слово "foo"% $posts = Post::whereHas('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get(); %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) === Выборка по отсутствию отношения === При получении записей модели бывает необходимо ограничить результаты выборки на основе отсутствия отношения. Например, если вы хотите получить все статьи, у которых **нет** комментариев. Для этого передайте имя отношения в метод %%doesntHave()%%: ~%% $posts = App\Post::doesntHave('comments')->get(); ~%% Для ещё большего уточнения используйте метод %%whereDoesntHave()%%, чтобы добавить условия %%(t)where%% в ваши запросы %%doesntHave()%%. Этот метод позволяет добавить дополнительные ограничения к отношению, например, проверку содержимого комментария: ~%% $posts = Post::whereDoesntHave('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get(); ~%% %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) === Подсчёт моделей в отношении === Если вы хотите посчитать число результатов отношения, не загружая их, используйте метод %%withCount()%%, который поместит столбец %%{relation}_count%% в вашу результирующую модель. Например: ~%% $posts = App\Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; } ~%% Вы можете добавить "число" для нескольких отношений так же, как и добавить ограничения к запросам: ~%% $posts = Post::withCount(['votes', 'comments' => function ($query) { $query->where('content', 'like', 'foo%'); }])->get(); echo $posts[0]->votes_count; echo $posts[0]->comments_count; ~%% %% == ((#нетерпеливая)) Нетерпеливая загрузка == При доступе к Eloquent отношениям как к свойствам отношения "лениво загружаются". Это означает, что данные отношения фактически не загружены, пока вы не обратитесь к свойству. Однако Eloquent может "нетерпеливо загружать" отношения в то время, когда вы запрашиваете родительскую модель. Нетерпеливая загрузка облегчает проблему N+1 запроса. Чтобы проиллюстрировать проблему N+1 запроса, рассмотрите модель %%(t)Book%%, которая связана с %%(t)Author%%: %% belongsTo('App\Author'); } } %% Теперь давайте получим все книги и их авторов: %% $books = App\Book::all(); foreach ($books as $book) { echo $book->author->name; } %% Этот цикл выполнит 1 запрос, чтобы получить все книги по таблице, затем выполнится другой запрос для каждой книги, чтобы получить автора. Так, если бы у нас было 25 книг, этот цикл выполнил бы 26 запросов: 1 для исходной книги и 25 дополнительных запросов, чтобы получить автора каждой книги. К счастью мы можем использовать нетерпеливую загрузку, чтобы уменьшить эту работу всего до 2 запросов. При запросах вы можете определить, какие отношения должны быть нетерпеливо загружены с использованием метода %%with()%%: %% $books = App\Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } %% Для данной операции будут выполнены только два запроса: %%(sql) select * from books select * from authors where id in (1, 2, 3, 4, 5, ...) %% **Нетерпеливо загружающиеся множественные отношения** Иногда вам, возможно, понадобится нетерпеливо загружать несколько различных отношений в единственной итерации. Для этого просто передайте дополнительные параметры в метод %%with()%%: %% $books = App\Book::with('author', 'publisher')->get(); %% **Вложенная нетерпеливая загрузка** Для вложенных отношений нетерпеливой загрузки вы можете использовать точечную нотацию. Например, давайте нетерпеливо загружать всех авторов книг и все личные контакты автора в одном Eloquent операторе: %% $books = App\Book::with('author.contacts')->get(); %% === Ограничение нетерпеливых загрузок === Иногда вам может понадобиться нетерпеливо загружать отношения. Но также иногда может понадобиться и определить дополнительные ограничения запроса для нетерпеливого запроса загрузки. Вот пример: %% $users = App\User::with(['posts' => function ($query) { $query->where('title', 'like', '%first%'); }])->get(); %% В данном примере Eloquent будет нетерпеливо загружать только те статьи, в которых столбец %%(t)title%% содержит слово %%(t)first%%. Конечно, вы можете вызвать другие методы ((/docs/v5/queries конструктора запросов)) для дополнительной настройки нетерпеливой загрузки операции: %% $users = App\User::with(['posts' => function ($query) { $query->orderBy('created_at', 'desc'); }])->get(); %% === Ленивая нетерпеливая загрузка === Иногда вам, возможно, понадобится нетерпеливо загружать отношение после того, как родительская модель уже была получена. Например, это может быть полезно, если вы должны динамично решить, загружать ли связанные модели: %% $books = App\Book::all(); if ($someCondition) { $books->load('author', 'publisher'); } %% Если вам нужно установить дополнительные ограничения запроса на нетерпеливо загружаемый запрос, вы можете передать массив, ключами которого будут отношения, которые необходимо загрузить. Значения массива должны быть экземплярами %%Closure%%, которые получают экземпляр запроса: %% $books->load(['author' => function ($query) { $query->orderBy('published_date', 'asc'); }]); %% == Вставка и изменение связанных моделей == === Метод Save === Eloquent предоставляет удобные методы для добавления новых моделей к отношениям. Например, если вам понадобится вставить новый %%(t)Comment%% для модели %%(t)Post%%. Вместо того, чтобы вручную установить атрибут %%(t)post_id%% для %%(t)Comment%%, вы можете вставить %%(t)Comment%% непосредственно из метода %%save()%% отношения: %% $comment = new App\Comment(['message' => 'A new comment.']); $post = App\Post::find(1); $post->comments()->save($comment); %% Заметьте, что мы не обращались к отношению %%comments()%% как к динамическому свойству. Вместо этого мы вызвали метод %%comments()%%, чтобы получить экземпляр отношения. Метод %%save()%% автоматически добавит надлежащее значение %%(t)post_id%% в новую модель %%(t)Comment%%. Если вам нужно сохранить несколько связанных моделей, вы можете использовать метод %%saveMany()%%: %% $post = App\Post::find(1); $post->comments()->saveMany([ new App\Comment(['message' => 'A new comment.']), new App\Comment(['message' => 'Another comment.']), ]); %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01) **Сохранение и отношения многие-ко-многим** При работе с отношением многие-ко-многим метод %%save()%% принимает вторым параметром массив атрибутов дополнительных промежуточных таблиц: ~%% App\User::find(1)->roles()->save($role, ['expires' => $expires]); ~%% %% === Метод Create === В дополнение к методам %%save()%% и %%saveMany()%% вы можете также использовать метод %%create()%%, который принимает массив атрибутов, создает модель и вставляет её в базу данных. Различие между %%save()%% и %%create()%% в том, что %%save()%% принимает экземпляр Eloquent модели целиком, в то время как %%create()%% принимает простой PHP %%array%%: %% $post = App\Post::find(1); $comment = $post->comments()->create([ 'message' => 'A new comment.', ]); %% Перед использованием метода %%create()%% пересмотрите документацию по ((/docs/v5/eloquent#массовое массовому назначению)) атрибутов. === Отношение "принадлежит к" === При обновлении отношения %%belongsTo()%% вы можете использовать метод %%associate()%%. Этот метод установит внешний ключ на дочерней модели: %% $account = App\Account::find(10); $user->account()->associate($account); $user->save(); %% При удалении отношения %%belongsTo()%% вы можете использовать метод %%dissociate()%%. Этот метод сбросит внешний ключ отношения в %%null%%: %% $user->account()->dissociate(); $user->save(); %% === Отношение многие-ко-многим === **Присоединение / Отсоединение** Также Eloquent предоставляет несколько дополнительных вспомогательных методов, чтобы сделать работу со связанными моделями более удобной. Например, давайте предположим, что у пользователя может быть много ролей, и у роли может быть много пользователей. Чтобы присоединить роль к пользователю вставкой записи в промежуточную таблицу, которая присоединяется к моделям, используйте метод %%attach()%%: %% $user = App\User::find(1); $user->roles()->attach($roleId); %% При присоединении отношения к модели вы можете также передать массив дополнительных данных, которые будут вставлены в промежуточную таблицу: %% $user->roles()->attach($roleId, ['expires' => $expires]); %% Конечно, иногда может быть необходимо отсоединить роль от пользователя. Чтобы удалить запись отношения многие-ко-многим, используйте метод %%detach()%%. Метод %%detach()%% удалит соответствующую запись из промежуточной таблицы. Однако, обе модели останутся в базе данных: %% // Отсоединить одну роль от пользователя... $user->roles()->detach($roleId); // Отсоединить все роли от пользователя... $user->roles()->detach(); %% Для удобства %%attach()%% и %%detach()%% также принимают массивы ID в параметрах: %% $user = App\User::find(1); $user->roles()->detach([1, 2, 3]); $user->roles()->attach([1 => ['expires' => $expires], 2, 3]); %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) **Изменение записи в сводной таблице** Если вам необходимо изменить существующую запись в вашей сводной таблице, используйте метод %%updateExistingPivot()%%: ~%% $user = App\User::find(1); $user->roles()->updateExistingPivot($roleId, $attributes); ~%% %% **Синхронизация ассоциаций** Вы можете также использовать метод %%sync()%%, чтобы создать ассоциации многие-ко-многим. Метод %%sync()%% принимает массив ID, чтобы поместить его в промежуточную таблицу. Все ID, которые не находятся в данном массиве, будут удалены из промежуточной таблицы. После того как эта работа завершена, только ID из данного массива будут существовать в промежуточной таблице: %% $user->roles()->sync([1, 2, 3]); %% Также вы можете передать дополнительные значения промежуточной таблицы с массивом ID: %% $user->roles()->sync([1 => ['expires' => true], 2, 3]); %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) Если вы не хотите отделять существующие ID, используйте метод %%syncWithoutDetaching()%%: ~%% $user->roles()->syncWithoutDetaching([1, 2, 3]); ~%% %% %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) **Переключение ассоциаций** Отношение многие-ко-многим также предоставляет метод %%toggle()%%, который "переключает" состояние присоединений с заданными ID. Если данный ID сейчас присоединён, то он будет отсоединён. И наоборот, если сейчас он отсоединён, то будет присоединён: ~%% $user->roles()->toggle([1, 2, 3]); ~%% **Сохранение дополнительных данных в сводной таблице** При работе с отношением многие-ко-многим метод %%save()%% принимает вторым аргументом массив дополнительных атрибутов промежуточной таблицы: ~%% App\User::find(1)->roles()->save($role, ['expires' => $expires]); ~%% **Изменение записи в сводной таблице** Для изменения существующей строки в сводной таблице используйте метод %%updateExistingPivot()%%. Этот метод принимает внешний ключ сводной записи и массив атрибутов для изменения: ~%% $user = App\User::find(1); $user->roles()->updateExistingPivot($roleId, $attributes); ~%% %% == Привязка родительских меток времени == Когда модель имеет связь %%belongsTo()%% или %%belongsToMany()%% с другими моделями, например, %%(t)Comment%%, которая принадлежит %%(t)Post%%, иногда полезно обновить метку времени родителя, когда дочерняя модель обновлена. Например, когда модель %%(t)Comment%% обновлена, вы можете автоматически "привязать" метки времени %%(t)updated_at%% на владеющий ей %%(t)Post%%. Eloquent упрощает эту работу. Просто добавьте свойство %%(t)touches%%, содержащее имена отношений к дочерней модели: %% belongsTo('App\Post'); } } %% Теперь, когда вы обновляете %%(t)Comment%%, у владеющего %%(t)Post%% тоже обновится столбец %%(t)updated_at%%, позволяя проще узнать, когда необходимо аннулировать кэш модели %%(t)Post%%: %% comment = App\Comment::find(1); $comment->text = 'Edit to this comment!'; $comment->save(); %%