在 上一篇 裡,講到一張評論表(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 協議》,轉載必須註明作者和本文連結