核心要點
morphTo
方法用於接收多態關聯的模型,而morphMany
或morphOne
方法用於與其他模型關聯的模型。 MorphMap
方法可用於指示Eloquent對每個模型使用自定義名稱,而不是類名。這在模型命名空間更改或命名空間過長的情況下很有幫助。 本文由Younes Rafie同行評審。感謝所有SitePoint的同行評審者,使SitePoint的內容盡善盡美!
您可能已經使用了模型或數據庫表之間不同類型的關係,例如Laravel中常見的:一對一、一對多、多對多以及has-many-through。但還有一種不太常見的類型:多態關聯。那麼什麼是多態關聯呢?
多態關聯是指一個模型在一個關聯上可以屬於多個其他模型。
為了闡明這一點,讓我們創建一個虛構的場景,其中我們有Topic和Post模型。用戶可以在主題和帖子中留下評論。使用多態關係,我們可以對這兩種情況使用單個comments表。令人驚訝,對吧?這似乎有點不切實際,因為理想情況下,我們必須創建post_comments表和topic_comments表來區分評論。使用多態關係,我們不需要兩個表。讓我們通過一個實際示例來了解多態關係。
我們將創建一個演示音樂應用程序,其中包含歌曲和專輯。在這個應用程序中,我們可以對歌曲和專輯進行點贊。使用多態關係,我們將對這兩種情況使用單個upvotes表。首先,讓我們檢查構建此關係所需的表結構:
<code>albums id - integer name - string songs id - integer title - string album_id - integer upvotes id - integer upvoteable_id - integer upvoteable_type - string </code>
讓我們討論一下upvoteable_id
和upvoteable_type
列,對於那些以前沒有使用過多態關係的人來說,這些列可能看起來有點陌生。 upvoteable_id
列將包含專輯或歌曲的ID值,而upvoteable_type
列將包含擁有模型的類名。 upvoteable_type
列是ORM在訪問upvoteable
關係時確定要返回哪個“類型”擁有模型的方式。
我假設您已經擁有一個正在運行的Laravel應用程序。如果沒有,這個高級快速入門課程可能會有所幫助。讓我們首先創建三個模型和遷移,然後編輯遷移以滿足我們的需求。
<code>albums id - integer name - string songs id - integer title - string album_id - integer upvotes id - integer upvoteable_id - integer upvoteable_type - string </code>
請注意,在創建模型時傳遞-m
標誌也會生成與這些模型關聯的遷移。讓我們調整這些遷移中的up
方法以獲得所需的表結構:
{some_timestamp}_create_albums_table.php
<code>php artisan make:model Album -m php artisan make:model Song -m php artisan make:model Upvote -m </code>
{some_timestamp}_create_songs_table.php
<code class="language-php">public function up() { Schema::create('albums', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }</code>
{some_timestamp}_create_upvotes_table.php
<code class="language-php">public function up() { Schema::create('songs', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->integer('album_id')->unsigned()->index(); $table->timestamps(); $table->foreign('album_id')->references('id')->on('albums')->onDelete('cascade'); }); }</code>
現在,我們可以運行artisan migrate命令來創建這三個表:
<code class="language-php">public function up() { Schema::create('upvotes', function (Blueprint $table) { $table->increments('id'); $table->morphs('upvoteable'); // 添加无符号整数 upvoteable_id 和字符串 upvoteable_type $table->timestamps(); }); }</code>
現在讓我們配置我們的模型以注意專輯、歌曲和點讚之間的多態關係:
app/Upvote.php
<code>php artisan migrate</code>
app/Album.php
<code class="language-php">[...] class Upvote extends Model { /** * 获取所有拥有模型。 */ public function upvoteable() { return $this->morphTo(); } }</code>
app/Song.php
<code class="language-php">class Album extends Model { protected $fillable = ['name']; public function songs() { return $this->hasMany(Song::class); } public function upvotes() { return $this->morphMany(Upvote::class, 'upvoteable'); } }</code>
Album和Song模型中的upvotes
方法定義了這些模型和Upvote模型之間的多態一對多關係,並將幫助我們獲取該特定模型實例的所有點贊。
定義了關係後,我們現在可以試用該應用程序,以便更好地了解多態關係的工作原理。我們不會為此應用程序創建任何視圖,我們只會從控制台試用我們的應用程序。
如果您考慮控制器以及我們應該放置點贊方法的位置,我建議創建一個AlbumUpvoteController和一個SongUpvoteController。通過這種方式,我們在處理多態關係時,可以將事情嚴格地與我們正在操作的對象聯繫起來。在我們的例子中,我們可以對專輯和歌曲進行點贊。點贊不屬於專輯,也不屬於歌曲。此外,它不是一般的點贊,這與我們在大多數一對多關係中擁有UpvotesController的方式相反。希望這說得通。
讓我們啟動控制台:
<code class="language-php">class Song extends Model { protected $fillable = ['title', 'album_id']; public function album() { return $this->belongsTo(Album::class); } public function upvotes() { return $this->morphMany(Upvote::class, 'upvoteable'); } }</code>
現在我們已經準備好了一些數據,我們可以通過我們的模型訪問我們的關係。以下是upvotes表中數據的屏幕截圖:
要訪問專輯的所有點贊,我們可以使用upvotes
動態屬性:
<code>php artisan tinker >>> $album = App\Album::create(['name' => 'More Life']); >>> $song = App\Song::create(['title' => 'Free smoke', 'album_id' => 1]); >>> $upvote1 = new App\Upvote; >>> $upvote2 = new App\Upvote; >>> $upvote3 = new App\Upvote; >>> $album->upvotes()->save($upvote1); >>> $song->upvotes()->save($upvote2); >>> $album->upvotes()->save($upvote3);</code>
也可以通過訪問執行對morphTo
的調用的方法的名稱,從多態模型檢索多態關係的所有者。在我們的例子中,那是Upvote模型上的upvoteable
方法。因此,我們將訪問該方法作為動態屬性:
<code class="language-php">$album = App\Album::find(1); $upvotes = $album->upvotes; $upvotescount = $album->upvotes->count();</code>
Upvote模型上的upvoteable
關係將返回一個Album實例,因為此點贊由Album實例的實例擁有。
由於可以獲取歌曲或專輯的點贊數量,因此我們可以根據視圖上的點贊對歌曲或專輯進行排序。這就是音樂排行榜的工作方式。
對於歌曲,我們將像這樣獲取點贊:
<code>albums id - integer name - string songs id - integer title - string album_id - integer upvotes id - integer upvoteable_id - integer upvoteable_type - string </code>
默認情況下,Laravel將使用完全限定的類名來存儲相關模型的類型。例如,在上面的示例中,Upvote可能屬於Album或Song,默認的upvoteable_type
分別為App\Album
或App\Song
。
但是,這有一個很大的缺陷。如果Album模型的命名空間發生更改怎麼辦?我們將不得不進行某種遷移以重命名upvotes表中的所有出現。這有點棘手!如果命名空間很長(例如App\Models\Data\Topics\Something\SomethingElse
)會發生什麼?這意味著我們必須在列上設置一個很長的最大長度。這就是MorphMap
方法可以幫助我們的地方。
“morphMap”方法將指示Eloquent對每個模型使用自定義名稱,而不是類名:
<code>php artisan make:model Album -m php artisan make:model Song -m php artisan make:model Upvote -m </code>
我們可以在AppServiceProvider的boot函數中註冊morphMap,或者創建一個單獨的服務提供者。為了使新的更改生效,我們必須運行composer dump-autoload命令。因此,現在我們可以添加這個新的點贊記錄:
<code class="language-php">public function up() { Schema::create('albums', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }</code>
它的行為方式與之前的示例完全相同。
即使您可能從未遇到過需要使用多態關係的情況,但那一天最終可能會到來。使用Laravel的好處是,處理這種情況非常容易,無需進行任何模型關聯技巧即可使事情正常工作。 Laravel甚至支持多對多多態關係。您可以在此處閱讀更多相關信息。
我希望您現在已經了解了多態關係以及可能需要這些類型關係的情況。另一個關於多態關係的稍微高級一點的例子在這裡。如果您覺得這有幫助,請與您的朋友分享,不要忘記點擊點贊按鈕。請隨時在下面的評論部分留下您的想法!
Laravel中的多態關係提供了一種靈活且有效的方式來處理不同模型之間的相關數據。它們允許一個模型在一個關聯上屬於多個其他類型的模型。這意味著您可以擁有所有相關數據的單個唯一標識符列表,而不管它們的類型如何。這可以極大地簡化您的數據庫結構並使您的代碼更易於維護。它還允許更動態和靈活的數據關係,這在復雜的應用程序中特別有用。
在Laravel中設置多態關係涉及在Eloquent模型中定義關係。首先,您需要在將接收多態關係的模型上定義關係。這是使用morphTo
方法完成的。然後,在將與其他模型關聯的模型上,根據關係是一對多還是一對一,使用morphMany
或morphOne
方法。
當然,讓我們考慮一個博客平台,其中帖子和用戶都可以有評論。在這種情況下,Comment模型將與Post和User模型具有多態關係。以下是如何定義此關係:
<code>albums id - integer name - string songs id - integer title - string album_id - integer upvotes id - integer upvoteable_id - integer upvoteable_type - string </code>
您可以像使用任何其他Eloquent關係一樣檢索多態關係中的相關記錄。例如,如果您想檢索帖子的所有評論,您可以這樣做:
<code>php artisan make:model Album -m php artisan make:model Song -m php artisan make:model Upvote -m </code>
保存多態關係中的相關記錄也類似於其他Eloquent關係。您可以使用associate
方法關聯模型,然後保存模型。這是一個例子:
<code class="language-php">public function up() { Schema::create('albums', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }</code>
在模型可以屬於多個其他類型模型的情況下,多態關係特別有用。一些常見的用例包括可以屬於帖子和用戶的評論、可以應用於多種類型內容的標籤以及可以附加到各種類型的實體的圖像或文件。
雖然多態關係提供了很大的靈活性,但它們也可能比標準的Eloquent關係更複雜,更難以設置和管理。它們也不支持標準關係的所有功能,例如渴望加載約束。
您可以使用關係上的delete
方法刪除多態關係中的相關記錄。例如,要刪除帖子的所有評論,您可以這樣做:
<code class="language-php">public function up() { Schema::create('songs', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->integer('album_id')->unsigned()->index(); $table->timestamps(); $table->foreign('album_id')->references('id')->on('albums')->onDelete('cascade'); }); }</code>
是的,Laravel通過morphToMany
和morphedByMany
方法支持多對多多態關係。這允許一個模型在多對多的基礎上屬於多個模型。
在數據庫遷移中,您通常會向將接收多態關係的表中添加兩列:一列用於相關模型ID,另一列用於相關模型類型。 Laravel提供了一個方便的morphs
方法來添加這些列:
<code class="language-php">public function up() { Schema::create('upvotes', function (Blueprint $table) { $table->increments('id'); $table->morphs('upvoteable'); // 添加无符号整数 upvoteable_id 和字符串 upvoteable_type $table->timestamps(); }); }</code>
這會向表中添加commentable_id
和commentable_type
列。
以上是重新引入雄辯的多態關係的詳細內容。更多資訊請關注PHP中文網其他相關文章!