在Yii2中从数据库多态找到模型

Ghe*_*man 4 php database polymorphism model-view-controller yii2

我在数据库中有一个表(mysql).但是这个表存储了几种略有不同的行类型.类型取决于此表的type列.我有一个表的抽象ActiveRecord类和几个后代子类,为不同类型的同一个表的行实现略有不同的逻辑.现在我正在为所有类型的行实现更新控制器操作.我提供了行的id,需要创建一个ActiveRecord实例来表示具有此id的行.但我不知何故需要根据相应行的类型创建不同子类的实例.

如果我同时提供了类型和id,我可以使用工厂来选择相应的子类.但是我已经可以在数据库中输入类型了,而id会给我足够的信息从那里选择它.但是,如果我首先从数据库中选择类型,然后创建相应子类的实例,这意味着执行相同的查询两次.

我想找到一种从数据库中获取数据的好方法,然后选择一个正确的ActiveRecord子类来为它创建一个实例,而不会产生过多的查询或需要过多的数据.有没有办法做到Yii2?

或者我应该以某种方式不同地处理这个问题?实际问题是几个几乎相同但存在于具有不同业务逻辑的单个表中的一些不同实体.

aro*_*hev 7

解决这个问题的方法之一称为"单表继承",Martin Fowler 在此描述.在samdark(主要的Yii 2贡献者之一)食谱中也有关于它在Yii 2中的实现的好文章,该文章目前正在编写,但可以在Github上获得.

我不会复制整篇文章,但只留下链接也是不够的.以下是一些重要的事情:

1)为所有类型的对象(例如汽车)创建通用查询:

namespace app\models;

use yii\db\ActiveQuery;

class CarQuery extends ActiveQuery {
    public $type;

    public function prepare($builder)
    {
        if ($this->type !== null) {
            $this->andWhere(['type' => $this->type]);
        }
        return parent::prepare($builder);
    }
}
Run Code Online (Sandbox Code Playgroud)

2)为每种类型创建单独的模型(从普通模型Car扩展):

跑车:

namespace app\models;

class SportCar extends Car
{
    const TYPE = 'sport';

    public static function find()
    {
        return new CarQuery(get_called_class(), ['type' => self::TYPE]);
    }

    public function beforeSave($insert)
    {
        $this->type = self::TYPE;
        return parent::beforeSave($insert);
    }
}
Run Code Online (Sandbox Code Playgroud)

重型车:

namespace app\models;

class HeavyCar extends Car
{
    const TYPE = 'heavy';

    public static function find()
    {
        return new CarQuery(get_called_class(), ['type' => self::TYPE]);
    }

    public function beforeSave($insert)
    {
        $this->type = self::TYPE;
        return parent::beforeSave($insert);
    }
}
Run Code Online (Sandbox Code Playgroud)

3)instantiate()Car模型中覆盖方法以返回正确类型的汽车:

public static function instantiate($row)
{
    switch ($row['type']) {
        case SportCar::TYPE:
            return new SportCar();
        case HeavyCar::TYPE:
            return new HeavyCar();
        default:
           return new self;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以单独使用任何类型的汽车作为常规车型.

  • 因此,通过覆盖“实例化”,我可以根据从数据库中检索到的数据从不同的ActiveRecord子类中进行选择。那正是我要问的!这样,在处理现有行时,我就无需向控制器提供类型。万分谢意! (2认同)