当然,您的数据库表很可能跟另一张表相关联。例如,一篇 blog 文章可能有很多评论,或是一张订单跟下单客户相关联。 Eloquent 让管理和处理这些关联变得很容易。 Laravel 有很多种关联类型:
* 一对一
* 一对多
* 多对多
* 远层一对多关联
* 多态关联
* 多态的多对多关联
## 一对一
#### 定义一对一关联
一对一关联是很基本的关联。例如一个 User 模型会对应到一个 Phone 。 在 Eloquent 里可以像下面这样定义关联:
~~~
class User extends Model {
public function phone()
{
return $this->hasOne('App\Phone');
}
}
~~~
传到 hasOne 方法里的第一个参数是关联模型的类名称。定义好关联之后,就可以使用 Eloquent 的动态属性取得关联对象:
~~~
$phone = User::find(1)->phone;
~~~
SQL 会执行如下语句:
~~~
select * from users where id = 1
select * from phones where user_id = 1
~~~
> 注意, Eloquent 假设对应的关联模型数据库表里,外键名称是基于模型名称。在这个例子里,默认 Phone 模型数据库表会以 user_id 作为外键。如果想要更改这个默认,可以传入第二个参数到 hasOne 方法里。更进一步,您可以传入第三个参数,指定关联的外键要对应到本身的哪个字段:
~~~
return $this->hasOne('App\Phone', 'foreign_key');
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
~~~
## 定义相对的关联
要在 Phone 模型里定义相对的关联,可以使用 belongsTo 方法:
~~~
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
}
~~~
在上面的例子里, Eloquent 默认会使用 phones 数据库表的 user_id 字段查询关联。如果想要自己指定外键字段,可以在 belongsTo 方法里传入第二个参数:
~~~
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User', 'local_key');
}
}
~~~
除此之外,也可以传入第三个参数指定要参照上层数据库表的哪个字段:
~~~
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User', 'local_key', 'parent_key');
}
}
~~~
## 一对多
一对多关联的例子如,一篇 Blog 文章可能「有很多」评论。可以像这样定义关联:
~~~
class Post extends Model {
public function comments()
{
return $this->hasMany('App\Comment');
}
}
~~~
现在可以经由动态属性取得文章的评论:
~~~
$comments = Post::find(1)->comments;
~~~
如果需要增加更多条件限制,可以在调用 comments 方法后面通过链式查询条件方法:
~~~
$comments = Post::find(1)->comments()->where('title', '=', 'foo')->first();
~~~
同样的,您可以传入第二个参数到 hasMany 方法更改默认的外键名称。以及,如同 hasOne 关联,可以指定本身的对应字段:
~~~
return $this->hasMany('App\Comment', 'foreign_key');
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
~~~
## 定义相对的关联
要在 Comment 模型定义相对应的关联,可使用 belongsTo 方法:
~~~
class Comment extends Model {
public function post()
{
return $this->belongsTo('App\Post');
}
}
~~~
## 多对多
多对多关联更为复杂。这种关联的例子如,一个用户( user )可能用有很多身份( role ),而一种身份可能很多用户都有。例如很多用户都是「管理者」。多对多关联需要用到三个数据库表: users , roles ,和 role_user 。 role_user 枢纽表命名是以相关联的两个模型数据库表,依照字母顺序命名,枢纽表里面应该要有 user_id 和 role_id 字段。
可以使用 belongsToMany 方法定义多对多关系:
~~~
class User extends Model {
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
~~~
现在我们可以从 User 模型取得 roles:
~~~
$roles = User::find(1)->roles;
~~~
如果不想使用默认的枢纽数据库表命名方式,可以传递数据库表名称作为 belongsToMany 方法的第二个参数:
~~~
return $this->belongsToMany('App\Role', 'user_roles');
~~~
也可以更改默认的关联字段名称:
~~~
return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');
~~~
当然,也可以在 Role 模型定义相对的关联:
~~~
class Role extends Model {
public function users()
{
return $this->belongsToMany('App\User');
}
}
~~~
## Has Many Through 远层一对多关联
「远层一对多关联」提供了方便简短的方法,可以经由多层间的关联取得远层的关联。例如,一个 Country 模型可能通过 Users 关联到很多 Posts 模型。 数据库表间的关系可能看起来如下:
|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 的 posts。我们可以定义以下关联:
~~~
class Country extends Model {
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User');
}
}
~~~
如果想要手动指定关联的字段名称,可以传入第三和第四个参数到方法里:
~~~
class Country extends Model {
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
}
}
~~~
## 多态关联
多态关联可以用一个简单的关联方法,就让一个模型同时关联多个模型。例如,您可能想让 photo 模型同时和一个 staff 或 order 模型关联。可以定义关联如下:
~~~
class Photo extends Model {
public function imageable()
{
return $this->morphTo();
}
}
class Staff extends Model {
public function photos()
{
return $this->morphMany('App\Photo', 'imageable');
}
}
class Order extends Model {
public function photos()
{
return $this->morphMany('App\Photo', 'imageable');
}
}
~~~
## 取得多态关联对象
现在我们可以从 staff 或 order 模型取得多态关联对象:
~~~
$staff = Staff::find(1);
foreach ($staff->photos as $photo)
{
//
}
~~~
## 取得多态关联对象的拥有者
然而,多态关联真正神奇的地方,在于要从 Photo 模型取得 staff 或 order 对象时:
~~~
$photo = Photo::find(1);
$imageable = $photo->imageable;
~~~
Photo 模型里的 imageable 关联会返回 Staff 或 Order 实例,取决于这是哪一种模型拥有的照片。
多态关联的数据库表结构
为了理解多态关联的运作机制,来看看它们的数据库表结构:
|staff|
|-----|
|id - integer|
|name - string|
|orders|
|-----|
|id - integer|
|price - integer|
|photos|
|-----|
|id - integer|
|path - string|
|imageable_id - integer|
|imageable_type - string|
要注意的重点是 photos 数据库表的 imageable_id 和 imageable_type。在上面的例子里, ID 字段会包含 staff 或 order 的 ID,而 type 是拥有者的模型类名称。这就是让 ORM 在取得 imageable 关联对象时,决定要哪一种模型对象的机制。
## 多态的多对多关联
Polymorphic Many To Many Relation Table Structure 多态的多对多关联数据库表结构
除了一般的多态关联,也可以使用多对多的多态关联。例如,Blog 的 Post 和 Video 模型可以共用多态的 Tag 关联模型。首先,来看看数据库表结构:
|posts|
|-----|
|id - integer|
|name - string|
|videos|
|-----|
|id - integer|
|name - string|
|tags|
|-----|
|id - integer|
|name - string|
|taggables|
|-----|
|tag_id - integer|
|taggable_id - integer|
|taggable_type - string|
现在,我们准备好设定模型关联了。 Post 和 Video 模型都可以经由 tags 方法建立 morphToMany 关联:
~~~
class Post extends Model {
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
~~~
在 Tag 模型里针对每一种关联建立一个方法:
~~~
class Tag extends Model {
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable');
}
public function videos()
{
return $this->morphedByMany('App\Video', 'taggable');
}
}
~~~
- 前言
- 发行说明/L5新特性
- 升级向导
- 升级到 5.0.16
- 从 4.2 升级到 5.0
- 从 4.1 升级到 4.2
- 从 4.1.x 升级到 4.1.29
- 从 4.1.25 升级到 4.1.26
- 从 4.0 升级到 4.1
- 贡献向导
- 环境配置
- 安装
- 配置
- 基本功能
- 路由
- 基本路由
- CSRF 保护
- 方法欺骗
- 路由参数
- 命名路由
- 路由群组
- 路由模型绑定
- 抛出 404 错误
- 中间件
- 建立中间件
- 注册中间件
- 可终止中间件
- 控制器
- 基础控制器
- 控制器中间件
- 隐式控制器
- RESTful 资源控制器
- 请求
- 取得请求实例
- 取得输入数据
- 旧输入数据
- Cookies
- 上传文件
- 其他的请求信息
- 响应
- 基本响应
- 重定向
- 其他响应
- 响应宏
- 系统架构
- 服务提供者
- 基本提供者例子
- 注册提供者
- 缓载提供者
- 服务容器
- 基本用法
- 将接口绑定到实现
- 上下文绑定
- 标签
- 实际应用
- 容器事件
- 参考:理解PHP 依赖注入|Laravel IoC容器
- Contracts
- 为什么用 Contracts
- Contract 参考
- 如何使用 Contracts
- Facades
- 实际用法
- 建立 Facades
- 模拟 Facades
- Facade 类参考
- 请求的生命周期
- 生命周期概要
- 聚焦于服务提供者
- 应用程序结构
- 根目录
- App 目录
- 为应用程序配置命名空间
- 系统服务
- 认证
- 用户认证
- 取得经过认证的用户
- 保护路由
- HTTP 基本认证
- 忘记密码与重设
- 第三方登陆认证
- 交易
- 配置文件
- 订购方案
- 一次性付款
- Single Charges
- 免信用卡试用
- 订购转换
- 订购数量
- 取消订购
- 恢复订购
- 确认订购状态
- 处理失败订阅
- 处理其它 Stripe Webhooks
- 收据
- 缓存
- 配置
- 缓存用法
- 递增与递减
- 缓存标签
- 缓存事件
- 数据库缓存
- 集合
- Command Bus
- 建立命令
- 调用命令
- 命令队列
- 命令管道
- 核心扩展
- 管理者和工厂
- 缓存
- Session
- 认证
- 基于服务容器的扩展
- Laravel Elixir
- 安装与配置
- 使用方式
- Gulp
- Custom Tasks and Extensions
- 加密
- Envoy 任务执行器
- 安装
- 执行任务
- 多服务器
- 并行执行
- 任务宏
- 通知
- 更新 Envoy
- 错误与日志
- 配置
- 错误处理
- HTTP 异常
- 日志
- 事件
- 基本用法
- 事件处理队列
- 事件订阅者
- 文件系统与云存储
- 配置文件
- 基本用法
- 自定义文件系统
- 哈希
- 基本用法
- 辅助方法
- 数组
- 路径
- 路由
- 字符串
- 网址(URL)
- 其他
- 本地化
- 语言文件
- 基本用法
- 复数
- 验证
- 覆写扩展包的语言文件
- 邮件
- 配置
- 基本用法
- 内嵌附件
- 邮件队列
- 邮件与本地端开发
- 扩展包开发
- 视图
- 语言
- 配置文件
- 公共资源
- 发布分类文件
- 路由
- 分页
- 配置
- 使用
- 追加分页链接
- 转换至 JSON
- 队列
- 设置
- 基本用法
- 队列闭包
- 执行一个队列监听
- 常驻队列处理器
- 推送队列
- 已失败的工作
- 会话
- 配置
- 使用 Session
- 暂存数据(Flash Data)
- 数据库 Sessions
- Session 驱动
- 模板
- Blade 模板
- Blade 控制语法结构
- Blade 扩展
- 参考:@section与@yield 介绍
- 单元测试
- 定义并执行测试
- 测试环境
- 从测试调用路由
- 模拟 Facades
- 框架 Assertions
- 辅助方法
- 重置应用程序
- 表单验证
- 基本用法
- 控制器验证
- 表单请求验证
- 使用错误信息
- 错误信息 & 视图
- 可用验证规则
- 条件验证规则
- 自定义错误信息
- 自定义验证规则
- 数据库
- 使用基础
- 配置
- 读取/写入连接
- 执行查找
- 数据库事务处理
- 获取连接
- 日志记录
- 查询构造器
- Selects
- Joins
- 高级 Wheres
- 聚合
- 原生表达式
- 添加
- 更新
- 删除
- Unions
- 悲观锁定 (Pessimistic Locking)
- Eloquent ORM
- 基本用法
- 批量赋值
- 新增,更新,删除
- 软删除
- 时间戳
- 范围查询
- Global Scopes
- 关联
- 关联查询
- 预载入
- 新增关联模型
- 更新上层时间戳
- 使用枢纽表
- 集合
- 获取器和修改器
- 日期转换器
- 属性类型转换
- 模型事件
- 模型观察者
- 模型 URL 生成
- 转换成数组 / JSON
- 结构生成器
- 建立与删除数据表
- 加入字段
- 修改字段
- 修改字段名称
- 移除字段
- 检查是否存在
- 加入索引
- 外键
- 移除索引
- 移除时间戳记和软删除
- 保存引擎
- 迁移和数据填充
- 建立迁移文件
- 执行迁移
- 回滚迁移
- 数据填充
- Redis
- 配置
- 使用方式
- 管道
- 开发包
- Confide 用户身份认证
- Entrust 权限管理
- Shoppingcart 购物车
- Genertators 代码生成工具
- IDE Helper IDE助手
- Artisan 命令行工具
- 概览
- 用法
- 在命令行接口以外的地方调用命令
- 定时调用 Artisan 命令
- 开发
- 建立自定义命令
- 注册自定义命令