Yii2 // Модель как атрибут модели

На самом деле я работаю над проектом, где я хочу иметь все DB-таблицы в качестве моделей. Но теперь я застрял в одной точке.
Допустим, у меня есть таблица «Master», в которой определено много разных отношений, как показано ниже (простой пример):

У человека одно сердце; У человека один мозг … и так далее …
Возможно ли заполнить мастер-модель другими моделями?
В PHP это выглядит так:

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

Напоследок хочу сказать:

$human-save(TRUE);

Валидировать все реляционные модели и сохранять все реляционные данные и человеческий объект в БД.

Это возможно? Я не могу найти что-то подобное во всем Интернете O_o.

Большое спасибо!

1

Решение

Я предлагаю вам следующий подход:

  1. Допустим, у вас есть те же имена отношений, что и имена свойств для вложенных объектов (необходимо вызвать какое-то правило $model->link() метод)
  2. Объявите общий класс для моделей с вложенными моделями (например, ActiveRecordWithNestedModels)
  3. Переопределить в общих методах класса save а также validate выполнить каскад для этих операций (используя отражение)
  4. Пусть ваши модели унаследуют этот общий класс

Или же, в качестве альтернативы для переопределения 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 в каскадных операциях
  • так далее
1

Другие решения

Вы можете переопределить 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

По вопросам рекламы [email protected]