В двух предыдущих уроках Мастер Академии показывал вам одно за другим. Eloquent Класс модели поддерживает семь типов ассоциаций. С помощью базового метода ассоциации мы можем быстро реализовать связь между моделями и выполнить Связанный. запрос. Сегодня мы проведем Связанный на основе определения модельного объединения. операции запроса, вставки и обновления, узнайте, как использовать ассоциацию модели для улучшения читаемости кода и повышения эффективности кодирования.
о Связанный запрос, мы уже представили его подробно, когда вводили определение ассоциативного отношения. Давайте кратко рассмотрим его здесь. существовать Eloquent Связанный по модели запрос в основном делится на два способа, один - ленивая загрузка (динамический атрибут), одна нетерпеливая загрузка(проходитьwith
метод)。С точки зрения производительности,нетерпеливая загрузка лучше, потому что она заранее начнется с базы данных запрашивает все связанные данные одновременно, в то время как ленивая загрузка будет выполнять запрос только каждый раз, когда запрашивает динамические атрибуты, и будет подключаться к базе несколько раз. данных, хуже по производительности (база Основная стоимость операции с данными составляет базу данные связаны, поэтому если вы хотите оптимизировать производительность в процессе разработки, постарайтесь уменьшить частые подключения к базе. данных)。
Ленивая загрузка осуществляется следующим способом:
$post = Post::findOrFail(1);
$author = $post->author;
за посещение author
Атрибуты будут выполнять запрос к базе данных. Если возвращаемый результат статьи представляет собой список, вам необходимо выполнить обход, чтобы получить информацию об авторе. Предположим, вы хотите выполнить цикл. N раз, плюс приобретение самой артикул-модели, всего N + 1 запросы, в то время как PHP Соединение с базой данных является коротким, и базу данных необходимо каждый раз подключать повторно, поэтому этот метод не рекомендуется с точки зрения производительности.
Кроме того, если вы получаете доступ к экземпляру модели author()
возвращается не пользовательский экземпляр, а экземпляр ассоциации, который внедряется в построитель запросов, поэтому на его основе можно создать построитель запросов с помощью цепочки методов для выполнения более сложных запросов. Мы используем Take a one. -ко-многим запрос в качестве примера:
$user = User::findOrFail(1);
$posts = $user->posts()->where('views', '>', 0)->get();
Таким образом, мы можем отфильтровать результаты статей, опубликованных этим пользователем, число просмотров которых превышает 1.
Есть фильтрация результатов
иногда,Возможно, вам придется отфильтровать результаты запроса на основе результатов Связанный запрос.,Например, мы хотим, чтобы все пользователи, опубликовавшие статьи,,Вы можете сделать это:
$users = User::has('posts')->get();
Возвращается коллекция экземпляров модели:
Нижний слой соответствует EXISTS
Запрос:
select
*
from
`users`
where
exists (
select
*
from
`posts`
where
`users`.`id` = `posts`.`user_id`
and `posts`.`deleted_at` is null
)
and `email_verified_at` is not null
Если вы хотите дополнительно фильтровать пользователей, опубликовавших более 1 статьи, вы можете добавить условия запроса:
$users = User::has('posts', '>', 1)->get();
Базовый оператор SQL-запроса выполняется следующим образом:
select
*
from
`users`
where
(
select
count(*)
from
`posts`
where
`users`.`id` = `posts`.`user_id`
and `posts`.`deleted_at` is null
) > 1
and `email_verified_at` is not null
Ты даже также можете пройти Вложенный Связанный запросить способ фильтрации пользователей, оставивших комментарии к статьям:
$users = User::has('posts.comments')->get();
По сути, это вложенный EXISTS
Запрос:
Кроме того, существует orHas
метод, как следует из названия, он выполнит OR
Запрос, например мы хотим отфильтровать статьи, содержащие комментарии или теги:
$posts = Post::has('comments')->orHas('tags')->get();
Если вы хотите пройти более сложный Связанный Пример модели фильтра запроса, Вы также можете пройти whereHas
/orWhereHas
Метод определяет условия запроса на основе функции закрытия. Например, мы хотим отфильтровать всех пользователей, чьи заголовки опубликованных статей содержат «Laravel Academy»:
$users = User::whereHas('posts', function ($query) {
$query->where('title', 'like', 'Академия Laravel%');
})->get();
Базовый оператор SQL-запроса выполняется следующим образом:
Если вы хотите дополнительно отфильтровать пользователей, чьи заголовки статей и комментарии содержат «Laravel Academy», вы можете дополнительно указать это через построитель запросов в приведенной выше функции закрытия:
$users = User::whereHas('posts', function ($query) {
$query->where('title', 'like', «Академия Laravel%»)
->whereExists(function ($query) {
$query->from('comments')
->whereRaw('`posts`.`id` = `comments`.`commentable_id`')
->where('content', 'like', «Академия Laravel%»)
->where('commentable_type', Post::class)
->whereNull('deleted_at');
});
})->get();
Если вы хотите отфильтровать пользователей, чьи заголовки статей или комментарии содержат «Laravel Academy», введите whereExists
Заменить на orWhereExists
Просто сделайте это:
$users = User::whereHas('posts', function ($query) {
$query->where('title', 'like', «Академия Laravel%»)
->orWhereExists(function ($query) {
$query->from('comments')
->whereRaw('`posts`.`id` = `comments`.`commentable_id`')
->where('content', 'like', «Академия Laravel%»)
->where('commentable_type', Post::class)
->whereNull('deleted_at');
});
})->get();
Если вы не хотите создавать построитель запросов самостоятельно, вы также можете добиться той же функции, что и выше, с помощью цепочки методов:
// and
$users = User::whereHas('posts', function ($query) {
$query->where('title', 'like', 'Академия Laravel%');
})->whereHas('posts.comments', function ($query) {
$query->where('content', 'like', 'Академия Laravel%');
})->get();
// or
$users = User::whereHas('posts', function ($query) {
$query->where('title', 'like', 'Академия Laravel%');
})->orWhereHas('posts.comments', function ($query) {
$query->where('content', 'like', 'Академия Laravel%');
})->get();
Нет фильтра результатов
и has
/orHas
В отличие от метода существует еще одна пара doesntHave
/orDoesntHave
метод. Очевидно, они используются для фильтрации экземпляров модели, которые не содержат соответствующих связанных результатов. Например, если нам нужны пользователи, которые не публиковали статьи, мы можем использовать doesntHave
Реализация метода:
$users = User::doesntHave('posts')->get();
Полученный результат также представляет собой набор экземпляров модели:
выполнен внизу SQL Одно заявление NOT EXISTS
Запрос:
select
*
from
`users`
where
not exists (
select
*
from
`posts`
where
`users`.`id` = `posts`.`user_id`
and `posts`.`deleted_at` is null
)
and `email_verified_at` is not null
Если вы хотите получать статьи без комментариев и тегов, вы можете комбинировать doesntHave
и orDoesntHave
Реализация метода:
$posts = Post::doesntHave('comments')->doesntHave('tags')->get();
Соответствующий оператор SQL:
и whereHas
Метод и orWhereHas
Условно говоря, существуют также whereDoesntHave
и orWhereDoesntHave
Метод и использование те же, поэтому я не буду здесь вдаваться в подробности.
Мы также можем пройти Eloquent предоставил withCount
Метод подсчитывает количество результатов корреляции без загрузки корреляционной модели. Например, если мы хотим посчитать количество комментариев к определенной статье, мы можем сделать это:
$post = Post::withCount('comments')->findOrFail(32);
Давайте проверим, что вернулось $post
Структура данных экземпляра модели:
который включает в себя comments_count
поле, через которое можно получить доступ к количеству комментариев к статье. Если вы хотите подсчитать числовые поля других связанных результатов модели, вы можете сделать вывод по очереди, и соответствующие поля будут {relation}_count
структура.
Примечание. Чтобы повысить производительность запросов в реальной разработке, мы часто
posts
Избыточность в таблице обеспечиваетcomments_count
поле, каждый раз, когда добавляется новый комментарий, значение этого поля увеличивается. 1. Просто извлекайте поле непосредственно при запросе, тем самым повышая производительность запроса.
Кроме того, вы можете передать несколько связей через массив для одновременного подсчета нескольких полей, а также указать условия фильтрации для соответствующей статистики с помощью функции закрытия:
$post = Post::withCount(['tags', 'comments' => function ($query) {
$query->where('content', 'like', «Академия Laravel»)
->orderBy('created_at', 'desc');
}])->findOrFail(32);
Вы даже можете установить псевдонимы для статистических полей, чтобы определенное поле можно было подсчитывать по разным измерениям:
$post = Post::withCount([
'comments',
'comments as pending_comments' => function($query) {
$query->where('status', Comment::PENDING);
}
])->findOrFail(32);
Соответствующие результаты возврата следующие:
Эта функция очень удобна для быстрого запроса в сценариях, где не учитывается производительность. Однако, если у вас высокие требования к производительности, это не рекомендуется, ведь вам нужно выполнить несколько запросов, чтобы получить статистику один за другим.
Мы уже это представили, нетерпеливая загрузкапроходить with
Реализация метода:
$post = Post::with('author')->findOrFail(1);
$author = $post->author;
нетерпеливая загрузка запросит результаты экземпляра модели через IN
Запрос получает связанные результаты и прикрепляет их к соответствующему экземпляру модели. База данных не будет запрашиваться при последующих обращениях. Таким образом, независимо от того, сколько экземпляров модели существует, результаты корреляции будут запрошены только один раз. Включая саму модель, всего имеется два запроса. При запросе в списке количество запросов на подключение к базе данных значительно сокращается. что приводит к повышению производительности. Рекомендуемое использование.
нетерпеливая загрузка поддерживает одновременную загрузку нескольких связанных моделей (имя параметра соответствует имени соответствующего связанного метода):
$posts = Post::with('author', 'comments', 'tags')->findOrFail(1);
Формат возвращаемых данных следующий:
также,нетерпеливая загрузка также поддерживает вложенные запросы,Например, мы хотим получить доступ к расширенной табличной информации автора статьи.,Вы можете сделать это:
$post = Post::with('author.profile')->findOrFail(1);
Таким образом, вы можете получить вложенные profile
Информация записана в таблице:
Здесь будут участвовать трое SQL Запрос:
select * from `posts` where `posts`.`id` = ? and `posts`.`deleted_at` is null limit 1;
select * from `users` where `users`.`id` in (?) and `email_verified_at` is not null;
select * from `user_profiles` where `user_profiles`.`user_id` in (?);
Вы также можете пройти with
Метод указывает поля для загрузки:
$post = Post::with('author:id,name')->findOrFail(1);
Примечание. Используйте эту функцию
id
Поля должны быть перечислены.
существоватьнетерпеливая загрузкасередина,Дополнительные ограничения также могут быть переданы через замыкания.,Просто это ограничение является фильтром самой связанной модели.,Запрос, не затрагивающий целевую модель:
$post = Post::with(['comments' => function ($query) {
$query->where('content', 'like', «Академия Laravel%»)
->orderBy('created_at', 'desc');
}])->where('id', '<', 5)->get();
выполнен внизу SQL Заявление заключается в следующем:
select
*
from
`posts`
where
`id` < 5
and `posts`.`deleted_at` is null
select
*
from
`comments`
where
`comments`.`commentable_id` in (1, 2, 3, 4)
and `comments`.`commentable_type` = "App\Post"
and `content` like «Академия Ларавель%»
and `comments`.`deleted_at` is null
order by
`created_at` desc
иногда,Вам может показаться, что загрузка всех связанных данных одновременно — немного расточительно.,Только при определенных условияхиспользоватьданных мы можемпроходить Выполняется динамическое условное суждениенетерпеливая загрузка или ленивая загрузка. Мы называем такую загрузку ленивой нетерпеливой. загрузка, эту загрузку можно осуществить через load
Реализация метода:
$users = User::all();
$condition = true;
if ($condition) {
$users->load('posts');
}
Ленивыйнетерпеливая загрузка Слишкомнетерпеливая загрузка, загружается только при необходимости, поэтому добавляется модификатор "ленивый", выполнено внизу SQL Оператор запроса инетерпеливая загрузка та же:
select
*
from
`posts`
where
`posts`.`user_id` in (?, ?, ?, ?, ?)
and `posts`.`deleted_at` is null
Подобно инетерпеливой загрузке, он также поддерживает передачу дополнительных ограничений через замыкания:
$posts = Post::where('id', '<=', 3)->get();
$posts->load(['comments' => function ($query) {
$query->where('content', 'like', «Академия Laravel%»)
->orderBy('created_at', 'desc');
}]);
При добавлении новой связанной модели,Вы можете вызвать соответствующий метод родительской модели, чтобы напрямую вставлять записи в данные., преимущество этого заключается в том, что нет необходимости указывать значение поля, связанного с внешним ключом, связанной модели и родительской модели, Eloquent Нижний слой автоматически определится и установится. Например, если мы хотим добавить новый комментарий к статье, мы можем сделать это:
$post = Post::findOrFail(1);
$faker = \Faker\Factory::create();
$comment = new Comment();
$comment->content = $faker->paragraph;
$comment->user_id = mt_rand(1, 15);
$post->comments()->save($comment);
Eloquent Нижний слой автоматически поможет нам его поддерживать. commentable_id
и commentable_type
поле.
Вы также можете пройти saveMany
Этот метод вставляет одновременно несколько связанных записей при условии, что для связанной модели настроено пакетное назначение. Например, мы. Comment
Белый список конфигурации класса модели $fillable
Атрибуты следующие (вы также не можете настроить пакетное назначение, но вам нужно создать экземпляр несколько раз и установить значения атрибутов модели комментариев одно за другим, что очень хлопотно):
protected $fillable = [
'content', 'user_id'
];
Таким образом, мы можем вставлять данные комментариев к статье пакетами:
$post = Post::findOrFail(1);
$faker = \Faker\Factory::create();
$post->comments()->saveMany([
new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)])
]);
Кроме того, Мы также можем пройти create
/createMany
Методы вставки связанных данных и save
/saveMany
Разница между методами в том, что эти два метода получают параметры массива:
// вставить запись
$post->comments()->create([
'content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)
]);
// Вставить несколько записей
$post->comments()->createMany([
['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)],
['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)],
['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]
]);
Если вы хотите обновить поля внешнего ключа модели (родительской модели), к которой принадлежит вновь созданный экземпляр модели, например: posts
Возьмем таблицу в качестве примера. Вновь добавленные записи требуют обновления. user_id
Поля можно реализовать следующим образом:
$user = User::findOrFail(1);
$post->author()->associate($user);
$post->save();
С другой стороны, если вы хотите удалить связь между текущей моделью и моделью, которой она принадлежит, вы можете использовать dissociate
Метод достижения:
$post->author()->dissociate();
$post->save();
Таким образом, это будет posts.user_id
установлен на null
。Предпосылка user_id
разрешено как null
,В противном случае будет выдано исключение.
пустая объектная модель
Если поле внешнего ключа user_id
разрешено как пустые слова, когда мы в гостях Post
на модели author
атрибут, возврат по умолчанию null
。Eloquent Позволяет нам определить тип по умолчанию для этого пустого объекта. Тип этого объекта можно указать при определении ассоциации:
public function author()
{
return $this->belongsTo(User::class, 'user_id', 'id', 'author')
->withDefault();
}
Таким образом, получите доступ к соответствующему Post
Когда используется экземпляр модели, возвращается пустое значение. App\User
экземпляр, вы также можете указать значения свойств по умолчанию для этого объекта:
public function author()
{
return $this->belongsTo(User::class, 'user_id', 'id', 'author')
->withDefault([
'id' => 0,
'name' => «Гостевой пользователь»,
]);
}
Посетите еще раз Post
на модели author
атрибут, будет возвращен следующий пустой объект по умолчанию:
Эта функция фактически применяет шаблон пустого объекта в шаблоне проектирования. Преимущество состоит в том, что согласованный код может быть написан для различных ситуаций в коде. Таким образом, нам не нужно везде судить, что делать, если информация об авторе статьи пуста, потому что в этом случае нормальный возврат User
Модельный экземпляр.
При вставке связанных записей «многие ко многим»,Это можно сделать вышеуказанным способом. Возьмем статью и тег в качестве примера,Вы можете добавить новую модель тега через модель статьи таким образом.,Также обновите записи промежуточной таблицы:
// Вставить одну запись
$post->tags()->save(
new Tag(['name' => $faker->word])
);
// Если промежуточная таблица получает дополнительные параметры, их можно передать через второй параметр.
$post->tags()->save(
new Tag(['name' => $faker->word]),
['user_id' => 1]
);
// Вставить несколько записей
$post->tags()->saveMany([
new Tag(['name' => $faker->unique()->word]),
new Tag(['name' => $faker->unique()->word]),
new Tag(['name' => $faker->unique()->word])
]);
// если Вставить несколько записейнужно пройтисередина Расписаниедополнительные поляценить(проходить键ценить关联对应记录идополнительные поля)
$post->tags()->saveMany([
1 => new Tag(['name' => $faker->unique()->word]),
2 => new Tag(['name' => $faker->unique()->word]),
3 => new Tag(['name' => $faker->unique()->word])
], [
1 => ['user_id' => 1],
2 => ['user_id' => 2],
3 => ['user_id' => 3],
]);
Нижний уровень также обеспечивает операции привязки и отмены для связей «многие ко многим» между существующими моделями.
Или возьмите статью и тег в качестве примера,Чтобы связать две записи, которые не связаны друг с другом,Можетпроходить attach
Реализация метода:
$post = Post::findOrFail(1);
$tag = Tag::findOrFail(1);
$post->tags()->attach($tag->id);
// Если в промежуточной таблице есть другие дополнительные поля, их можно передать через второй параметр массива.
// $post->tags()->attach($tag->id, ['user_id' => $userId]);
// Вы также можете привязать несколько тегов одновременно
// $post->tags()->attach([1, 2]);
// Если вы связываете несколько тегов и хотите передать дополнительные значения полей, вы можете сделать это:
/*$post->tags()->attach([
1 => ['user_id' => 1],
2 => ['user_id' => 2]
]);*/
Если вы хотите отменить эту ассоциацию, вы можете использовать detach
Реализация метода:
$post->tags()->detach(1);
// Если вы хотите отменить несколько ассоциаций одновременно, вы можете сделать это:
// $post->tags()->detach([1, 2]);
// Если вы хотите удалить все ассоциации сразу, вы можете сделать это:
// $post->tags()->detach();
Два вышеописанных метода очень удобны, но есть еще более удобный. Когда мы обновляем тег статьи, он часто предполагает одновременную отвязку и отвязку связанного тега. Согласно приведенной выше логике, нам нужно сначала запросить все записи тегов, а затем определить, какие из них необходимо связать и связать, какие нужно разъединить, а какие нужно вставить в новые записи тегов, а затем передать attach
и detach
Метод окончательно завершает связывание и разъединение соответствующего артикля.
Для тех записей тегов, которые уже существуют,我们Можетпроходитьболее эффективный Метод и Статья связывает ассоциацию иразблокирует,этотметодто есть sync
,позвони сюдаметод Просто передайте только что созданный/Тег соответствия обновленных статей ID относительно того, какие ассоциации, не существовавшие ранее, необходимо связать, какие существующие ассоциации необходимо освободить, а какие необходимо сохранить статус-кво, оставьте это на усмотрение. Eloquent Чтобы судить на нижнем уровне:
$post->tags()->sync([1, 2, 3]);
Если необходимо передать дополнительные параметры, соответствующие новым данным, см. attach
Вот и все, оба одинаковы.
Иногда вам может потребоваться просто обновить значение поля промежуточной таблицы. В этом случае вы можете пройти мимо. updateExistingPivot
Метод передает значения полей, которые необходимо обновить, во втором параметре в виде ассоциативного массива:
$post->tags()->updateExistingPivot($tagId, $attributes);
Когда одна модель принадлежит другой модели, например. Comment
модель принадлежит Post
Модель: при обновлении дочерней модели часто полезно синхронно обновлять время обновления родительской модели, например, запускать обновление кэша страницы статьи при появлении новых комментариев или уведомлять поисковые системы об обновлении страницы и т. д. . красноречивый Этот механизм синхронизации предназначен для того, чтобы помочь нам инициировать время обновления родительской модели при обновлении дочерней модели. updated_at
Обновление значения поля. Чтобы этот механизм вступил в силу, его необходимо настроить в подмодели. $touches
свойство:
// Родительские отношения для запуска обновления
protected $touches = [
'commentable'
];
Значение атрибута представляет собой имя соответствующего метода ассоциации и поддерживает настройку нескольких ассоциаций. Ниже мы кратко продемонстрируем, с помощью id=31
Например, запись комментария , соответствующие данные модели и данные модели статьи, к которой она принадлежит, выглядят следующим образом:
Теперь мы обновляем соответствующие данные модели комментариев и сохраняем их:
$comment = Comment::findOrFail(31);
$comment->content = «Laravel Academy стремится предоставлять высококачественные ресурсы для изучения китайского языка Laravel»;
$comment->save();
Еще раз проверьте модель комментариев и соответствующие данные модели статьи. Вы можете видеть, что события обновления модели статьи и время обновления модели комментариев совпадают:
Хорошо, на этом у нас закончились отношения ассоциации. Мы разделили ее на три статьи, чтобы представить отношения управления в модели Eloquent. Для обзора она в основном включает в себя следующее содержание:
Я надеюсь, что вы сможете понять и полностью освоить его после прочтения этой серии руководств от Academy Master. Eloquent определение моделиииспользовать,В чем проблема,Добро пожаловать на связь со мной в любое время.