模型关联
Eloquent关联在模型中以方法的形式呈现,提供了强大的链式调用和查询功能。可以分为一对一、一对多、多对多、远程一对多、多态一对一、多态一对多、多态多对多、自定义几种。
一对一
用户 User 和手机 Phone 是关联模型,用户拥有手机,手机属于用户。
关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
//获取用户关联的手机
public function phone()
{
return $this->hasOne('App\Phone');
}
}
一旦定义了关联,就可以使用Eloquent的动态属性获取相关的记录,如下:
$phone = User::find(1)->phone;
Eloquent会基于模型决定外键名称,会自动假设 Phone 模型有一个 user_id 字段,如果想要覆盖这个决定,可以传递 hasOne 第二个参数,如下:
return $this->hasOne('App\Phone', 'foreign_key');
如果Eloquent外键值与父级id不匹配,可以传递 hasOne 第三个参数,如下:
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
反向关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
//获得拥有此手机的用户
public function user()
{
return $this->belongsTo('App\User');
}
}
一对多
文章 Post 和评论 Comment 是关联模型,对一篇文章可以发表很多评论。
关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//获取文章的评论
public function comments()
{
return $this->hasMany('App\Comment');
}
}
访问 Post 文章的 comments 的属性获取评论的集合,如下:
$comments = App\Comment::find(1)->comments();
foreach ( $comments as $comment) {
//
}
还可以添加过滤,如下:
$comments = App\Comment::find(1)->comments()->where('title', 'xxx')->first();
反向关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
//获取评论所属文章
public function post()
{
return $this->belongsTo('App\Post');
}
}
定义好之后,可以通过 Comment 模型的 post 动态属性来访问,如下:
$comment = App\Comment::find(1);
echo $comment->post->title;
多对多
用户 User ,角色 Role (中间模型为 UserRole )模型可以认为是多对多关联,用户可以有多个角色,每个角色可以为多个用户所有。对应的数据分别为 users ,roles ,role_user 。其中 role_user 是按两个模型字母排序,包含 user_id 和 role_id 两个字段。
关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
//用户的角色
public funciton roles()
{
return $this->belongsToMany('App\Role');
}
}
定义好 roles 动态属性后,可以获取用户的角色,如下:
$user = App\User::find(1);
foreach ( $user->roles as $role ) {
//
}
也可以使用 roles 方法,如下:
$roles = App\User::find(1)->roles()->orderBy('name')->get();
如果关联表名不符合默认规则,可以传递 belongsToMany 第二个参数,如下:
return $this->belongsToMany('App\Role', 'role_user');
如果关联表两个字段不符合默认规则,可以传递 belongsToMany 第三个、第四个参数,如下:
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
定义反向关联
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
//拥有此角色的用户
public function users()
{
return $this->belongsToMany('App\User');
}
}
取中间表字段
在获取关联对象后,可以使用模型的 pivot 访问中间表数据,如下:
$user = App\User::find(1);
foreach ( $user->roles as $role ) {
echo $role->pivot->created_at;
}
默认情况下,pivot 只包含了两个模型的主键,如果中间表还有其它的字段,需要在关联时明确指出,如下:
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
如果想自动维护中间表的 created_at 和 updated_at 时间戳,需在关联时附加 withTimestamps 方法:
return $this->belongsToMany('App\Role')->withTimestamps();
自定义中间表模型
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User')->using('App\UserRole');
}
}
自定义时 UserRole 模型,如下:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations/Pivot;
class UserRole extends Pivot
{
//...
}
远程一对多
国家 Country 有很多用户 User , 用户有很多博客 Post,对应的数据表结构如下:
countries
id:主键id
name:国家名称
...
users
id:主键id
country_id:国家id
name:姓名
...
posts
id:主键id
user_id:用户id
title:博客题目
...
代码实现:
<?php
namespace App;
use Illuminate\Database\Eloquent\Modell;
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',
'id',
'id'
}
}
多态一对一
一对一的多态关联类似于简单的一对一关联,但目标模型在当个关联上属于多个其它模型。博客 Post ,用户 User 共享与图像 Image 模型的多态一对一关联。
数据表结构
posts
id:主键
title:标题
body:文章内容
...
users
id:主键
title:姓名
url:头像
...
images
id:主键
body:图片
imageable_id:关联主键
imageable_type:关联模型
...
images 表 imageable_id 存储博客或用户id,imageable_type 存储所属模型的类名。imageable_type 来决定我们访问关联模型时,要返回的父模型的类型。
模型结构
<?php
use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
//获取此图像所属的模型
public function imageable()
{
return $this->morphTo();
}
}
class Post extends Model
{
//获取此博客的图像
public function image()
{
return $this->morphOne('App\Image', 'imageable');
}
}
class User extends Model
{
//获取此用户的图像
public function image()
{
return $this->morphOne('App\Image', 'imageable');
}
}
获取多态关联
$posts = App\Post::find(1);
$image = $posts->image;
$image = App\Image::find(1);
$imageable = $image->imageable;
多态一对多
一对多的多态关联类似于简单的一对多关联,但目标在单个模型上可以属于多个其它模型。文章 Post 和视频 video 的评论 comment 可以看作是一个一对多的多态关联。
数据表结构
posts
id:主键
...
videos
id:主键
...
comments
id:主键
commentable_id
commentable_type
...
comments 数据表 commentable_id 字段存储文章或视频的主键,commentable_type 存储所属模型的类名。commentable_type 来决定我们访问关联模型时,返回父模型的类型。
模型结构
<?php
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
//获取拥有此评论的模型
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
//获取此文章的所有评论
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
class Video extends Model
{
//获取此视频的评论
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
获取多态关联
$posts = App\Post::find(1);
foreach ($posts->comments as $comment)
{
//
}
$comment = App\Comment::find(1);
$commentable = $comment->commentable;
多态多对多
文章 Post 和视频 Video 共享标签 Tag 模型。
数据表结构
posts
id:主键
...
videos
id:主键
...
tags
id:主键
...
taggables
tag_id:标签ID
taggable_id:关联模型ID
taggable_type:关联模型类名
...
模型结构
<?php
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//获取文章标签
public function tags()
{
return $this->morphToMany('App\Tag', ''taggable);
}
}
class Video extends Model
{
//获取视频标签
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
class Tag extends Model
{
//获取拥有此标签的文章
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable');
}
//获取拥有此标签的视频
public function videos()
{
return $this->morphedByMany('App\Video', 'taggable');
}
}
获取关联
$post = App\Post::find(1);
foreach ($post->tags as $tag)
{
//
}
$tag = App\Tag::find(1);
foreach($tag->videos as $video)
{
//
}
查询关联关系是否存在
//获取至少有一条评论的文章
$posts = App\Post::has('comments')->get();
//获取至少有三条评论的文章
$posts = App\Post::has('comments', '>=', 3)->get();
//获取有一条评论的文章,并加载评论的投票信息
$posts = App\Post::has('comments.votes')->get();
//获取至少有一条评论的文章,并且以foo开头
$posts = App\Post::whereHas(function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
关联计数
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post)
{
echo $post->comments_count;
}
$posts = App\Post::withCount('votes', 'comments' => function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;
$posts = App\Post::withCount(
[
'comments',
'comments as pending_comments_count' => function ($query) {
$query->where('approved', false);
})
])->get();
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;
$posts = App\Post::select(['title','body'])->withCount('comments')->get();
echo $posts[0]->title;
echo $posts[0]->body;
echo $posts[0]->comments_count;
插入更新关联模型
保存方法
添加一个 Comment 到博客 *** Post*** 模型,可以不用在 Comment 中设置 post_id 属性,使用关联模型的 save 方法将 Comment 直接插入,如下:
$comment = new App\Comment(['message' => 'a message comment’]);
$post = App\Post::find(1);
$post->comments->save($comment);
如需要保存多个关联模型,可以使用 saveMany 方法,如下:
$post = App\Post::find(1);
$post->comments()->saveMany([
new App\Comment(['message' => 'a comment']),
new App\Comment(['message' => 'b comment'])
});
新增方法
create 方法接收普通的php数组,如下:
$post = App\Post::find(1);
$comment = $post->comments->create([
'message' => 'a new comment'
});
createMany 可以创建多个关联模型:
$post = App\Post::find(1);
$post->comments->createMany([
[
'message' => 'a new comment'
],
[
'message' => 'a new comment'
]
]);
更新belongsTo关联
当更新 belongsTo 关联时,可以使用 associate 方法。此方法将会在子模型中设置外键。
$account = App\Account(1);
$user->account->associate($account);
$user->save();
当移除 belongsTo 关联时,可以使用 dissociate 方法。此方法会将关联外键设置为 null 。
$user->account->dissociate();
$user->save();
默认模型
belongsTo 关系允许指定默认模型,当给定关系为 null 时,将会返回默认模型。
//获取博客的作者
public function user()
{
return $this->belongsTo('App\User')->withDefault();
}
如果需要在默认模型里添加属性,可以传递数组或回调方法到 withDefault 方法中,如下:
//获取博客的作者
public function user()
{
return $this->belongsTo('App\User')->withDefault([
'name' => 'Guest Author'
]);
}
public function user()
{
return $this->belongsTo('App\User')->withDefault(function ($user) {
$user->name = 'Guest Author';
});
}
多对多关联
附加/分离
给某个用户添加一个角色是通过向中间表插入一条记录实现的,可以使用 attach 方法,如下:
$user = App\User::find(1);
$user->roles()->attach($roleId);
在将关系附加到模型时,还可以传递一组要插入中间表的附加数据,如下:
$user->roles()->attach($roleId, ['expires' => $expores]);
移除用户的角色,使用 detach 移除多对多关联记录,如下:
//移除用户的一个角色
$user->roles()->detach($roleId);
//移除用户的所有角色
$user->roles()->detach();
为了方便,attach 和 detach 允许传递一个id数组,如下:
$user = App\User::find(1);
$user->roles()->detach([1,2,3]);
$user->roles()->attach([
1 => ['expires' => $expires],
2 => ['expires' => $expires]
]);
同步关联
sync 接收id数组以替换中间表的记录,所有未在id数组中的记录都会被移除,如下:
$user->roles()->sync([1,2,3]);
可以传递额外的附加数据到中间表,如下:
$user->roles()->sync([1 => ['expires' => $expires], 2,3);
如果不想移除现有的id,可以使用 syncWithoutDetaching 方法,如下:
$user->roles()->syncWithoutDetaching([1,2,3]);
本文详细介绍了Laravel框架中的Eloquent ORM关联模型,包括一对一、一对多、多对多等多种关联类型,并展示了如何定义、操作和查询关联数据,如动态属性、中间表、多态关联等,同时涵盖保存、更新和删除关联模型的方法。
1895

被折叠的 条评论
为什么被折叠?



