iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 13
1

今天我們來處理前面說到的文章標籤

什麼是多對多關係

多對多關係,顧名思義,就是甲乙兩個物件:

  • 甲可以和多個乙物件有對應關係
  • 乙可以和多個甲物件有對應關係

可能有讀者會疑惑:「這有什麼難的,不就原本是一對多,現在變成多對多而已嗎?」。

問題在於,之前甲乙兩個物件,有某個只會有單一對應關係時,我們可以讓該物件去紀錄對應物件的 id。但是,當甲乙兩個物件,都可能對應多個物件的時候,我們就比較難用之前紀錄 id 的方式來處理。

比較良好的設計,是使用一個「多對多關係表」,專門紀錄物件的對應關係。比方說以我們的「文章-標籤」對應關係來說,就可以用:

資料表欄位名稱 資料欄位內容
post_id 文章 id
tag_id 標籤 id

來進行處理。

建立關係表

Laravel 這個框架同樣的套路,相信讀者們又猜到了。

繼前面的資料表命名由 Laravel 綁定,接著一對多關係的 id 名稱由 Laravel 綁定,可以想像的到,其實多對多關係表的結構 Laravel 還是幫我們設計好了。

PostTag 物件的多對多關係,對應表的名稱是 post_tag,注意物件名稱前後,要依據字母順序前後,不能是 tag_post

再來就是裡面的欄位名稱,如說明一樣,會是 post_idtag_id

我們來建立 Tag 物件的資料表,和這張關係對應表

$ php artisan make:migration create_tags_table
Created Migration: 2019_09_15_111649_create_tags_table
$ php artisan make:migration create_post_tag_table
Created Migration: 2019_09_15_111657_create_post_tag_table

然後跟之前一樣,改寫 migration 裡面內容

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTagsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('content', 255);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tags');
    }
}

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostTagTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('post_tag', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('post_id');
            $table->bigInteger('tag_id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('post_tag');
    }
}

改完之後,我們再跑一下 migrate,資料表關係就建立完成囉!

建立多對多關係

再來就是實際在物件裡面建立多對多關係囉!

我們先建立 Tag 物件

$ php artisan make:model Tag
Model created successfully.

然後,我們在 Tag 物件裡面,加上 belongsToMany() 函式,來宣告其多對多關係

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}

對應的,我們也在 Post 物件裡面加上一樣的函式

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'content'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function subject()
    {
        return $this->belongsTo(Subject::class);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}

成功囉!我們一樣用測試路徑來看看效果。

首先我們在 tags 裡面加入幾筆資料:

id content created_at updated_at
1 111 NULL NULL
2 222 NULL NULL
3 333 NULL NULL

然後修改一下 post_tag 裡面的資料

id post_id tag_id created_at updated_at
1 1 1 NULL NULL
2 2 1 NULL NULL
3 2 2 NULL NULL
4 3 2 NULL NULL

首先,我們來取出 tag 標記為一的文章(也就是 id 為 1 和 2 的文章)

Route::get('/test', function(){
    $tag = App\Tag::find(1);
    $posts = $tag->posts;
    return $posts;
});

來取出資料看看,連線 http://127.0.0.1/test

[
    {
        id: 1,
        content: "Laravel demo 6.0 day 11 test",
        created_at: null,
        updated_at: "2019-09-13 09:20:51",
        subject_id: 1,
        pivot: {
            tag_id: 1,
            post_id: 1
        }
    },
    {
        id: 2,
        content: "Laravel demo 6.0 day 11 test",
        created_at: null,
        updated_at: null,
        subject_id: 1,
        pivot: {
            tag_id: 1,
            post_id: 2
        }
    }
]

成功囉!我們取出了對應的 post 資料了!

如果我們將 /test 改成

Route::get('/test', function(){
    $tag = App\Tag::find(2);
    $posts = $tag->posts;
    return $posts;
});

那就會變成

[
    {
        id: 2,
        content: "Laravel demo 6.0 day 11 test",
        created_at: null,
        updated_at: null,
        subject_id: 1,
        pivot: {
            tag_id: 2,
            post_id: 2
        }
    },
    {
        id: 3,
        content: "Laravel demo 6.0 day 11 test",
        created_at: null,
        updated_at: null,
        subject_id: 2,
        pivot: {
            tag_id: 2,
            post_id: 3
        }
    }
]

是不是很容易呀!


要反過來透過 Post 物件找到對應的 Tag 也是很容易的

我們將路徑改成

Route::get('/test', function(){
    $post = App\Post::find(2);
    $tags = $post->tags;
    return $tags;
});

就可以看到

[
    {
        id: 1,
        content: "111",
        created_at: null,
        updated_at: null,
        pivot: {
            post_id: 2,
            tag_id: 1
        }
    },
    {
        id: 2,
        content: "222",
        created_at: null,
        updated_at: null,
        pivot: {
            post_id: 2,
            tag_id: 2
        }
    }
]

可以找到每個 Post 所對應的 Tag 物件囉!


我們這裡小總結一下

今天我們延續上次所談到的一對多關係,討論多對多關係所需要的架構,並且實作了文章與標籤之間的多對多關係。

希望今天的文章能讓大家滿意!我們明天見!


上一篇
[Day 12] 實作物件之間的關聯!談 Laravel Model Relation
下一篇
[Day 14] 在開發時加入資料!聊 database seeder
系列文
Laravel 6.0 初體驗!怎麼用最新的 laravel 架網站!30

尚未有邦友留言

立即登入留言