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:
$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.
Is that possible? I cant find something like that on the whole internet O_o.
Thank you very much!
I suggest you following approach:
$model->link()
method)save
and validate
to perform cascade for these operations (using reflection) Or, as an alternative for overriding validate
method, you can build some suitable implementation for rules
method in common class.
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();
P.S. here can be many complex details in further implementation, as for example
save
if some child object fails savedelete
one-to-many
and many-to-many
relations$attributeNames
in cascade operations