在 Yii2 中以多态方式从数据库中查找模型
Polymorphically find model from database in Yii2
我在数据库中有一个 table(mysql)。但是这个 table 存储了几个稍微不同类型的行。类型取决于此 table 的 type
列。我有一个 table 的抽象 ActiveRecord class 和几个后代子 classes 为不同类型的相同 table 的行实现稍微不同的逻辑。现在我正在为所有类型的行实施更新控制器操作。我获得了行的 ID,需要创建一个 ActiveRecord 实例来表示具有此 ID 的行。但是我不知何故需要根据相应行的类型创建不同子 classes 的实例。
如果同时提供类型和 ID,我可以使用工厂来选择相应的子class。但是我已经可以在数据库中拥有该类型,并且 id 为我提供了足够的信息以从那里选择它。但是,如果我先从数据库中选择类型,然后创建相应子 class 的实例,那将意味着执行相同的查询两次。
我想找到一种从数据库中获取数据的好方法,然后选择一个合适的 ActiveRecord subclass 为其创建一个实例,而不需要进行过多的查询或需要过多的数据。 Yii2 有办法吗?
或者我应该以不同的方式处理这个问题吗?实际的问题是有几个几乎相同但有点不同的实体存储在一个 table 中,业务逻辑有点不同。
解决此问题的方法之一称为 "Single table inheritance",由 Martin Fowler here. There is also good article about its implementation in Yii 2 in samdark's (one of the main Yii 2 contributors) cookbook, which is currently in process of writing but is available on Github 描述。
我不会复制整篇文章,但只留下 link 也是不够的。这里有一些重要的事情:
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);
}
}
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);
}
}
重型车:
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);
}
}
3) 将 Car
模型中的 instantiate()
方法覆盖为 return 正确的汽车类型:
public static function instantiate($row)
{
switch ($row['type']) {
case SportCar::TYPE:
return new SportCar();
case HeavyCar::TYPE:
return new HeavyCar();
default:
return new self;
}
}
然后您可以将任何类型的汽车单独用作普通模型。
我在数据库中有一个 table(mysql)。但是这个 table 存储了几个稍微不同类型的行。类型取决于此 table 的 type
列。我有一个 table 的抽象 ActiveRecord class 和几个后代子 classes 为不同类型的相同 table 的行实现稍微不同的逻辑。现在我正在为所有类型的行实施更新控制器操作。我获得了行的 ID,需要创建一个 ActiveRecord 实例来表示具有此 ID 的行。但是我不知何故需要根据相应行的类型创建不同子 classes 的实例。
如果同时提供类型和 ID,我可以使用工厂来选择相应的子class。但是我已经可以在数据库中拥有该类型,并且 id 为我提供了足够的信息以从那里选择它。但是,如果我先从数据库中选择类型,然后创建相应子 class 的实例,那将意味着执行相同的查询两次。
我想找到一种从数据库中获取数据的好方法,然后选择一个合适的 ActiveRecord subclass 为其创建一个实例,而不需要进行过多的查询或需要过多的数据。 Yii2 有办法吗?
或者我应该以不同的方式处理这个问题吗?实际的问题是有几个几乎相同但有点不同的实体存储在一个 table 中,业务逻辑有点不同。
解决此问题的方法之一称为 "Single table inheritance",由 Martin Fowler here. There is also good article about its implementation in Yii 2 in samdark's (one of the main Yii 2 contributors) cookbook, which is currently in process of writing but is available on Github 描述。
我不会复制整篇文章,但只留下 link 也是不够的。这里有一些重要的事情:
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);
}
}
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);
}
}
重型车:
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);
}
}
3) 将 Car
模型中的 instantiate()
方法覆盖为 return 正确的汽车类型:
public static function instantiate($row)
{
switch ($row['type']) {
case SportCar::TYPE:
return new SportCar();
case HeavyCar::TYPE:
return new HeavyCar();
default:
return new self;
}
}
然后您可以将任何类型的汽车单独用作普通模型。