首页 >后端开发 >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