多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### [](https://octobercms.com/docs/database/relations#one-to-one)一对一 一对一关系是非常基本的关系。例如,`User`模型可能与关联`Phone`。为了定义这种关系,我们向模型`phone`的`$hasOne`属性添加一个条目`User`。 ~~~ <?php namespace Acme\Blog\Models; use Model; class User extends Model { public $hasOne = [ 'phone' => 'Acme\Blog\Models\Phone' ]; } ~~~ 定义关系后,我们可以使用相同名称的model属性检索相关记录。这些属性是动态的,使您可以像访问模型中的常规属性一样访问它们: ~~~ $phone = User::find(1)->phone; ~~~ 模型基于模型名称假定关系的外键。在这种情况下,将`Phone`自动假定模型具有`user_id`外键。如果您希望覆盖此约定,则可以将`key`参数传递给定义: ~~~ public $hasOne = [ 'phone' => ['Acme\Blog\Models\Phone', 'key' => 'my_user_id'] ]; ~~~ 此外,该模型还假定外键的值应`id`与父级的列匹配。换句话说,它将在记录的`id`列中查找用户列的值。如果您希望该关系使用以外的值,则可以将参数传递给定义:`user_id``Phone``id``otherKey` ~~~ public $hasOne = [ 'phone' => ['Acme\Blog\Models\Phone', 'key' => 'my_user_id', 'otherKey' => 'my_id'] ]; ~~~ #### 定义关系的逆关系 现在,我们可以从中访问`Phone`模型了`User`。让我们做相反的事情,并在`Phone`模型上定义一个关系,使我们可以访问`User`拥有手机的。我们可以`hasOne`使用`$belongsTo`属性定义关系的逆: ~~~ class Phone extends Model { public $belongsTo = [ 'user' => 'Acme\Blog\Models\User' ]; } ~~~ 在上面的例子中,模型将尝试匹配的`user_id`从`Phone`模型到一个`id`上`User`的模型。它通过检查关系定义的名称并在名称后缀来确定默认的外键名称`_id`。但是,如果`Phone`模型上的外键不是`user_id`,则可以使用`key`定义中的参数传递自定义键名称: ~~~ public $belongsTo = [ 'user' => ['Acme\Blog\Models\User', 'key' => 'my_user_id'] ]; ~~~ 如果您的父模型不用`id`作其主键,或者您希望将子模型连接到其他列,则可以将`otherKey`参数传递给指定父表的自定义键的定义: ~~~ public $belongsTo = [ 'user' => ['Acme\Blog\Models\User', 'key' => 'my_user_id', 'otherKey' => 'my_id'] ]; ~~~ #### 默认型号 的`belongsTo`关系,您可以定义如果给定的关系是将要返回默认模式`null`。此模式通常称为[空对象模式](https://en.wikipedia.org/wiki/Null_Object_pattern),可以帮助删除代码中的条件检查。在以下示例中,如果该帖子未附加,则该`user`关系将返回一个空`Acme\Blog\Models\User`模型`user`: ~~~ public $belongsTo = [ 'user' => ['Acme\Blog\Models\User', 'default' => true] ]; ~~~ 要使用属性填充默认模型,可以将数组传递给`default`参数: ~~~ public $belongsTo = [ 'user' => [ 'Acme\Blog\Models\User', 'default' => ['name' => 'Guest'] ] ]; ~~~ ### [](https://octobercms.com/docs/database/relations#one-to-many)一对多 一对多关系用于定义单个模型拥有任意数量其他模型的关系。例如,博客帖子可能有无限数量的评论。与所有其他关系一样,定义了一对多关系`$hasMany`,在模型的属性中添加了一个条目: ~~~ class Post extends Model { public $hasMany = [ 'comments' => 'Acme\Blog\Models\Comment' ]; } ~~~ 请记住,模型将自动确定模型上正确的外键列`Comment`。按照惯例,它将采用拥有模型的“ snake case”名称,并在其后缀`_id`。因此,对于此示例,我们可以假定`Comment`模型上的外键为`post_id`。 定义关系后,我们可以通过访问`comments`属性来访问评论集合。请记住,由于模型提供了“动态属性”,因此我们可以像在模型上将它们定义为属性一样访问关系: ~~~ $comments = Post::find(1)->comments; foreach ($comments as $comment) { // } ~~~ 当然,由于所有关系也都可以用作查询生成器,因此可以通过调用`comments`方法并继续将条件链接到查询上来为注释添加更多约束: ~~~ $comments = Post::find(1)->comments()->where('title', 'foo')->first(); ~~~ 像该`hasOne`关系一样,您还可以通过分别在定义上传递`key`和`otherKey`参数来覆盖外键和本地键: ~~~ public $hasMany = [ 'comments' => ['Acme\Blog\Models\Comment', 'key' => 'my_post_id', 'otherKey' => 'my_id'] ]; ~~~ #### 定义关系的逆关系 现在我们可以访问帖子的所有评论,让我们定义一个关系以允许评论访问其父帖子。要定义`hasMany`关系的逆关系,请`$belongsTo`在子模型上定义属性: ~~~ class Comment extends Model { public $belongsTo = [ 'post' => 'Acme\Blog\Models\Post' ]; } ~~~ 一旦定义了关系,我们可以通过访问“动态属性”来检索`Post`模型:`Comment``post` ~~~ $comment = Comment::find(1); echo $comment->post->title; ~~~ 在上面的例子中,模型将尝试匹配的`post_id`从`Comment`模型到一个`id`上`Post`的模型。它通过检查关系的名称并为它加后缀来确定默认的外键名称`_id`。但是,如果`Comment`模型上的外键不是`post_id`,则可以使用以下`key`参数传递自定义键名称: ~~~ public $belongsTo = [ 'post' => ['Acme\Blog\Models\Post', 'key' => 'my_post_id'] ]; ~~~ 如果您的父模型不用`id`作其主键,或者您希望将子模型连接到其他列,则可以将`otherKey`参数传递给指定父表的自定义键的定义: ~~~ public $belongsTo = [ 'post' => ['Acme\Blog\Models\Post', 'key' => 'my_post_id', 'otherKey' => 'my_id'] ]; ~~~ ### [](https://octobercms.com/docs/database/relations#many-to-many)多对多 多对多关系比`hasOne`和`hasMany`关系稍微复杂。这种关系的一个示例是具有多个角色的用户,其中这些角色也由其他用户共享。例如,许多用户可能具有“管理员”角色。要定义这种关系,需要三个数据库表:`users`,`roles`,和`role_user`。该`role_user`表是从相关模型名称的字母顺序得出的,并且包含`user_id`和`role_id`列。 下面的示例显示了用于创建联接表的[数据库表结构](https://octobercms.com/docs/plugin/updates#migration-files)。 ~~~ Schema::create('role_user', function($table) { $table->integer('user_id')->unsigned(); $table->integer('role_id')->unsigned(); $table->primary(['user_id', 'role_id']); }); ~~~ 定义了多对多关系`$belongsToMany`,在模型类的属性上添加了一个条目。例如,让`roles`我们在`User`模型上定义方法: ~~~ class User extends Model { public $belongsToMany = [ 'roles' => 'Acme\Blog\Models\Role' ]; } ~~~ 定义关系后,您可以使用`roles`dynamic属性访问用户的角色: ~~~ $user = User::find(1); foreach ($user->roles as $role) { // } ~~~ 当然,像所有其他关系类型一样,您可以调用该`roles`方法以继续将查询约束链接到该关系上: ~~~ $roles = User::find(1)->roles()->orderBy('name')->get(); ~~~ 如前所述,为了确定关系的联接表的表名,模型将按字母顺序联接两个相关的模型名。但是,您可以随意重写此约定。您可以通过将`table`参数传递给`belongsToMany`定义来实现: ~~~ public $belongsToMany = [ 'roles' => ['Acme\Blog\Models\Role', 'table' => 'acme_blog_role_user'] ]; ~~~ 除了自定义联接表的名称之外,还可以通过将其他参数传递给`belongsToMany`定义来自定义表上键的列名。该`key`参数是要为其定义关系模型的外键名称,而`otherKey`参数是要加入到模型的外键名: ~~~ public $belongsToMany = [ 'roles' => [ 'Acme\Blog\Models\Role', 'table' => 'acme_blog_role_user', 'key' => 'my_user_id', 'otherKey' => 'my_role_id' ] ]; ~~~ #### 定义关系的逆关系 要定义多对多关系的逆关系,只需`$belongsToMany`在相关模型上放置另一个属性即可。继续我们的用户角色示例,让我们`users`在`Role`模型上定义关系: ~~~ class Role extends Model { public $belongsToMany = [ 'users' => 'Acme\Blog\Models\User' ]; } ~~~ 如您所见,该关系的定义与其`User`对应关系完全相同,只是简单地引用了`Acme\Blog\Models\User`模型。由于我们正在重用该`$belongsToMany`属性,因此在定义多对多关系的逆关系时,所有常用的表和键自定义选项均可用。 #### 检索中间表列 如您所知,使用多对多关系需要一个中间联接表。模型提供了一些与该表进行交互的非常有用的方法。例如,假设我们的`User`对象有许多`Role`与之相关的对象。访问此关系后,我们可以使用`pivot`模型上的属性访问中间表: ~~~ $user = User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } ~~~ 请注意,`Role`我们检索的每个模型都会自动分配一个`pivot`属性。此属性包含表示中间表的模型,并且可以像其他任何模型一样使用。 默认情况下,只有模型关键点会出现在`pivot`对象上。如果数据透视表包含额外的属性,则在定义关系时必须指定它们: ~~~ public $belongsToMany = [ 'roles' => [ 'Acme\Blog\Models\Role', 'pivot' => ['column1', 'column2'] ] ]; ~~~ 如果希望您的数据透视表具有自动维护`created_at`和`updated_at`时间戳记,请使用`timestamps`关系定义上的参数: ~~~ public $belongsToMany = [ 'roles' => ['Acme\Blog\Models\Role', 'timestamps' => true] ]; ~~~ 这些是`belongsToMany`关系支持的参数: | 论据 | 描述 | | --- | --- | | **table** | 联接表的名称。 | | **key** | 定义模型的关键列名称(在数据透视表内部)。默认值由型号名称和`_id`后缀组合而成,即`user_id` | | **parentKey** | 定义模型的键列名称(在定义模型表内部)。默认值:id | | **otherKey** | 相关模型的关键列名称(在数据透视表内部)。默认值由型号名称和`_id`后缀组合而成,即`role_id` | | **relatedKey** | 相关模型的关键列名称(在相关模型表内部)。默认值:id | | **pivot** | 在联接表中找到的一组枢轴列,可通过访问属性`$model->pivot`。 | | **pivotModel** | 指定访问枢纽关系时要返回的自定义模型类。默认为`October\Rain\Database\Pivot`。 | | **timestamps** | 如果为true,则联接表应包含`created_at`和`updated_at`列。默认值:false | ### [](https://octobercms.com/docs/database/relations#has-many-through)有很多通过 拥有多次关系为通过中间关系访问远处的关系提供了方便的捷径。例如,一个`Country`模型可能`Post`通过中间`User`模型具有许多模型。在此示例中,您可以轻松地收集给定国家/地区的所有博客文章。让我们看一下定义此关系所需的表: ~~~ countries id - integer name - string users id - integer country_id - integer name - string posts id - integer user_id - integer title - string ~~~ 尽管`posts`不包含`country_id`列,但该`hasManyThrough`关系可通过访问该国家/地区的帖子`$country->posts`。要执行此查询,模型将检查`country_id`中间`users`表上的。找到匹配的用户标识后,将使用它们来查询`posts`表。 现在我们已经检查了关系的表结构,让我们在`Country`模型上定义它: ~~~ class Country extends Model { public $hasManyThrough = [ 'posts' => [ 'Acme\Blog\Models\Post', 'through' => 'Acme\Blog\Models\User' ], ]; } ~~~ 传递给该`$hasManyThrough`关系的第一个参数是我们希望访问的最终模型的名称,而`through`参数是中间模型的名称。 执行关系查询时,将使用典型的外键约定。如果您想自定义关系的钥匙,你可以通过他们的`key`,`otherKey`和`throughKey`参数的`$hasManyThrough`定义。该`key`参数是在中间模型外键的名称,`throughKey`参数是最终的模型外键的名称,而`otherKey`为本地密钥。 ~~~ public $hasManyThrough = [ 'posts' => [ 'Acme\Blog\Models\Post', 'key' => 'my_country_id', 'through' => 'Acme\Blog\Models\User', 'throughKey' => 'my_user_id', 'otherKey' => 'my_id' ], ]; ~~~ ### [](https://octobercms.com/docs/database/relations#has-one-through)一通 一对一关系通过单个中间关系链接模型。例如,如果每个供应商有一个用户,并且每个用户与一个用户历史记录相关联,则供应商模型可以通过用户访问用户的历史。让我们看一下定义此关系所需的数据库表: ~~~ users id - integer supplier_id - integer suppliers id - integer history id - integer user_id - integer ~~~ 尽管该`history`表不包含`supplier_id`列,但是该`hasOneThrough`关系可以提供对供应商模型的用户历史记录的访问。现在我们已经检查了关系的表结构,让我们在`Supplier`模型上定义它: ~~~ class Supplier extends Model { public $hasOneThrough = [ 'userHistory' => [ 'Acme\Supplies\Model\History', 'through' => 'Acme\Supplies\Model\User' ], ]; } ~~~ 传递给该`$hasOneThrough`属性的第一个数组参数是我们希望访问的最终模型的名称,而`through`键是中间模型的名称。 执行关系查询时,将使用典型的外键约定。如果您想自定义关系的钥匙,你可以通过他们的`key`,`otherKey`和`throughKey`参数的`$hasManyThrough`定义。该`key`参数是在中间模型外键的名称,`throughKey`参数是最终的模型外键的名称,而`otherKey`为本地密钥。 ~~~ public $hasOneThrough = [ 'userHistory' => [ 'Acme\Supplies\Model\History', 'key' => 'supplier_id', 'through' => 'Acme\Supplies\Model\User' 'throughKey' => 'user_id', 'otherKey' => 'id' ], ]; ~~~