Laravel模型继承:如何从父级实例化正确类型的模型

时间:2019-08-07 14:09:52

标签: php laravel inheritance eloquent

我想尽可能避免继承,但是现有代码中有一个需要处理的情况。

请考虑一个CartItem模型,该模型将被CartItemTypeXCartItemTypeY等不同的实现模型继承

这三个模型共享相同的数据库表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,因为将路由参数注入了控制器方法,而第二次是获取实现。

肯定有更好的方法。任何想法或见解将不胜感激!

0 个答案:

没有答案