首頁 >後端開發 >php教程 >重新引入雄辯的多態關係

重新引入雄辯的多態關係

Lisa Kudrow
Lisa Kudrow原創
2025-02-09 12:17:13590瀏覽

Re-Introducing Eloquent's Polymorphic Relationships

核心要點

  • Laravel的多態關聯允許一個模型在一個關聯上屬於多個其他模型。這簡化了數據庫結構,使代碼更易維護,並允許更動態和靈活的數據關係。
  • 在Laravel中設置多態關聯涉及在Eloquent模型中定義關聯。 morphTo方法用於接收多態關聯的模型,而morphManymorphOne方法用於與其他模型關聯的模型。
  • Laravel的MorphMap方法可用於指示Eloquent對每個模型使用自定義名稱,而不是類名。這在模型命名空間更改或命名空間過長的情況下很有幫助。
  • Laravel支持多對多多態關係,允許一個模型在多對多的基礎上屬於多個模型。這在復雜的應用程序中特別有用。

本文由Younes Rafie同行評審。感謝所有SitePoint的同行評審者,使SitePoint的內容盡善盡美!


Re-Introducing Eloquent's Polymorphic Relationships

您可能已經使用了模型或數據庫表之間不同類型的關係,例如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_idupvoteable_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表中數據的屏幕截圖:

Re-Introducing Eloquent's Polymorphic Relationships

要訪問專輯的所有點贊,我們可以使用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\AlbumApp\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甚至支持多對多多態關係。您可以在此處閱讀更多相關信息。

我希望您現在已經了解了多態關係以及可能需要這些類型關係的情況。另一個關於多態關係的稍微高級一點的例子在這裡。如果您覺得這有幫助,請與您的朋友分享,不要忘記點擊點贊按鈕。請隨時在下面的評論部分留下您的想法!

關於Eloquent的多態關係的常見問題解答

使用Laravel中的多態關係有哪些好處?

Laravel中的多態關係提供了一種靈活且有效的方式來處理不同模型之間的相關數據。它們允許一個模型在一個關聯上屬於多個其他類型的模型。這意味著您可以擁有所有相關數據的單個唯一標識符列表,而不管它們的類型如何。這可以極大地簡化您的數據庫結構並使您的代碼更易於維護。它還允許更動態和靈活的數據關係,這在復雜的應用程序中特別有用。

如何在Laravel中設置多態關係?

在Laravel中設置多態關係涉及在Eloquent模型中定義關係。首先,您需要在將接收多態關係的模型上定義關係。這是使用morphTo方法完成的。然後,在將與其他模型關聯的模型上,根據關係是一對多還是一對一,使用morphManymorphOne方法。

你能提供一個Laravel中多態關係的例子嗎?

當然,讓我們考慮一個博客平台,其中帖子和用戶都可以有評論。在這種情況下,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通過morphToManymorphedByMany方法支持多對多多態關係。這允許一個模型在多對多的基礎上屬於多個模型。

如何在數據庫遷移中處理多態關係?

在數據庫遷移中,您通常會向將接收多態關係的表中添加兩列:一列用於相關模型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_idcommentable_type列。

以上是重新引入雄辯的多態關係的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn