Yii2 // 模型作为模型属性
Yii2 // Model as Model attribute
实际上我正在做一个项目,我想把所有的数据库表都作为模型。但现在我停留在一点。
假设我有一个 "Master"-Table,其中定义了许多不同的关系,如下所示(简单示例):
人有一颗心;人只有一个大脑……等等……
是否可以用其他模型填充主模型?
在 PHP 中它看起来像这样:
$human = new Human();
$human->heart = new Heart();
$human->brain = new Brain();
最后我想说:
$human-save(TRUE);
验证所有关系模型并将所有关系数据和人类对象保存在数据库中。
这可能吗?我在整个互联网上找不到类似的东西 O_o。
非常感谢!
您可以覆盖 ActiveModel
Save
方法,根据 docs:
public function save($runValidation = true, $attributeNames = null)
{
if ($this->getIsNewRecord()) {
$save = $this->insert($runValidation, $attributeNames);
} else {
$save = $this->update($runValidation, $attributeNames) !== false;
}
/* Condition Work if heart and brain is also ActiveModel then
you can trigger save method on these models as well
or you can add your custom logic as well.
*/
if($this->heart && $this->brain) {
return $this->heart->save() && $this->brain->save();
}
return $save;
}
我建议您采用以下方法:
- 假设您的关系名称与嵌套对象的 属性 名称相同(调用
$model->link()
方法需要一些规则)
- 为具有嵌套模型(例如 ActiveRecordWithNestedModels)的模型声明通用 class
- 覆盖常见的 class 方法
save
和 validate
以对这些操作执行级联(使用反射)
- 让你的模型继承这个共同点class
或,作为覆盖validate
方法的替代方法,您可以为rules
公共方法构建一些合适的实现class。
这个常见的class can看起来如下(这是一个简单的草稿,没有测试,只是为了展示概念):
<?php
namespace app\models;
use yii\db\ActiveRecord;
class ActiveRecordWithNestedModels extends ActiveRecord
{
public function save($runValidation = true, $attributeNames = null)
{
$saveResult = parent::save($runValidation, $attributeNames);
$class = new \ReflectionClass($this);
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$propertyValue = $property->getValue($this);
if (!empty($propertyValue) && is_subclass_of($propertyValue, ActiveRecord::className())) {
/* @var ActiveRecord $nestedModel */
$nestedModel = $propertyValue;
$nestedModel->save($runValidation);
$relation = $property->name;
$this->link($relation, $nestedModel);
}
}
return $saveResult;
}
public function validate($attributeNames = null, $clearErrors = true)
{
$class = new \ReflectionClass($this);
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$propertyValue = $property->getValue($this);
if (!empty($propertyValue) && is_subclass_of($propertyValue, ActiveRecord::className())) {
/* @var ActiveRecord $nestedModel */
$nestedModel = $propertyValue;
if (!$nestedModel->validate(null, $clearErrors)) {
array_push($this->errors, [
$property->name => $nestedModel->errors
]);
}
}
}
parent::validate($attributeNames, $clearErrors);
if ($this->hasErrors()) return false;
return true;
}
}
那么你的模型可以是这样的:
class Heart extends ActiveRecordWithNestedModels
{
}
class Human extends ActiveRecordWithNestedModels
{
/* @var Heart $heart */
public $heart = null;
/**
* The relation name will be 'heart', same as property `heart'
*
* @return \yii\db\ActiveQuery
*/
public function getHeart()
{
return $this->hasOne(Heart::className(), ['id', 'heart_id']);
}
}
而且(理论上)您可以这样做:
$human = new Human();
$human->heart = new Heart();
$human->save();
P.S. 这里可以有很多复杂的细节进一步实现,例如
- 如果某些子对象保存失败
,则使用事务回滚save
- 覆盖
delete
- 服务
one-to-many
和 many-to-many
关系
- 如果属性没有对应关系则跳过级联
- 在级联操作中提供
$attributeNames
- 等
实际上我正在做一个项目,我想把所有的数据库表都作为模型。但现在我停留在一点。 假设我有一个 "Master"-Table,其中定义了许多不同的关系,如下所示(简单示例):
人有一颗心;人只有一个大脑……等等…… 是否可以用其他模型填充主模型? 在 PHP 中它看起来像这样:
$human = new Human();
$human->heart = new Heart();
$human->brain = new Brain();
最后我想说:
$human-save(TRUE);
验证所有关系模型并将所有关系数据和人类对象保存在数据库中。
这可能吗?我在整个互联网上找不到类似的东西 O_o。
非常感谢!
您可以覆盖 ActiveModel
Save
方法,根据 docs:
public function save($runValidation = true, $attributeNames = null)
{
if ($this->getIsNewRecord()) {
$save = $this->insert($runValidation, $attributeNames);
} else {
$save = $this->update($runValidation, $attributeNames) !== false;
}
/* Condition Work if heart and brain is also ActiveModel then
you can trigger save method on these models as well
or you can add your custom logic as well.
*/
if($this->heart && $this->brain) {
return $this->heart->save() && $this->brain->save();
}
return $save;
}
我建议您采用以下方法:
- 假设您的关系名称与嵌套对象的 属性 名称相同(调用
$model->link()
方法需要一些规则) - 为具有嵌套模型(例如 ActiveRecordWithNestedModels)的模型声明通用 class
- 覆盖常见的 class 方法
save
和validate
以对这些操作执行级联(使用反射) - 让你的模型继承这个共同点class
或,作为覆盖validate
方法的替代方法,您可以为rules
公共方法构建一些合适的实现class。
这个常见的class can看起来如下(这是一个简单的草稿,没有测试,只是为了展示概念):
<?php
namespace app\models;
use yii\db\ActiveRecord;
class ActiveRecordWithNestedModels extends ActiveRecord
{
public function save($runValidation = true, $attributeNames = null)
{
$saveResult = parent::save($runValidation, $attributeNames);
$class = new \ReflectionClass($this);
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$propertyValue = $property->getValue($this);
if (!empty($propertyValue) && is_subclass_of($propertyValue, ActiveRecord::className())) {
/* @var ActiveRecord $nestedModel */
$nestedModel = $propertyValue;
$nestedModel->save($runValidation);
$relation = $property->name;
$this->link($relation, $nestedModel);
}
}
return $saveResult;
}
public function validate($attributeNames = null, $clearErrors = true)
{
$class = new \ReflectionClass($this);
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$propertyValue = $property->getValue($this);
if (!empty($propertyValue) && is_subclass_of($propertyValue, ActiveRecord::className())) {
/* @var ActiveRecord $nestedModel */
$nestedModel = $propertyValue;
if (!$nestedModel->validate(null, $clearErrors)) {
array_push($this->errors, [
$property->name => $nestedModel->errors
]);
}
}
}
parent::validate($attributeNames, $clearErrors);
if ($this->hasErrors()) return false;
return true;
}
}
那么你的模型可以是这样的:
class Heart extends ActiveRecordWithNestedModels
{
}
class Human extends ActiveRecordWithNestedModels
{
/* @var Heart $heart */
public $heart = null;
/**
* The relation name will be 'heart', same as property `heart'
*
* @return \yii\db\ActiveQuery
*/
public function getHeart()
{
return $this->hasOne(Heart::className(), ['id', 'heart_id']);
}
}
而且(理论上)您可以这样做:
$human = new Human();
$human->heart = new Heart();
$human->save();
P.S. 这里可以有很多复杂的细节进一步实现,例如
- 如果某些子对象保存失败 ,则使用事务回滚
- 覆盖
delete
- 服务
one-to-many
和many-to-many
关系 - 如果属性没有对应关系则跳过级联
- 在级联操作中提供
$attributeNames
- 等
save