На самом деле я работаю над проектом, где я хочу иметь все DB-таблицы в качестве моделей. Но теперь я застрял в одной точке.
Допустим, у меня есть таблица «Master», в которой определено много разных отношений, как показано ниже (простой пример):
У человека одно сердце; У человека один мозг … и так далее …
Возможно ли заполнить мастер-модель другими моделями?
В PHP это выглядит так:
$human = new Human();
$human->heart = new Heart();
$human->brain = new Brain();
Напоследок хочу сказать:
$human-save(TRUE);
Валидировать все реляционные модели и сохранять все реляционные данные и человеческий объект в БД.
Это возможно? Я не могу найти что-то подобное во всем Интернете O_o.
Большое спасибо!
Я предлагаю вам следующий подход:
$model->link()
метод)save
а также validate
выполнить каскад для этих операций (используя отражение) Или же, в качестве альтернативы для переопределения 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();
Постскриптум здесь может быть много сложных деталей в дальнейшей реализации, как, например,
save
если какой-либо дочерний объект не удается сохранитьdelete
one-to-many
а также many-to-many
связи$attributeNames
в каскадных операцияхВы можете переопределить 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;
}