php - Laravel 模型继承:如何从父级实例化正确类型的模型
问题描述
我想尽可能地避免继承,但我在现有代码中有一个需要处理的案例。
考虑一个CartItem
被不同实现模型继承的模型,例如CartItemTypeX
和CartItemTypeY
这三个模型共享同一个数据库表cart_items,并且具有完全相同的结构。其中一列已命名payload
,并且每种类型的内容可能不同,这就是我们有不同模型的原因。
该表还有一列type
通过以下简单逻辑自动保存:
static::creating(function (Model $model) {
$model->type = $model->getMorphClass();
});
到目前为止,一切都很好。每个 CartItem 都以其正确的类型正确保存到数据库中。
现在,我们有一些接受 aCartItem
作为参数的路由。例如,让我们考虑以下内容:
class SomeController {
public function update(CartItemRequest $request, CartItem $cartItem) {
// Our logic goes here...
// We would like $cartItem to automatically be of the implementation type (CartItemTypeX for instance)
}
}
在解决这个问题时,我还有一些额外的障碍要克服。例如,我们的乐观锁定机制将模型的版本 if 保存在相关模型中。它使用 morph 关系来做到这一点,所以它有 morph 类CartItemTypeX
,而不是CartItem
因为我们从不保存 a这样的CartItem
类。
这是我一直在研究的。
重写newFromBuilder
方法CartItem
我尝试在类上覆盖此方法,如下所示:
public function newFromBuilder($attributes = [], $connection = null)
{
$classname = Relation::getMorphedModel($attributes->type);
$instance = new $classname();
$instance->exists = true;
$instance->setRawAttributes((array) $attributes, true);
$instance->setConnection($connection ?: $this->getConnectionName());
$instance->fireModelEvent('retrieved', false);
return $instance;
}
当我通过构建器检索模型时,这似乎工作正常(例如CartItem::whereIsNull('order_id')->get()
将返回CartItemTypeX
模型(我只记得我没有尝试使用不同的购物车类型进行测试,但我怀疑这会工作......)
但是,当CartItem
从路由注入到我的控制器中时,它仍然是类型的模型,CartItem
而不是CartItemTypeX
所以这对我没有用。
只需添加一个方法来获取实现
所以我忘了把这个责任委托给 Eloquent(这是我的偏好,因为我想让我的控制器和业务代码尽可能干净和简单)
然后我尝试在添加到类的新方法中应用与上述相同的逻辑CartItem
:
class CartItem {
public function getImplementation() {
$classname = Relation::getMorphedModel($this->type);
$instance = new $classname();
$instance->exists = $this->exists;
$instance->setRawAttributes((array) $this->attributes, true);
$instance->setConnection($this->connection ?: $this->getConnectionName());
return $instance;
}
}
然后在我的控制器中,我可以轻松地做到这一点:
class MyController {
public function view(Request $request, CartItem $cartItem) {
$cartItem = $cartItem->getImplementation();
/* ... */
}
}
这也有效,但我上面讨论的版本控制特性对我来说是失败的。在检索模型时,此特征通过变形关系获取相关的。由于它是CartItem
从数据库中检索的类型,它使用变形类型CartItem
而不是CartItemTypeX
从数据库中检索它。
因此,仅通过在代码中实例化正确的实现类并原始设置属性(这是一个词吗?),我们没有正确的值,version
在保存模型时会引发冲突。
那么回到第一个解决方案?
我在代码中继承的第一个解决方案是这样的:
class CartItem {
public function getImplementation() {
$classname = Relation::getMorphedModel($this->type);
return $classname::find($this->id);
}
}
是的,这行得通。我进入CartItem
并通过执行$cartItem->getImplementation()
,Eloquent 正在CartItemTypeX
从数据库中检索 。版本控制(乐观锁定)的特性也可以正常工作,因为我们只是从数据库中重新获取它......
哦 - 又是那个问题......一个新的检索。这个实现显然是在扼杀性能,对于检索 CartItemTypeX,我们现在总是有两次检索:一次是CartItem
因为路由参数注入到控制器方法中,第二次是当我们获取实现时。
肯定有更好的方法。任何想法或见解将不胜感激!
解决方案
推荐阅读
- swift - 需要在 viewcontroller 和 navigationcontroller 中设置两次目标方法 - swift - 以编程方式
- webpack - Webpack sass 到 css 并保留文件夹结构
- javascript - 使用 express/mongoose 将用户 IP 地址保存到数据库
- swift - 带有多平台应用程序的 Swift 包管理器
- javascript - 如何在 React Router 中使用 Link 链接操作?
- javascript - javascript下拉选择时间条件电流
- r - R中的条件数据表列总和
- vue.js - 如何推送到数组(在 Vue 3 中)?
- c - C 编程,unicode/UTF-8 特殊字符未显示在 linux 控制台上
- python - Pandas - 遍历数据帧行并更新 df(一行代码)