链接失效,无法访问
相关信息:
- 类型:文档文章
- 文章: 模型关联
- 文档: 《Laravel 8 中文文档(8.5)》
此投稿已在 3年前 合并。
内容修改:
Old | New | Differences |
---|---|---|
1 | ||
2 | 1 | # Eloquent: 关联 |
3 | 2 | |
4 | 3 | - [简介](#introduction) | … | … |
51 | 50 | - [多态一对多](#one-to-many-polymorphic-relations) |
52 | 51 | - [多态多对多](#many-to-many-polymorphic-relations) |
53 | 52 | |
54 | ||
55 | ||
53 | ||
54 | ||
56 | 55 | <a name="defining-relationships"></a> |
57 | 56 | ## 定义关联 |
58 | 57 | … | … |
92 | 91 | |
93 | 92 | return $this->hasOne(Phone::class, 'foreign_key'); |
94 | 93 | |
95 | ||
96 | ||
94 | ||
95 | ||
97 | 96 | 另外,Eloquent 假设外键的值是与父模型的主键(Primary Key)相同的。换句话说,Eloquent 将会通过 `Phone` 记录的 `user_id` 列中查找与用户表的 `id` 列相匹配的值。如果你希望使用自定义的主键值,而不是使用 `id` 或者模型中的 `$primaryKey` 属性,你可以给 `hasOne` 方法传递第三个参数: |
98 | 97 | |
99 | 98 | return $this->hasOne(Phone::class, 'foreign_key', 'local_key'); | … | … |
132 | 131 | return $this->belongsTo(User::class, 'foreign_key'); |
133 | 132 | } |
134 | 133 | |
135 | ||
136 | ||
134 | ||
135 | ||
137 | 136 | 如果父模型不使用 `id` 字段来作为主键,或者您想要使用其他的字段来匹配相关联的模型,那么您可以向 `belongsTo` 方法传递第三个参数,这个参数是在父模型中自己定义的字段: |
138 | 137 | |
139 | 138 | /** | … | … |
178 | 177 | // |
179 | 178 | } |
180 | 179 | |
181 | ||
182 | ||
180 | ||
181 | ||
183 | 182 | 由于所有的关系都可以看成是查询构造器,所以您也可以通过链式调用的方式,在 `comments` 方法中继续添加条件约束: |
184 | 183 | |
185 | 184 | $comment = Post::find(1)->comments() | … | … |
226 | 225 | |
227 | 226 | Eloquent 通过检查关联方法的名称,从而在关联方法名称后面加上 `_` ,然后再加上父模型 (Post)的主键名称,以此来作为默认的外键名。因此,在上面这个例子中,Eloquent 将会默认 `Post` 模型在 `comments` 表中的外键是 `post_id`。 |
228 | 227 | |
229 | ||
230 | ||
228 | ||
229 | ||
231 | 230 | 但是,如果您的外键不遵循这种约定的话,那么您可以传递一个自定义的外键名来作为 `belongsTo` 方法的第二个参数: |
232 | 231 | |
233 | 232 | /** | … | … |
283 | 282 | }); |
284 | 283 | } |
285 | 284 | |
286 | ||
287 | ||
285 | ||
286 | ||
288 | 287 | <a name="has-one-through"></a> |
289 | 288 | ### 远程一对一 |
290 | 289 | … | … |
350 | 349 | } |
351 | 350 | } |
352 | 351 | |
353 | ||
354 | ||
352 | ||
353 | ||
355 | 354 | <a name="has-many-through"></a> |
356 | 355 | ### 远程一对多 |
357 | 356 | … | … |
414 | 413 | } |
415 | 414 | } |
416 | 415 | |
417 | ||
418 | ||
416 | ||
417 | ||
419 | 418 | <a name="many-to-many"></a> |
420 | 419 | ## 多对多关联 |
421 | 420 | 多对多关联比 `hasOne` 和 `hasMany` 关联稍微复杂些。举个例子,一个用户可以拥有多个角色,同时这些角色也可以分配给其他用户。例如,一个用户可是「作者」和「编辑」;当然,这些角色也可以分配给其他用户。所以,一个用户可以拥有多个角色,一个角色可以分配给多个用户。 | … | … |
460 | 459 | } |
461 | 460 | } |
462 | 461 | ``` |
463 | ||
464 | ||
462 | ||
463 | ||
465 | 464 | 一旦关联关系被定义后,你可以通过 `roles`「动态属性」获取用户角色: |
466 | 465 | |
467 | 466 | use App\Models\User; | … | … |
506 | 505 | } |
507 | 506 | } |
508 | 507 | |
509 | ||
510 | ||
508 | ||
509 | ||
511 | 510 | 如你所见,除了引入模型 `App\Models\User` 外,其它与在 `User` 模型中定义的完全一样。由于我们重用了 `belongsToMany` 方法,自定义连接表表名和自定义连接表里的键的字段名称在这里同样适用。 |
512 | 511 | |
513 | 512 | <a name="retrieving-intermediate-table-columns"></a> | … | … |
535 | 534 | |
536 | 535 | > 注意:使用 Eloquent 自动维护时间戳的中间表需要同时具有 `created_at` 和 `updated_at` 时间戳字段。 |
537 | 536 | |
538 | ||
539 | ||
537 | ||
538 | ||
540 | 539 | <a name="customizing-the-pivot-attribute-name"></a> |
541 | 540 | #### 自定义 `pivot` 属性名称 |
542 | 541 | … | … |
572 | 571 | |
573 | 572 | 如果你想定义一个自定义模型来表示多对多关系的中间表,你可以在定义关系时调用 `using` 方法。 |
574 | 573 | |
575 | ||
576 | ||
574 | ||
575 | ||
577 | 576 | 自定义多对多中间表模型都必须扩展自 `Illuminate\Database\Eloquent\Relations\Pivot` 类,自定义多对多(多态)中间表模型必须继承 `Illuminate\Database\Eloquent\Relations\MorphPivot` 类。例如,我们在写 `Role` 模型的关联时,使用自定义中间表模型 `RoleUser`: |
578 | 577 | |
579 | 578 | <?php | … | … |
626 | 625 | 多态关联允许目标模型借助单个关联从属于多个模型。例如,你正在构建一个允许用户共享博客文章和视频的应用程序,其中 `Comment` 模型可能同时从属于 `Post` 和 `Video` 模型。 |
627 | 626 | |
628 | 627 | <a name="one-to-one-polymorphic-relations"></a> |
629 | ||
630 | ||
628 | ||
629 | ||
631 | 630 | ### 一对一 (多态) |
632 | 631 | |
633 | 632 | <a name="one-to-one-polymorphic-table-structure"></a> | … | … |
695 | 694 | } |
696 | 695 | } |
697 | 696 | |
698 | ||
699 | ||
697 | ||
698 | ||
700 | 699 | <a name="one-to-one-polymorphic-retrieving-the-relationship"></a> |
701 | 700 | #### 获取关联 |
702 | 701 | … | … |
755 | 754 | commentable_id - integer |
756 | 755 | commentable_type - string |
757 | 756 | |
758 | ||
759 | ||
757 | ||
758 | ||
760 | 759 | <a name="one-to-many-polymorphic-model-structure"></a> |
761 | 760 | #### 模型结构 |
762 | 761 | … | … |
849 | 848 | taggable_id - integer |
850 | 849 | taggable_type - string |
851 | 850 | |
852 | > 技巧:在深入研究多态多对多关系之前,你可能会从阅读有关典型 [多对多关系](#many-to-many) 的文档中受益。 | |
853 | ||
851 | > 技巧:在深入研究多态多对多关系之前,你可能会从阅读有关典型 [多对多关系](#many-to-many) 的文档中受益。 | |
852 | ||
854 | 853 | <a name="many-to-many-polymorphic-model-structure"></a> |
855 | 854 | #### 模型结构 |
856 | 855 | … | … |
903 | 902 | } |
904 | 903 | } |
905 | 904 | |
906 | ||
907 | ||
905 | ||
906 | ||
908 | 907 | <a name="many-to-many-polymorphic-retrieving-the-relationship"></a> |
909 | 908 | #### 获取关联 |
910 | 909 | … | … |
945 | 944 | ]); |
946 | 945 | |
947 | 946 | 可以在 `App\Providers\AppServiceProvider` 的 `boot` 函数中注册 `morphMap`,或者创建一个单独的服务提供者。 |
948 | ||
949 | ||
947 | ||
948 | ||
950 | 949 | 您可以在运行时使用 `getMorphClass` 方法确定给定模型的别名。相反,您可以使用 `Relation::getMorphedModel` 方法来确定与别名相关联的类名: |
951 | 950 | |
952 | 951 | use Illuminate\Database\Eloquent\Relations\Relation; | … | … |
978 | 977 | |
979 | 978 | 因为所有的 Eloquent 关联都是通过方法定义的,你可以调用这些方法来获取关联的实例,而无需真实执行查询来获取相关的模型。此外,所有的 Eloquent 关联也可以用作[查询生成器](/docs/laravel/8.x/queries),允许你在最终对数据库执行 SQL 查询之前,继续通过链式调用添加约束条件。 |
980 | 979 | |
981 | ||
982 | ||
980 | ||
981 | ||
983 | 982 | 例如,假设有一个博客系统,它的 `User` 模型有许多关联的 `Post` 模型: |
984 | 983 | |
985 | 984 | <?php | … | … |
1038 | 1037 | }) |
1039 | 1038 | ->get(); |
1040 | 1039 | |
1041 | ||
1042 | ||
1040 | ||
1041 | ||
1043 | 1042 | 上面的示例将生成以下 SQL。 请注意,逻辑分组已对约束进行了正确分组,并且查询仍然限定于特定用户: |
1044 | 1043 | |
1045 | 1044 | ```sql | … | … |
1078 | 1077 | // 查出至少有三条评论的文章... |
1079 | 1078 | $posts = Post::has('comments', '>=', 3)->get(); |
1080 | 1079 | |
1081 | ||
1082 | ||
1080 | ||
1081 | ||
1083 | 1082 | 也可以用「点」语法构造嵌套的 `has` 语句。例如,查出至少有一条评论和图片的文章: |
1084 | 1083 | |
1085 | 1084 | // 查出至少有一条带图片的评论的文章... | … | … |
1118 | 1117 | $query->where('content', 'like', 'code%'); |
1119 | 1118 | })->get(); |
1120 | 1119 | |
1121 | ||
1122 | ||
1120 | ||
1121 | ||
1123 | 1122 | 您可以使用“点”符号对嵌套关系执行查询。 例如,以下查询将检索所有没有评论的帖子; 但是,有未被禁止的作者评论的帖子将包含在结果中: |
1124 | 1123 | |
1125 | 1124 | use Illuminate\Database\Eloquent\Builder; | … | … |
1170 | 1169 | } |
1171 | 1170 | )->get(); |
1172 | 1171 | |
1173 | ||
1174 | ||
1172 | ||
1173 | ||
1175 | 1174 | <a name="querying-all-morph-to-related-models"></a> |
1176 | 1175 | #### 查询所有关联模型 |
1177 | 1176 | … | … |
1224 | 1223 | echo $posts[0]->comments_count; |
1225 | 1224 | echo $posts[0]->pending_comments_count; |
1226 | 1225 | |
1227 | ||
1228 | ||
1226 | ||
1227 | ||
1229 | 1228 | <a name="deferred-count-loading"></a> |
1230 | 1229 | #### 延迟加载计数 |
1231 | 1230 | … | … |
1274 | 1273 | ### 计算多态关联关系的数量 |
1275 | 1274 | |
1276 | 1275 | 如果你想预加载多态关联关系以及这个关联关系关联的其他关联关系的计数统计,可以通过将`with` 方法与 `morphTo`关系和`morphWithCount` 方法结合来实现。 |
1277 | ||
1278 | ||
1276 | ||
1277 | ||
1279 | 1278 | 在这个例子中,我们假设 `Photo` 和 `Post` 模型可以创建 `ActivityFeed` 模型。 我们将假设 `ActivityFeed` 模型定义了一个名为 `parentable` 的多态关联关系,它允许我们为给定的 `ActivityFeed` 实例检索父级 `Photo` 或 `Post` 模型。 此外,让我们假设`Photo` 模型有很多`Tag` 模型、`Post` 模型有很多`Comment` 模型。 |
1280 | 1279 | |
1281 | 1280 | 假如我们想要检索 `ActivityFeed` 实例并为每个 `ActivityFeed` 实例预先加载 `parentable` 父模型。 此外,我们想要检索与每张父照片关联的标签数量以及与每个父帖子关联的评论数量: | … | … |
1324 | 1323 | } |
1325 | 1324 | } |
1326 | 1325 | |
1327 | ||
1328 | ||
1326 | ||
1327 | ||
1329 | 1328 | 我们检索所有书籍及其作者: |
1330 | 1329 | |
1331 | 1330 | use App\Models\Book; | … | … |
1371 | 1370 | <a name="nested-eager-loading-morphto-relationships"></a> |
1372 | 1371 | #### 嵌套预加载 `morphTo` 关联 |
1373 | 1372 | |
1374 | ||
1375 | ||
1373 | ||
1374 | ||
1376 | 1375 | 如果你希望加载一个 `morphTo` 关系,以及该关系可能返回的各种实体的嵌套关系,可以将 `with` 方法与 `morphTo` 关系的 `morphWith` 方法结合使用。 为了帮助说明这种方法,让我们参考以下模型: |
1377 | 1376 | |
1378 | 1377 | <?php | … | … |
1443 | 1442 | } |
1444 | 1443 | } |
1445 | 1444 | |
1446 | ||
1447 | ||
1445 | ||
1446 | ||
1448 | 1447 | 如果你想从单个查询的 `$with` 属性中删除一个预加载,你可以使用 `without` 方法: |
1449 | 1448 | |
1450 | 1449 | $books = Book::without('author')->get(); | … | … |
1487 | 1486 | ]); |
1488 | 1487 | }])->get(); |
1489 | 1488 | |
1490 | ||
1491 | ||
1489 | ||
1490 | ||
1492 | 1491 | 在这个例子中,Eloquent 只会预先加载未被隐藏的帖子,并且视频的 `type` 值为 `educational`。 |
1493 | 1492 | |
1494 | 1493 | <a name="lazy-eager-loading"></a> | … | … |
1538 | 1537 | |
1539 | 1538 | 在这个例子中,让我们假设 `Event` 、`Photo` 和 `Post` 模型可以创建 `ActivityFeed` 模型。此外,让我们假设 `Event` 模型属于 `Calendar` 模型,`Photo` 模型与 `Tag` 模型相关联,`Post` 模型属于 `Author` 模型。 |
1540 | 1539 | |
1541 | ||
1542 | ||
1540 | ||
1541 | ||
1543 | 1542 | 使用这些模型定义和关联关系,我们方可以检索 `ActivityFeed` 模型实例,并立即加载所有 `parentable` 模型及其各自的嵌套关系: |
1544 | 1543 | |
1545 | 1544 | $activities = ActivityFeed::with('parentable') | … | … |
1587 | 1586 | // 所有评论,包括新保存的评论... |
1588 | 1587 | $post->comments; |
1589 | 1588 | |
1590 | ||
1591 | ||
1589 | ||
1590 | ||
1592 | 1591 | <a name="the-push-method"></a> |
1593 | 1592 | #### 递归保存模型和关联数据 |
1594 | 1593 | … | … |
1623 | 1622 | ['message' => 'Another new comment.'], |
1624 | 1623 | ]); |
1625 | 1624 | |
1626 | 你还可以使用 `findOrNew`、`firstOrNew`、`firstOrCreate` 和 `updateOrCreate` 方法来 [创建和更新关系模型](https://laravel.com/docs/ | |
1625 | 你还可以使用 `findOrNew`、`firstOrNew`、`firstOrCreate` 和 `updateOrCreate` 方法来 [创建和更新关系模型](https://laravel.com/docs/8.x/eloquent#Upserts)。 | |
1627 | 1626 | |
1628 | 1627 | > 技巧:在使用 `create` 方法前,请务必确保查看过本文档的 [批量赋值](/docs/laravel/8.x/eloquent#mass-assignment) 章节。 |
1629 | 1628 | … | … |
1640 | 1639 | |
1641 | 1640 | $user->save(); |
1642 | 1641 | |
1643 | ||
1644 | ||
1642 | ||
1643 | ||
1645 | 1644 | 当移除 `belongsTo` 关联时,可以使用 `dissociate` 方法。此方法会将关联外键设置为 `null` : |
1646 | 1645 | |
1647 | 1646 | $user->account()->dissociate(); | … | … |
1692 | 1691 | |
1693 | 1692 | $user->roles()->sync([1, 2, 3]); |
1694 | 1693 | |
1695 | ||
1696 | ||
1694 | ||
1695 | ||
1697 | 1696 | 你也可以通过 ID 传递额外的附加数据到中间表: |
1698 | 1697 | |
1699 | 1698 | $user->roles()->sync([1 => ['expires' => true], 2, 3]); | … | … |
1752 | 1751 | } |
1753 | 1752 | |
1754 | 1753 | > 注意:只有使用 Eloquent 的 `save` 方法更新子模型时,才会更新父模型时间戳。 |
1754 | ||
1755 | 1755 | |
1756 |