laravel orm 的多态有的时候是真的好用。比如,我们在设计一个消息表的时候,这个多态就很有用。消息对于整个项目,是一个基础的统称。比如,有人注册账号了,给他一个欢迎的消息。 有人充值了一个会员,给他一个充值消息。有人参加了一个活动,给他一个参加的消息。就这样一个场景。在消息表里边,我们仅仅需要构建这些字段
user_id、content、messageable_id、messageable_type、status、updated_at、created_at
就可以了。是不是很干净,是不是很舒适。后端的东西往往是给前端服务的。我们要会联想到前端的一个场景。有一个消息列表,我们点击消息,应该可以查看到消息的详情。比如会员消息详情、充值详情(当然有的时候是不需要详情,比如欢迎语。)这个时候,多态一对一在这个场景就很符合。这个多态和面向对象里的多态(继承、接口)不一样,但有相似的意境。
开始
多态的重要信息是 xxxable_id、和 xxxable_type。一个对应的是目标对象的 id,一个对应的是目标对象的类名(比如 App\Models\Topic)。
在 xxx 模型中。我们要定义一个 xxxable 方法。这里以 Message 模型为例。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Message extends Model
{
use HasFactory;
protected $fillable = ['user_id', 'content', 'messageable_id', 'messageable_type'];
public function user()
{
return $this->belongsTo(User::class);
}
/**
* 获取父级的对象
*/
public function messageable(): MorphTo
{
return $this->morphTo();
}
}
在父级对象中,你也可以定义相对应的关系。当然也可以不定义。在这个场景中,我们重要的是消息列表以及消息对应的对象。所以仅仅在消息中定义 morph 就好。
使用
比如: Message::query()->take(10)->with('messageable')->get();
一个完整的过程。我们有这三个模型:Message(消息)、Topic(话题)、Member(会员)。
$user = $request->user();
$data = [xxx];
$topic = Topic::create($data);
# 获取类名
$topicClass = get_class($topic);
# 创建消息
Message::create([
'user_id' => $user->id,
'content' => '您的话题已创建,请等待审核',
'messageable_id' => $topic->id,
'messageable_type' => $topicClass
]);
---------------------------------------
$member = Member::create($data);
Message::create([
'user_id' => $user->id,
'content' => '您已成为会员,请愉快的玩耍吧',
'messageable_id' => $member->id,
'messageable_type' => get_class($member)
]);
再啰嗦一点点
上边说了,前端是要点击消息,做对应的跳转的。既然后端是动态绑定(多态算是动态的一种绑定吧),那么前端就是一个强制的硬性的绑定了,可以理解为是枚举。可以用 if 来搞定。因为消息的种类就那么多,不像数据库,有成千上万百万千万的数据。
if (消息类型一) {
跳转到一
} else if (消息类型二) {
跳转到二
}
因为有了 messageable_id,我们就可以通过接口来请求各自所想要的信息了。
对于消息列表。如果没有特殊的要求 messageable
我们也是不需要在这里就请求上的。因为请求了,就需要数据库去查询。查询了又不用,那不是浪费。这里看具体情况哈。
中文文档
https://learnku.com/docs/laravel/10.x/eloquent-relationshipsmd/14889#5981a6