前言
Laravel 官方文件為多型資料表的外鍵使用了特殊的命名規則,例如 imageable
、commentable
,和 taggable
等,這樣的命名方式不是很自然,並且需要在 Model 中定義資料表的名稱,因為 Laravel 並不知道這些詞彙的複數型態為何。
以下參考 spatie/laravel-permission 套件的命名方式,使用 model
作為多型資料表的外鍵名稱,並使用例如 model_has_tags
來為多型資料表命名。
一對一多型關聯
假設一個網站的頁面(page)和文章(post)各自擁有一張圖片(image),可以利用一對一多型關聯(one-to-one polymorphic relation)將圖片儲存在共用的資料表。
關聯架構
1 | pages |
模型、遷移檔與模型工廠
新增 Page
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Page -m -f |
新增 Post
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Post -m -f |
新增 Image
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Image -m -f |
遷移檔
修改 create_images_table.php
遷移檔:
1 | Schema::create('images', function (Blueprint $table) { |
執行遷移。
1 | php artisan migrate |
關聯方法
修改 Page
模型,定義關聯方法:
1 | /** |
修改 Post
模型,定義關聯方法:
1 | /** |
修改 Image
模型,定義關聯方法:
1 | /** |
測試資料
進入 Tinker 介面。
1 | php artisan tinker |
新增一些測試資料:
1 | factory(App\Page::class)->create(); |
使用
為第一個頁面新增一張圖片:
1 | Page::first()->image()->save(factory(App\Image::class)->make()); |
為第一個頁面取得所有圖片:
1 | Page::first()->image()->get(); |
為第一篇文章新增一張圖片:
1 | Post::first()->image()->save(factory(App\Image::class)->make()); |
為第一篇文章取得所有圖片:
1 | Post::first()->image()->get(); |
取得擁有第一張圖片的模型:
1 | Image::first()->model()->get(); |
一對多多型關聯
假設一個網站的頁面(page)和文章(post)各自擁有多個評論(comment),可以利用一對多多型關聯(one-to-many polymorphic relation)將評論儲存在共用的資料表。
關聯架構
1 | pages |
模型、遷移檔與模型工廠
新增 Page
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Page -m -f |
新增 Post
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Post -m -f |
新增 Comment
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Comment -m -f |
遷移檔
修改 create_comments_table.php
遷移檔:
1 | Schema::create('comments', function (Blueprint $table) { |
執行遷移。
1 | php artisan migrate |
關聯方法
修改 Page
模型,定義關聯方法:
1 | /** |
修改 Post
模型,定義關聯方法:
1 | /** |
修改 Image
模型,定義關聯方法:
1 | /** |
測試資料
進入 Tinker 介面。
1 | php artisan tinker |
新增一些測試資料:
1 | factory(App\Page::class)->create(); |
使用
為第一個頁面新增兩則評論:
1 | Page::first()->comments()->saveMany(factory(App\Comment::class, 2)->make()); |
為第一個頁面取得所有評論:
1 | Page::first()->comments()->get(); |
為第一篇文章新增兩則評論:
1 | Post::first()->comments()->saveMany(factory(App\Comment::class, 2)->make()); |
為第一篇文章取得所有評論:
1 | Post::first()->comments()->get(); |
取得擁有第一則評論的模型:
1 | Comment::first()->model()->get(); |
多對多多型關聯
假設一個網站的頁面(page)和文章(post)共用多個標籤(tag),可以利用多對多多型關聯(many-to-many polymorphic relation)將標籤儲存在共用的資料表。
關聯架構
1 | pages |
模型、遷移檔與模型工廠
新增 Page
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Page -m -f |
新增 Post
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Post -m -f |
新增 Tag
模型,與其遷移檔、模型工廠。
1 | php artisan make:model Tag -m -f |
新增 create_model_has_tags_table
遷移檔。
1 | php artisan make:migration create_model_has_tags_table |
遷移檔
修改 create_model_has_tags_table.php
遷移檔:
1 | Schema::create('model_has_tags', function (Blueprint $table) { |
執行遷移。
1 | php artisan migrate |
關聯方法
修改 Page
模型,定義關聯方法:
1 | /** |
修改 Post
模型,定義關聯方法:
1 | /** |
修改 Tag
模型,定義關聯方法:
1 | /** |
測試資料
進入 Tinker 介面。
1 | php artisan tinker |
新增一些測試資料:
1 | factory(App\Page::class)->create(); |
使用
為第一個頁面新增所有標籤:
1 | Page::first()->tags()->saveMany(Tag::all()); |
為第一個頁面取得所有標籤:
1 | Page::first()->tags()->get(); |
為第一篇文章新增所有標籤:
1 | Post::first()->tags()->saveMany(Tag::all()); |
為第一篇文章取得所有標籤:
1 | Post::first()->tags()->get(); |
取得擁有第一個標籤的所有頁面:
1 | Tag::first()->pages()->get(); |
取得擁有第一個標籤的所有文章:
1 | Tag::first()->posts()->get(); |
特徵機制
由於 Page
模型和 Post
模型會使用到共同的關聯方法,因此可以在 app
資料夾新增 Traits
資料夾,並建立共用的特徵機制。
新增 HasImage.php
檔:
1 | namespace App\Traits; |
新增 HasComments.php
檔:
1 | namespace App\Traits; |
新增 HasTags.php
檔:
1 | namespace App\Traits; |
重構 Page
模型:
1 | namespace App; |
重構 Post
模型:
1 | namespace App; |
自訂多型類型
Laravel 預設會使用完全符合的類別名稱來儲存關聯模型的類型,也就是 model_type
會儲存像是 App\Page
或 App\Post
這樣的類別名稱。最好定義一個關聯的對照表,來指示 Eloquent 使用自訂的名稱來儲存類型,將應用程式與資料庫解耦。
新增一個 RelationServiceProvider
服務提供者。
1 | php artisan make:provider RelationServiceProvider |
將服務提供者修改如下:
1 | namespace App\Providers; |
註冊到 config
資料夾的 app.php
設定檔。
1 | return [ |
重新產生 Composer 自動載入檔案。
1 | composer dump-autoload |