我想尽可能避免继承,但是现有代码中有一个需要处理的情况。
请考虑一个CartItem
模型,该模型将被CartItemTypeX
和CartItemTypeY
等不同的实现模型继承
这三个模型共享相同的数据库表cart_items,并且具有完全相同的结构。其中一列名为payload
,每种类型的内容都可能不同,这就是我们拥有不同模型的原因。
该表还具有一列type
,该列将通过以下简单逻辑自动保存:
static::creating(function (Model $model) {
$model->type = $model->getMorphClass();
});
到目前为止,一切都很好。每个CartItem都已使用正确的类型正确保存到数据库中。
现在,我们有一些路由接受CartItem
作为参数。
例如,让我们考虑以下内容:
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)
}
}
在解决此问题时,我还有其他障碍。例如,我们的乐观锁定机制将模型的版本保存在相关模型中。它使用变体关系来完成此操作,因此它具有变体类CartItemTypeX
而不是CartItem
,因为我们从不保存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();
/* ... */
}
}
也可以,但是上面讨论的我的版本控制特性对我来说失败了。在检索Model时,此特征通过morph关系获取相关信息。从数据库中检索时,它的类型为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
,因为将路由参数注入了控制器方法,而第二次是获取实现。>
肯定有更好的方法。任何想法或见解将不胜感激!