当前位置: 代码迷 >> 综合 >> 理解laravel中 Eloquent 关联中的多态关联(Polymorphic Relations)
  详细解决方案

理解laravel中 Eloquent 关联中的多态关联(Polymorphic Relations)

热度:70   发布时间:2023-11-25 02:29:22.0

简介

你可能会这样设计你的博客系统:一张文章表(posts)和一张评论表(comments)。

postsid - integertitle - stringbody - textcommentsid - integerbody - textpost_id - integer

突然有一天,你开始录播视频教程了,那么就会多一个张视频表(videos)。

videosid - integertitle - stringurl - string

此时,为了能够重用之前的评论表,就要对评论表修改了。怎么改才好呢?用冗余字段?

commentsid - integerbody - textpost_id - integervideo_id - integer

这当然没问题!但是,如果以后又多了什么图片、音频、名人名言之类的内容,它们也都可以评论,那是否就意味着评论表又变了?

commentsid - integerbody - textpost_id - integervideo_id - integerimage_id - integeraudio_id - integerquote_id - integer

这让人抓狂,因为冗余字段实在太多了,对于后台逻辑判断也是负担。Laravel 提供的解决方案是这样的:

commentsid - integerbody - textcommentable_id - integercommentable_type - string

使用 commentable_id 和 commentable_type 两个字段替代冗余字段的方式。comments 表的内容类似于这样:

id body commentable_id commentable_type
1 这是文章 1 的评论 1 posts
2 这是文章 2 的评论 2 posts
3 这是视频 1 的评论 1 videos
4 这是视频 2 的评论 2 videos
5 这是音频 1 的评论 1 audios
6 这是音频 2 的评论 2 audios

这样即使日后增加新的内容类型,只要定义一个新的 commentable_type 值就可以了。

我们称 Comment Model 与 Post Model、Video Model 的关系是多态关系,而在它们的 Model 中定义的关联称为多态关联

实现

创建表

php artisan make:model Models/Post -m -cphp artisan make:model Models/Video -m -cphp artisan make:model Models/Comment -m -c
Schema::create('posts', function (Blueprint $table) {$table->increments('id');$table->string('title')->unique();$table->text('body');$table->timestamps();
});Schema::create('videos', function (Blueprint $table) {$table->increments('id');$table->string('title');$table->string('url')->unique();$table->timestamps();
});Schema::create('comments', function (Blueprint $table) {$table->increments('id');$table->text('body');$table->unsignedInteger('commentable_id');$table->string('commentable_type');$table->timestamps();
});
php artisan migrate

定义关联关系

class Comment extends Model
{protected $fillable = ['body'];/*** 取得评论的文章/视频。** @return \Illuminate\Database\Eloquent\Relations\MorphTo*/public function commentable(){return $this->morphTo();}
}class Post extends Model
{const TABLE = 'posts';protected $table = self::TABLE;/*** 取得文章评论** @return \Illuminate\Database\Eloquent\Relations\MorphMany*/public function comments(){return $this->morphMany(Comment::class, 'commentable');}
}class Video extends Model
{const TABLE = 'videos';protected $table = self::TABLE;/*** 取得视频评论** @return \Illuminate\Database\Eloquent\Relations\MorphMany*/public function comments(){return $this->morphMany(Comment::class, 'commentable');}
}

在 AppServiceProvider boot 方法中自定义多态关联的类型字段。

use App\Models\Post;
use App\Models\Video;
use Illuminate\Database\Eloquent\Relations\Relation;public function boot()
{$this->bootEloquentMorphs();
}/*** 自定义多态关联的类型字段*/
private function bootEloquentMorphs()
{Relation::morphMap([Post::TABLE => Post::class,Video::TABLE => Video::class,]);
}

插入数据

在 ModelFactory 中定义 Model 的工厂方法。

use App\Models\Post;
use App\Models\Video;
use App\Models\Comment;$factory->define(Post::class, function (Faker\Generator $faker) {return ['title' => $faker->sentence,'body' => $faker->text,];
});$factory->define(Video::class, function (Faker\Generator $faker) {return ['title' => $faker->sentence,'url' => $faker->url,];
});$factory->define(Comment::class, function (Faker\Generator $faker) {return ['body' => $faker->text,'commentable_id' => factory(Post::class)->create()->id,'commentable_type' => Post::TABLE,];//    return [
//        'body' => $faker->text,
//        'commentable_id' => factory(Video::class)->create()->id,
//        'commentable_type' => Video::TABLE,
//    ];
});

插入伪数据。

php artisan tinker>>> namespace App;
>>> factory(Models\Comment::class, 10)->create();

使用

php artisan tinker>>> namespace App\Models;
>>> $post = Post::find(1);
>>> $post->comments
=> Illuminate\Database\Eloquent\Collection {#733all: [App\Models\Comment {#691id: 1,body: "Ut omnis voluptatem esse mollitia nisi saepe vero. Est sed et eius pariatur hic harum sed. Laboriosam autem quis vel optio fugiat tota
m laboriosam.",commentable_id: 1,commentable_type: "posts",created_at: "2017-07-21 02:42:17",updated_at: "2017-07-21 02:42:17",},],}
>>> $comment = Models\Comment::find(1);
>>> $comment->commentable
=> App\Models\Post {#731id: 4,title: "Earum est nisi praesentium numquam nisi.",body: "Dicta quod dolor quibusdam aut. Ut at numquam dolorem non modi adipisci vero sit. Atque enim cum ut aut dolore voluptas.",created_at: "2017-07-21 02:42:17",updated_at: "2017-07-21 02:42:17",}
>>> Post::find(1)->comments()->save(new Comment(['body' => 'a new comment']));
=> App\Models\Comment {#711body: "a new comment",commentable_type: "posts",commentable_id: 1,updated_at: "2017-07-21 06:45:28",created_at: "2017-07-21 06:45:28",id: 11,}