核心要点
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中文网其他相关文章!