Laravel Eloquent 关联

本文详细介绍了Laravel框架中的Eloquent ORM关联模型,包括一对一、一对多、多对多等多种关联类型,并展示了如何定义、操作和查询关联数据,如动态属性、中间表、多态关联等,同时涵盖保存、更新和删除关联模型的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模型关联

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 )模型可以认为是多对多关联,用户可以有多个角色,每个角色可以为多个用户所有。对应的数据分别为 usersrolesrole_user 。其中 role_user 是按两个模型字母排序,包含 user_idrole_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_atupdated_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:关联模型
	...

imagesimageable_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();

为了方便,attachdetach 允许传递一个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]);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值