deMemory 2年前

修改理由:

菜单链接,人称统一

相关信息:


此投稿由 Vbm_pz 2年前 合并。

标题修改:

+ 模型关联

内容修改:

红色背景 为原始内容

绿色背景 为新增或者修改的内容

OldNewDifferences
1  
21# Eloquent: 关联
32
43- [简介](#introduction)
 
4847
4948<div class="content-list" markdown="1">
5049
51 - [一对一](#一对一)
52 - [一对多](#一对多)
53 - [多对多](#多对多)
54 - [远程一对一](#远程一对一)
55 - [远程一对多](#远程一对多)
56 - [多态一对一](#多态一对一)
57 - [多态一对多](#多态一对多)
58 - [多态多对多](#多态多对多)
 50- [一对一](#one-to-one)
 51- [一对多](#one-to-many)
 52- [多对多](#many-to-many)
 53- [远程一对一](#has-one-through)
 54- [远程一对多](#has-many-through)
 55- [多态一对一](#one-to-one-polymorphic-relations)
 56- [多态一对多](#one-to-many-polymorphic-relations)
 57- [多态多对多](#many-to-many-polymorphic-relations)
5958
6059</div>
6160
62 
63 
 61
 62
6463<a name="defining-relationships"></a>
6564## 定义关联
6665
 
101100
102101   return $this->hasOne(Phone::class, 'foreign_key');
103102
104 
105 
 103
 104
106105另外,Eloquent 假设外键的值是与父模型的主键(Primary Key)相同的。换句话说,Eloquent 将会通过 `Phone` 记录的 `user_id` 列中查找与用户表的 `id` 列相匹配的值。如果你希望使用自定义的主键值,而不是使用 `id` 或者模型中的 `$primaryKey` 属性,你可以给 `hasOne` 方法传递第三个参数:
107106
108107   return $this->hasOne(Phone::class, 'foreign_key', 'local_key');
 
142141       return $this->belongsTo(User::class, 'foreign_key');
143142   }
144143
145 
146 
 144
 145
147146如果父模型的主键未使用 `id` 作为字段名,或者你想要使用其他的字段来匹配相关联的模型,那么你可以向 `belongsTo` 方法传递第三个参数,这个参数是在父模型中自己定义的字段名称:
148147
149148   /**
 
189188       // ...
190189   }
191190
192 
193 
 191
 192
194193由于所有的关系都可以看成是查询构造器,所以你也可以通过链式调用的方式,在 `comments` 方法中继续添加条件约束:
195194
196195   $comment = Post::find(1)->comments()
 
237236在上面这个例子中,Eloquent 将会尝试寻找 `Post` 模型中的 `id` 字段与 `Comment` 模型中的 `post_id` 字段相匹配。
238237
239238Eloquent 通过检查关联方法的名称,从而在关联方法名称后面加上 `_` ,然后再加上父模型 (Post)的主键名称,以此来作为默认的外键名。因此,在上面这个例子中,Eloquent 将会默认 `Post` 模型在 `comments` 表中的外键是 `post_id`。
240 
241 
 239
 240
242241但是,如果你的外键不遵循这种约定的话,那么你可以传递一个自定义的外键名来作为 `belongsTo` 方法的第二个参数:
243242
244243   /**
 
294293       });
295294   }
296295
297 
298 
 296
 297
299298<a name="querying-belongs-to-relationships"></a>
300299#### 查询所属关系
301300
 
334333}
335334```
336335
337 
338 
 336
 337
339338同样,你可以定义一个方法来检索 「oldest」或第一个相关模型:
340339
341340```php
 
387386}
388387```
389388
390 
391 
 389
 390
392391<a name="has-one-through"></a>
393392### 远程一对一
394393
 
442441return $this->throughCars()->hasOwner();
443442```
444443
445 
446 
 444
 445
447446<a name="has-one-through-key-conventions"></a>
448447#### 键名约定
449448
 
495494       environment_id - integer
496495       commit_hash - string
497496
498 
499 
 497
 498
500499既然我们已经检查了关系的表结构,现在让我们在 `Project` 模型上定义该关系:
501500
502501   <?php
 
550549       }
551550   }
552551
553 
554 
 552
 553
555554或者,如前所述,如果涉及关系的相关关系已经在所有模型上定义,你可以通过调用 `through` 方法并提供这些关系的名称来定义「has-many-through」关系。这种方法的优点是可以重复使用已经定义在现有关系上的键约定:
556555
557556```php
 
586585       user_id - integer
587586       role_id - integer
588587
589 
590 
 588
 589
591590<a name="many-to-many-model-structure"></a>
592591#### 模型结构
593592
 
633632
634633   return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');
635634
636 
637 
 635
 636
638637<a name="many-to-many-defining-the-inverse-of-the-relationship"></a>
639638#### 定义反向关联
640639
 
679678
680679   return $this->belongsToMany(Role::class)->withPivot('active', 'created_by');
681680
682 
683 
 681
 682
684683如果你想让中间表自动维护 `created_at` 和 `updated_at` 时间戳,那么在定义关联时附加上 `withTimestamps` 方法即可:
685684
686685   return $this->belongsToMany(Role::class)->withTimestamps();
 
737736                   ->as('subscriptions')
738737                   ->wherePivotNotNull('expired_at');
739738
740 
741 
 739
 740
742741<a name="ordering-queries-via-intermediate-table-columns"></a>
743742### 通过中间表字段排序
744743
 
791790
792791
793792<a name="custom-pivot-models-and-incrementing-ids"></a>
794 
795 
 793
 794
796795#### 自定义中间模型和自增 ID
797796
798797如果你用一个自定义的中继模型定义了多对多的关系,而且这个中继模型拥有一个自增的主键,你应当确保这个自定义中继模型类中定义了一个 `incrementing` 属性且其值为 `true`。
 
833832
834833请注意 `images` 表上的 `imageable_id` 和 `imageable_type` 两列。`imageable_id` 列将包含帖子或用户的ID值,而 `imageable_type` 列将包含父模型的类名。`imageable_type` 列用于 Eloquent 在访问 `imageable` 关联时确定要返回哪种类型的父模型。在本例中,该列将包含 `App\Models\Post` 或 `App\Models\User`。
835834
836 
837 
 835
 836
838837<a name="one-to-one-polymorphic-model-structure"></a>
839838#### 模型结构
840839
 
920919       return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id');
921920   }
922921
923 
924 
 922
 923
925924<a name="one-to-many-polymorphic-relations"></a>
926925### 一对多(多态)
927926
 
10101009       // ...
10111010   }
10121011
1013 
1014 
 1012
 1013
10151014你还可以通过访问执行对 `morphTo` 的调用的方法名来从多态模型获取其所属模型。在我们的例子中,这就是 `Comment` 模型上的 `commentable` 方法。因此,我们将以动态属性的形式访问该方法:
10161015
10171016   use App\Models\Comment;
 
10501049```
10511050
10521051默认情况下,`latestOfMany` 和 `oldestOfMany` 方法将基于模型的主键(必须可排序)检索最新或最旧的相关模型。但是,有时你可能希望使用不同的排序条件从较大的关系中检索单个模型。
1053 
1054 
 1052
 1053
10551054例如,使用 `ofMany` 方法,可以检索用户点赞最高的图像。`ofMany` 方法接受可排序列作为其第一个参数,以及在查询相关模型时应用哪个聚合函数(`min` 或 `max`):
10561055
10571056```php
 
11011100
11021101接下来,我们可以定义模型之间的关联。`Post` 和 `Video` 模型都将包含一个 `tags` 方法,该方法调用了基础 Eloquent 模型类提供的 `morphToMany` 方法。
11031102
1104 
1105 
 1103
 1104
11061105`morphToMany` 方法接受相关模型的名称以及“关系名称”。根据我们分配给中间表的名称及其包含的键,我们将将关系称为 「taggable」:
11071106
11081107   <?php
 
11591158<a name="many-to-many-polymorphic-retrieving-the-relationship"></a>
11601159#### 获取关联
11611160
1162 一旦定义了数据库表和模型,您就可以通过模型访问关系。 例如,要访问帖子的所有标签,您可以使用 `tags` 动态关系属性:
 1161一旦定义了数据库表和模型,你就可以通过模型访问关系。 例如,要访问帖子的所有标签,你可以使用 `tags` 动态关系属性:
11631162
11641163   use App\Models\Post;
11651164
 
11691168       // ...
11701169   }
11711170
1172 
1173 
 1171
 1172
11741173还可以访问执行 `morphedByMany` 方法调用的方法名来从多态模型获取其所属模型。在这个示例中,就是 `Tag` 模型的 `posts` 或 `videos` 方法。可以像动态属性一样访问这些方法:
11751174
11761175   use App\Models\Tag;
 
12121211> **注意**
12131212> 向现有应用程序添加「变形映射」时,数据库中仍包含完全限定类的每个可变形 `*_type` 列值都需要转换为其「映射」名称。
12141213
1215 
1216 
 1214
 1215
12171216<a name="dynamic-relationships"></a>
12181217### 动态关联
12191218
 
12641263
12651264   $user->posts()->where('active', 1)->get();
12661265
1267 
1268 
 1266
 1267
12691268你可以在关联上使用任意的 [查询构造器]([查询构造器 | 《Laravel 10 中文文档》 (learnku.com)](https://learnku.com/docs/laravel/10.x/queriesmd/14883)) 方法,所以一定要阅读查询构造器的文档,了解它的所有方法,这会对你非常有用。
12701269
12711270<a name="chaining-orwhere-clauses-after-relationships"></a>
 
13181317       // ...
13191318   }
13201319
1321 
1322 
 1320
 1321
13231322动态属性是 「懒加载」 的,只有实际访问到才会加载关联数据。因此,通常用 [预加载](#eager-loading) 来准备模型需要用到的关联数据。预加载能大量减少因加载模型关联执行的 SQL 语句。
13241323
13251324<a name="querying-relationship-existence"></a>
 
13591358> **注意**
13601359> Eloquent 目前不支持跨数据库查询关系是否存在。 这些关系必须存在于同一数据库中。
13611360
1362 
1363 
 1361
 1362
13641363<a name="inline-relationship-existence-queries"></a>
13651364#### 内联关系存在查询
13661365
 
14011400       $query->where('banned', 0);
14021401   })->get();
14031402
1404 
1405 
 1403
 1404
14061405<a name="querying-morph-to-relationships"></a>
14071406### 查询多态关联
14081407
 
14561455       $query->where('title', 'like', 'foo%');
14571456   })->get();
14581457
1459 
1460 
 1458
 1459
14611460<a name="aggregating-related-models"></a>
14621461## 聚合相关模型
14631462
 
15141513       $query->where('rating', 5);
15151514   }])
15161515
1517 
1518 
 1516
 1517
15191518<a name="relationship-counting-and-custom-select-statements"></a>
15201519#### 关联计数和自定义查询字段
15211520
 
15671566
15681567在这个例子中,我们假设 `Photo` 和 `Post` 模型可以创建 `ActivityFeed` 模型。 我们将假设 `ActivityFeed`模型定义了一个名为`parentable`的多态关联关系,它允许我们为给定的 `ActivityFeed` 实例检索父级 `Photo` 或 `Post` 模型。 此外,让我们假设 `Photo` 模型有很多 `Tag` 模型、`Post` 模型有很多 `Comment` 模型。
15691568
1570 
1571 
 1569
 1570
15721571假如我们想要检索 `ActivityFeed` 实例并为每个 `ActivityFeed` 实例预先加载 `parentable` 父模型。 此外,我们想要检索与每张父照片关联的标签数量以及与每个父帖子关联的评论数量:
15731572
15741573   use Illuminate\Database\Eloquent\Relations\MorphTo;
 
16261625       echo $book->author->name;
16271626   }
16281627
1629 
1630 
 1628
 1629
16311630该循环将执行一个查询以检索数据库表中的所有书籍,然后对每本书执行另一个查询以检索该书的作者。 因此,如果我们有 25 本书,上面的代码将运行 26 个查询:一个查询原本的书籍信息,另外 25 个查询来检索每本书的作者。
16321631
16331632值得庆幸的是,我们可以使用预加载将这个操作减少到两个查询。 在构建查询时,可以使用 `with` 方法指定应该预加载哪些关系:
 
16691668       ],
16701669   ])->get();
16711670
1672 
1673 
 1671
 1672
16741673<a name="nested-eager-loading-morphto-relationships"></a>
16751674#### 嵌套预加载 `morphTo` 关联
16761675
 
17171716> **注意**
17181717> 使用此功能时,应始终在要检索的列列表中包括 `id` 列和任何相关的外键列。
17191718
1720 
1721 
 1719
 1720
17221721<a name="eager-loading-by-default"></a>
17231722#### 默认预加载
17241723
 
17681767<a name="constraining-eager-loads"></a>
17691768### 约束预加载
17701769
1771 有时,你可能希望预加载一个关联,同时为预加载查询添加额外查询条件。可以通过将一个关联数组传递给 `with` 方法来实现这一点,其中数组键是关联名称,数组值是一个闭包,它为预先加载查询添加了额外的约束:
 1770有时,你可能希望预加载一个关联,同时为预加载查询添加额外查询条件。可以通过将一个关联数组传递给 `with` 方法来实现这一点,其中数组键是关联名称,数组值是一个闭包,它为预先加载查询添加了额外的约束:
17721771
17731772   use App\Models\User;
17741773
 
17851784> **注意**
17861785> 在约束预加载时,不能使用 limit 和 take 查询构造器方法。
17871786
1788 
1789 
 1787
 1788
17901789<a name="constraining-eager-loading-of-morph-to-relationships"></a>
17911790#### `morphTo` 关联预加载添加约束
17921791
 
18331832       $books->load('author', 'publisher');
18341833   }
18351834
1836 
1837 
 1835
 1836
18381837如果要在渴求式加载的查询语句中进行条件约束,可以通过数组的形式去加载,键为对应的关联关系,值为 `Closure` 闭包函数,该闭包的参数为一个查询实例:
18391838
18401839   $author->load(['books' => function (Builder $query) {
 
18481847<a name="nested-lazy-eager-loading-morphto"></a>
18491848#### 嵌套延迟预加载 & `morphTo`
18501849
1851 如果要预加载 `morphTo` 关系,以及该关系可能返回的各种实体上的嵌套关系,可以使用 `loadMorph` 方法。
 1850如果要预加载 `morphTo` 关系,以及该关系可能返回的各种实体上的嵌套关系,可以使用 `loadMorph` 方法。
18521851
18531852这个方法接受 `morphTo` 关系的名称作为它的第一个参数,第二个参数接收模型数组、关系数组。例如:
18541853
 
18801879           Post::class => ['author'],
18811880       ]);
18821881
1883 
1884 
 1882
 1883
18851884<a name="preventing-lazy-loading"></a>
18861885### 防止延迟加载
18871886
 
19301929
19311930   $post->comments()->save($comment);
19321931
1933 
1934 
 1932
 1933
19351934注意,我们没有将 `comments` 关联作为动态属性访问,相反,我们调用了 `comments` 方法来来获得关联实例, `save` 方法会自动添加适当的 `post_id` 值到新的 `Comment` 模型中。
19361935
19371936如果需要保存多个关联模型,你可以使用 `saveMany` 方法:
 
19811980       'message' => 'A new comment.',
19821981   ]);
19831982
1984 
1985 
 1983
 1984
19861985你还可以使用 `createMany` 方法去创建多个关联模型:
19871986
19881987   $post = Post::find(1);
 
20122011<a name="updating-belongs-to-relationships"></a>
20132012### Belongs To 关联
20142013
2015 如果您想将子模型分配给新的父模型,您可以使用 `associate` 方法。在这个例子中,`User` 模型定义了一个与 `Account` 模型的 `belongsTo` 关系。 这个 `associate` 方法将在子模型上设置外键:
 2014如果你想将子模型分配给新的父模型,你可以使用 `associate` 方法。在这个例子中,`User` 模型定义了一个与 `Account` 模型的 `belongsTo` 关系。 这个 `associate` 方法将在子模型上设置外键:
20162015
20172016   use App\Models\Account;
20182017
 
20222021
20232022   $user->save();
20242023
2025 要从子模型中删除父模型,可以使用 `dissociate` 方法。此方法会将关联外键设置为 `null`:
 2024要从子模型中删除父模型,可以使用 `dissociate` 方法。此方法会将关联外键设置为 `null`:
20262025
20272026   $user->account()->dissociate();
20282027
 
20422041
20432042   $user->roles()->attach($roleId);
20442043
2045 
2046 
 2044
 2045
20472046在将关系附加到模型时,还可以传递一组要插入到中间表中的附加数据:
20482047
20492048   $user->roles()->attach($roleId, ['expires' => $expires]);
 
20782077
20792078   $user->roles()->sync([1 => ['expires' => true], 2, 3]);
20802079
2081 如果想为每个同步的模型 IDs 插入相同的中间表,你可以使用 `syncWithPivotValues` 方法:
 2080如果想为每个同步的模型 IDs 插入相同的中间表,你可以使用 `syncWithPivotValues` 方法:
20822081
20832082   $user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);
20842083
 
20862085
20872086   $user->roles()->syncWithoutDetaching([1, 2, 3]);
20882087
2089 
2090 
 2088
 2089
20912090<a name="toggling-associations"></a>
20922091#### 切换关联
20932092
 
20952094
20962095   $user->roles()->toggle([1, 2, 3]);
20972096
2098 还可以将附加的中间表值与ID 一起传递:
 2097还可以将附加的中间表值与ID 一起传递:
20992098
21002099   $user->roles()->toggle([
21012100       1 => ['expires' => true],
 
21462145   }
21472146
21482147> **注意**:只有使用 `Eloquent` 的 `save` 方法更新子模型时,才会触发更新父模型时间戳。
 2148  
21492149
2150