介绍 Eloquent 关联中的多对多多态关联(Many To Many Polymorphic Relations)
在 上一篇 里,讲到一张评论表(comments)可以给文章表(posts)用,也可以给视频表(videos)用。如果你足够细心就会发现,文章和评论、视频和评论都是一对多的,这其实就是一对多多态关系。
但是有没有多对多的多态关联呢?这是废话,题目中就已经暴露了——确实有的,比如:标签。一篇文章的标签是 JavaScript
,但是一个视频的标签也可能是 JavaScript
啊。
文章和标签、视频和标签都是多对多的,这就是多对多多态关系,定义在它们 Model 中的关联就是多对多多态关联。
下面就来实现。
创建表
创建 tags
表和 taggables
表。
php artisan make:model Models/Tag -m -c
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->string('slug')->unique();
});
DB::table('tags')->insert([
['name' => 'Laravel', 'slug' => 'laravel'],
['name' => 'Lumen', 'slug' => 'lumen'],
['name' => 'Spark', 'slug' => 'spark'],
['name' => 'Forge', 'slug' => 'Forge'],
['name' => 'Envoyer', 'slug' => 'envoyer'],
['name' => 'Homestead', 'slug' => 'homestead'],
['name' => 'Valet', 'slug' => 'valet'],
['name' => 'Socialite', 'slug' => 'socialite'],
['name' => 'Mix', 'slug' => 'mix'],
['name' => 'Dusk', 'slug' => 'dusk'],
]);
}
php artisan make:migration create_taggables_table --create=taggables
Schema::create('taggables', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('taggable_id');
$table->string('taggable_type');
$table->timestamps();
});
taggables
是多对多关系中的中间表/关系表。taggable_id
、taggable_type
和 上一篇 中的 commentable_id
、commentable_type
两个字段的作用是一样一样的。
php artisan migrate
设定关联
Tag Model
class Tag extends Model
{
protected $fillable = ['name', 'slug'];
public $timestamps = false;
/**
* 获取该标签下的文章
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
/**
* 获取该标签下的视频
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
Post Model
/**
* 取得文章标签
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
Video Model
/**
* 取得视频标签
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
你会发现 Post Model 和 Video Model 中定义的 tags
方法是一样的,没错,包括后来的内容类型,在其 Model 中定义这个方法都是一样的。
所以,我们就把这个方法写成一个 trait。
<?php
namespace App\Helpers;
use App\Models\Tag;
trait HasTags
{
/**
* 取得爱谁谁的标签
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
然后改写前面 Post Model 和 Video Model 的写法。
use App\Helpers\HasTags;
class Post extends Model
{
use HasTags;
...
}
class Video extends Model
{
use HasTags;
...
}
使用
php artisan tinker
>>> namespace App\Models;
>>> Post::find(1)->tags()->sync([1,2,3]);
=> [
"attached" => [
1,
2,
3,
],
"detached" => [],
"updated" => [],
]
>>> Post::find(1)->tags;
=> Illuminate\Database\Eloquent\Collection {#784
all: [
App\Models\Tag {#785
id: 1,
name: "Laravel",
slug: "laravel",
pivot: Illuminate\Database\Eloquent\Relations\MorphPivot {#782
taggable_id: 1,
tag_id: 1,
},
},
App\Models\Tag {#786
id: 2,
name: "Lumen",
slug: "lumen",
pivot: Illuminate\Database\Eloquent\Relations\MorphPivot {#783
taggable_id: 1,
tag_id: 2,
},
},
App\Models\Tag {#789
id: 3,
name: "Spark",
slug: "spark",
pivot: Illuminate\Database\Eloquent\Relations\MorphPivot {#778
taggable_id: 1,
tag_id: 3,
},
},
],
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
创建
taggables
表时,主键应该是tag_id
@mokecc 我照着试了下,taggables表里没有内容?而且从Tag::find(1)->posts, 没法看到内容。能帮忙解惑么
请问
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
中taggable是神马含义,随便定义还是有什么约定。起什么作用?
弱弱的问一下:
php artisan make:model Models/Tag -m -c
中的"-c"表示什么意思?
请问怎么能限制tag 显示的字段呢,