介紹 Eloquent 關聯中的多對多多型關聯(Many To Many Polymorphic Relations)

zhangbao發表於2017-07-21

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

相關文章