简体   繁体   English

Yii2 //将模型作为模型属性

[英]Yii2 // Model as Model attribute

actually im working on a project, where i want to have all DB-tables as Models. 实际上我在一个项目上工作,我想将所有数据库表都作为模型。 But now im stucking at one Point. 但是现在我停留在某一点。 Lets say i have a "Master"-Table where many different relations are defined like the following (easy example): 可以说我有一个“主”表,其中定义了许多不同的关系,如下所示(简单的示例):

Human has one heart; 人有一颗心; Human has one brain... and so on... Is it possible, to fill up the Master-Model with other Models? 人类有一个大脑……等等……是否有可能用其他模型填充主模型? In PHP it would looks like that: 在PHP中看起来像这样:

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

Finally i want to say: 最后我想说:

$human-save(TRUE);

to VALIDATE all relational models AND save all relational data and the human object in DB. 验证所有关系模型并将所有关系数据和人类对象保存在DB中。

Is that possible? 那可能吗? I cant find something like that on the whole internet O_o. 我在整个互联网上都找不到类似的内容O_o。

Thank you very much! 非常感谢你!

You can override ActiveModel Save method, according to docs : 您可以根据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;

}

I suggest you following approach: 我建议您采用以下方法:

  1. Let's say you have same relation names as property names for nested objects (some rule needed to call $model->link() method) 假设您具有与嵌套对象的属性名称相同的关系名称(调用$model->link()方法需要一些规则)
  2. Declare common class for Models with nested Models (for example ActiveRecordWithNestedModels) 为具有嵌套模型的模型声明通用类(例如ActiveRecordWithNestedModels)
  3. Override in common class methods save and validate to perform cascade for these operations (using reflection) 覆盖通用类方法savevalidate以执行这些操作的级联(使用反射)
  4. Let your models will inherit this common class 让您的模型继承这个通用类

Or , as an alternative for overriding validate method, you can build some suitable implementation for rules method in common class. 或者 ,作为压倒一切的另一种validate方法,你可以建立一些适合的实施rules共同类方法。

This common class can looks as follows ( this is a simple draft, not tested, just to show the conception ): 该通用类如下所示( 这是一个简单的草稿,未经测试,仅用于显示概念 ):

<?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;
    }

}

Then your models can looks like this: 然后您的模型可以如下所示:

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']);
    }
}

And (in theory) you can do: 并且(理论上),您可以执行以下操作:

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

PS here can be many complex details in further implementation, as for example 此处的PS在进一步的实现中可能包含许多复杂的细节,例如

  • using transactions to rollback save if some child object fails save 使用事务回滚save ,如果一些子对象无法保存
  • overriding delete 覆盖delete
  • serving one-to-many and many-to-many relations 服务one-to-manymany-to-many关系
  • skip cascade if property has no corresponding relation 如果属性没有对应关系,则跳过级联
  • serving $attributeNames in cascade operations 在级联操作中提供$attributeNames
  • etc 等等

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM