Yii2 //模型作为模型属性

时间:2016-07-19 07:32:46

标签: php validation yii2 models composite

实际上我正在开发一个项目,我希望将所有数据库表作为模型。但现在我坚持一点。 让我们说我有一个“大师” - 表,其中许多不同的关系定义如下(简单示例):

人类有一颗心;人类有一个大脑......依此类推...... 是否可以用其他模型填充Master-Model? 在PHP中它看起来像:

$human = new Human();
$human->heart = new Heart(); 
$human->brain = new Brain(); 

最后我想说:

$human-save(TRUE);

验证所有关系模型并将所有关系数据和人类对象保存在DB中。

这可能吗?我无法在整个互联网O_o上找到类似的东西。

非常感谢!

2 个答案:

答案 0 :(得分:1)

根据docs,您可以覆盖ActiveModel Save方法:

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;

}

答案 1 :(得分:1)

我建议您遵循以下方法:

  1. 假设您拥有与嵌套对象的属性名称相同的关系名称(调用$model->link()方法需要一些规则)
  2. 为具有嵌套模型的模型声明公共类(例如ActiveRecordWithNestedModels)
  3. 覆盖常用的类方法savevalidate以执行这些操作的级联(使用反射)
  4. 让你的模型继承这个通用类
  5. ,作为覆盖validate方法的替代方法,您可以在公共类中为rules方法构建一些合适的实现。

    这个普通类看起来如下(这是一个简单的草案,未经过测试,只是为了显示概念):

    <?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-manymany-to-many关系
    • 如果属性没有对应关系,则跳过级联
    • 在级联操作中提供$attributeNames
    • etc