动态属性是惰性加载的,这意味着在你实际访问他们之前,其关联数据是不会加载的。正因为如此,开发的时候通常使用预加载来进行加载一些即将用到的关联模型。预加载要求必须加载一个模型的关系,这有效的减少了查询的次数。
查询关联是否存在
当访问一个模型的记录时,你可能会希望基于关联的记录是否存在来对结果进行限制。比如,想象一下你希望获取最少有一条评论的博客文章。你可以传递关联的名称到 has 方法来做这些:
// Retrieve all posts that have at least one comment... $posts = App\Post::has('comments')->get();
你也可以指定操作符,和数量来进一步定制查询:
// Retrieve all posts that have three or more comments... $posts = Post::has('comments', '>=', 3)->get();
你也可以使用 . 语法来构造嵌套的 has 语句。比如,你可以获取所有包含至少一条评论和投票的文章:
// Retrieve all posts that hava at least one comment with votes... $posts = Post::has('comments.votes')->get();
如果你需要更高的控制,你可以使用 whereHas 和 orWhereHas 方法来在 has 查询中插入 where 子句。这些方法允许你为关联进行自定义的约束查询。比如检查评论的内容:
// Retrieve all posts with at least one comment containing words like foo% $posts = Post::whereHas('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get();
统计关联结果
如果你希望统计关联的结果而不实际的加载它们,你可以使用 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 查找问题,我们可以来看一个 Author 关联 Book 的示例:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Book extends Model { /** * Get the author that wrote the book. */ public function author() { return $this->belongsTo('App\Author'); } }
现在,让我们检索所有的书籍和他们的作者:
$books = App\Book::all(); foreach ($books as $book) { echo $book->author->name; }
这个循环会执行一次查找回所有的书籍,接着每本书会运行一次查找作者的操作。所以,如果我们拥有 25 本书,那么循环将会进行 26 次查询:1 次查询所有的书籍,25 次查询相关书籍的作者。
非常幸运的,我们可以使用预加载来将查询有效的控制在 2 次。当查询时,使用 with 方法来指定关联的预加载:
$books = App\Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; }
对于这个操作,只会执行两个查询:
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 会值预加载文章的 title 列包含 first 单词的记录。当然,你也可以调用其他查询生成器可用的方法:
$users = App\User::with(['posts' => function ($query) { $query->orderBy('created_at', 'desc'); }])->get();
延迟预加载
有时候你可能需要在上层模型被获取后才预加载其关联。当你需要来动态决定是否加载关联模型时尤其有用:
$books = App\Book::all(); if ($someCondition) { $books->load('author', 'publisher'); }
如果你需要对预加载做一些查询约束,你可以传递 Closure 到 load 方法:
$books->load(['author' => function ($query) { $query->orderBy('published_date', 'asc'); }]);
插入关系模型
Save 方法