Laravel 模型关联关系

一对一

建立关联关系

通过hasOne方法定义一对一关联

public function profile()
{
	return $this->hasOne(UserProfile::class);
}

Eloquent 底层约定

hasOne方法参数

public function hasOne($related, $foreignKey = null, $localKey = null)

第一个参数是关联模型的类名,第二个参数是关联模型类所属表的外键,第三个参数是关联表的外键关联到当前模型所属表的哪个字段。

建立相对的关联关系

通过belongsTo方法来建立相对的一对一关联关系。

public function user()
{
	return $this->belongsTo(User::class);
}

belongsTo方法参数

public function belongsTo($related,$foreignKey = null, $ownerKer = null, $relation = null)

第一个参数是关联模型的类名。第二个参数是当前模型类所属表的外键,第三个参数是关联模型类所属表的主键,第四个参数默认约定是关联关系方法名,也是关联关系动态属性名。

一对多

建立关联关系

通过hasMany方法实现

public function posts()
{
	return $this->hasMany(Post::class);
}

懒加载

通过with方法实现

$post = Post::with('author')
	->where('views','>',0)
	->get();

多对多

建立关联关系

通过belongsToMany方法实现

public function tags()
{
	return $this->belongsToMany(Tag::class,'post_tags');
}

底层约定

belongsToMany方法参数如下

public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null, $parentKey = null, $relatedKey = null, $relation = null)

第一个参数是关联模型的类名。第二个参数是中间表,第三个参数是中间表中当前模型类的外键,第四个参数是中间表当前关联模型类的外键,第五个参数表示对应当前模型的哪个字段,第六个参数表示对应关联模型的哪个字段,最后一个参数表示关联关系名称,默认是关联方法名。

获取中间表字段

通过with方法传入字段然后将其返回:

public function tags()
{
	return $this->belongsToMany(Tag::class,'post_tags')->withPivot('user_id')->withTimestamps();
}

远程一对多关联

远程一对多是借助中间表进行关联。

建立远程一对多关联关系

通过hasManyThrough方法定义

public function posts()
{
	return $this->hasManyThrough(Post::class,User::class);
}

第一个参数是关联的模型类,第二个参数是中间借助的模型类。

底层约定

hasManyThrough方法的方法参数

public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)

第一个参数是关联模型类,第二个参数是中间模型类,第三个参数是中间模型类与当前模型类的关联外键,第四个参数指的是中间模型类与关联模型类的关联外键,第五个参数是当前模型类的主键,第六个参数是中间模型类的主键。

定义相对的关联关系

public function image()
{
	return $this->morphOne(Image::class,'imageable');
}

一对多的多态关联

一对多多态关联与一对一类似。

在模型类中构建一对多多态关联

通过morphTo方法定义

public function commentable()
{
	return $this->morphTo();
}

定义相对的关联关系

通过morphMany方法实现

public function comments()
{
	return $this->morphMany(Comment::class,'commentable');
}

多对多的多态关联

通过morphedByMany方法实现

public function posts()
{
    return $this->morphedByMany(Post::class, 'taggable');
}

morphedByMany方法完整参数如下

public function morphedByMany($related, $name, $table = null, $foreignPivotKey = null, $relatedPivotKey = null, $parentKey = null, $relatedKey = null)

第一个参数表示关联的模型类,第二个参数表示关联名称,第三个参数表示中间表名称,第四个参数表示当前模型类在中间表的外键,第五个参数表示中间表中的关联ID字段,第六个参数表示当前模型类的主键,第七个参数表示关联模型类的主键。

定义相对的关联关系

morphToMany方法实现

public function tags()
{
    return $this->morphToMany(Tag::class, 'taggable');
}

关联查询

关联查询分为懒惰式加载(动态属性),一种是渴求式加载(通过with方法)。

懒惰式加载

$post = Post::findOrFail(1);
$author = $post->author;

基于关联查询过滤模型实例

有结果过滤

如要查询所有发布过文章的用户

$users = User::has('posts')->get();

获取发布文章数量大于一的

$users = User::has('posts','>',1)->get();

获取发布的文章且有评论的用户

$users = User::has('posts.comments')->get(); 

包含评论或标签的文章

$posts = Post::has('comments')->orHas('tags')->get();

无结果过滤

获取没有发布过文章的用户

$users = User::doesntHave('posts')->get();

统计关联模型

通过withCount在不加载关联模型的情况下统计关联结果数量。

$post = Post::withCount('comments')->findOrFail(32);

渴求式加载

通过with方法实现

$post = Post::with('author')->findOrFail(1);

懒惰渴求式加载

$users = User::all();
$condition = true;

if ($condition) {
    $users->load('posts');
}

关联插入与更新

一对多关联记录插入

$post = Post::findOrFail(1);
$faker = \Faker\Factory::create();
$comment = new Comment();
$comment->content = $faker->paragraph;
$comment->user_id = mt_rand(1, 15);
$post->comments()->save($comment);

通过saveMany方法一次插入多条关联记录

$post = Post::findOrFail(1);
$faker = \Faker\Factory::create();
$post->comments()->saveMany([
    new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
    new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
    new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
    new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]),
    new Comment(['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)])
]);

还可通过create/createMany方法来插入关联数据

// 插入一条记录
$post->comments()->create([
    'content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)
]);

// 插入多条记录
$post->comments()->createMany([
    ['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)],
    ['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)],
    ['content' => $faker->paragraph, 'user_id' => mt_rand(1, 15)]
]);

更新一对多所属模型外键字段

更新新创建的模型实例所属模型(父模型)的外键字段

$user = User::findOrFail(1);
$post->author()->associate($user);
$post->save();

通过dissociate方法接触当前模型与所属模型之间的关联

$post->author()->dissociate();
$post->save();

空对象模型

public function author()
{
    return $this->belongsTo(User::class, 'user_id', 'id', 'author')
        ->withDefault();
}

空对象指定默认值

public function author()
{
    return $this->belongsTo(User::class, 'user_id', 'id', 'author')
        ->withDefault([
            'id' => 0,
            'name' => '游客用户',
        ]);
}

多对多关联的绑定与解除

// 插入单条记录
$post->tags()->save(
    new Tag(['name' => $faker->word])
); 

// 如果中间表接收额外参数可以通过第二个参数传入
$post->tags()->save(
    new Tag(['name' => $faker->word]), 
    ['user_id' => 1]
); 

// 插入多条记录
$post->tags()->saveMany([
    new Tag(['name' => $faker->unique()->word]),
    new Tag(['name' => $faker->unique()->word]),
    new Tag(['name' => $faker->unique()->word])
]);

// 如果插入多条记录需要传递中间表额外字段值(通过键值关联对应记录与额外字段)
$post->tags()->saveMany([
    1 => new Tag(['name' => $faker->unique()->word]),
    2 => new Tag(['name' => $faker->unique()->word]),
    3 => new Tag(['name' => $faker->unique()->word])
], [
    1 => ['user_id' => 1],
    2 => ['user_id' => 2],
    3 => ['user_id' => 3],
]);

绑定通过attach方法实现

$post = Post::findOrFail(1);
$tag = Tag::findOrFail(1);
$post->tags()->attach($tag->id);
// 如果中间表还有其它额外字段,可以通过第二个数组参数传入
// $post->tags()->attach($tag->id, ['user_id' => $userId]);
// 还可以一次绑定多个标签
// $post->tags()->attach([1, 2]);
// 如果绑定多个标签,要传递额外字段值,可以这么做:
/*$post->tags()->attach([
    1 => ['user_id' => 1],
    2 => ['user_id' => 2]
]);*/

解除关联关系

$post->tags()->detach(1);
// 如果想要一次解除多个关联,可以这么做:
// $post->tags()->detach([1, 2]);
// 如果想要一次解除所有关联,可以这么做:
// $post->tags()->detach();

只更新中间表字段通过updateExistingPivot方法实现

$post->tags()->updateExistingPivot($tagId, $attributes);

触发父模型时间戳更新

需要在子模型中配置$touches属性

// 要触发更新的父级关联关系
protected $touches = [
    'commentable'
];
posted @ 2020-07-28 23:55  _大可乐  阅读(547)  评论(0编辑  收藏  举报