iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
1
Software Development

30天完成家庭任務平台系列 第 11

30天完成家庭任務平台:第十一天

  • 分享至 

  • xImage
  •  

今天是Eloquent的Polymorphic關係系列,但其實它跟一開始的一對一/一對多/多對多關係系列並沒有太大的差別,最大的不同在於一個Model現在可以跟多個Model連結。
以官方文件的例子說明:

  • Post和User擁有一個Image,一個Image屬於Post或User:
    如果以非Polymorphic 的一對一關係來寫,則在寫images的資料表會遇見問題,就是它的外鍵被限制在一個資料表上(users),所以當需要另外表示Post擁有一個Image時,就必須要增加多一份資料表。

    Schema::create('images', function (Blueprint $table) {
           $table->id();
           $table->string('url');
           $table->foreignId('user_id')->constrained()->onDelete('cascade');
           $table->timestamps();
       });
    
    

    同一概念卻不能表現在同一張資料表上是造成上面問題的原因,所以透過多紀錄了外鍵的Model型態(imageable_type)為何,讓Image可以自由跟Post或User連結起來。

    Schema::create('images', function (Blueprint $table) {
           $table->id();
           $table->string('url');
           $table->unsignedInteger('imageable_id');
           $table->string('imageable_type');
           $table->timestamps();
       });
    

    PS 在activities資料表中所使用的nullableMorphs是Laravel提供的功能,可以幫你產生_id和_tyep的兩個欄位。

  • 來個測試看看是否成功

    1. 設定資料表
      • users:直接使用已經設定的users資料表
      • posts:
      public function up()
  {
      Schema::create('posts', function (Blueprint $table) {
          $table->id();
          $table->string('name');
          $table->timestamps();
      });
  }

  • images:同上的images
  1. 設定Model

    • User:直接使用已經設定的User資料表
    • Post:
    class Post extends Model
{
    use HasFactory;

    public function image()
    {
       return $this->morphOne('App\Models\Image', 'imageable');
    }
}
  • Image:
class Image extends Model
{
    protected $fillable =['url'];
    use HasFactory;

    public function imageable()
    {
        return $this->morphTo();
    }
}
  1. 測試:php artisan test通過
class PolymorphicTest extends TestCase
{
    use RefreshDatabase, WithFaker;
    /** @test */
    public function images_can_belong_to_a_user_or_to_a_post()
    {
        $user = User::factory()->create();
        $post = Post::factory()->create();
        $user->image()->create(['url' => $this->faker->url]);
        $post->image()->create(['url' => $this->faker->url]);
        $this->assertDatabaseHas(
            'images',
            [
                'imageable_id' => $user->id,
                'imageable_type' => 'App\Models\User'
            ]
        );
        $this->assertDatabaseHas(
            'images',
            [
                'imageable_id' => $post->id,
                'imageable_type' => 'App\Models\Post'
            ]
        );
        $userImage = $user->image;
        $postImage = $post->image;
        $this->assertTrue($userImage->imageable->is($user));
        $this->assertTrue($postImage->imageable->is($post));
    }
}

PS 1. alias test='vendor/bin/phpunit --filter'是當我不用套件,用命令列測試單獨測試時使用的,設定後就可以 test images_can_belong_to_a_user_or_to_a_post; 2. 通常建議在單一測試裡面不要太多assert的斷定,但是因為是自己的練習,所以我都寫在一起。

  • 關係對應的整理
    前面說過其實一對一/一對多/多對多在非Polymorphic或是Polymorphic的關係下差異不大,只是Polymorphic可以更自由地連結不同的Model,所以就做個對應的小語法整理,詳細參數官方的範例很清楚。

    關係 一般 Polymorphic

    一對一|hasOne/belongsTo|morphOne/morphTo
    一對多|hasMany/belongsTo|morphMany/morphTo
    多對多|belongsToMany/belongsToMany|morphedByMany/morphedToMany

PS 如果多個Tag可以對應多個Post或多個Video時,則Tag中使用morphedByMany規定跟Post或Video的關係,Post或Video用morphedToMany規定跟Tag的關係

參考文件

Eloquent:Relationship


上一篇
30天完成家庭任務平台:第十天
下一篇
30天完成家庭任務平台:第十二天
系列文
30天完成家庭任務平台30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言