@ -13,6 +13,7 @@ use yii\db\BaseActiveRecord;
use yii\db\Transaction;
use yii\db\Transaction;
use yii\helpers\ArrayHelper;
use yii\helpers\ArrayHelper;
use yii\helpers\Inflector;
use yii\helpers\Inflector;
use yii\helpers\VarDumper;
/**
/**
* This Active Record Behavior allows to validate and save the Model relations when the save() method is invoked.
* This Active Record Behavior allows to validate and save the Model relations when the save() method is invoked.
@ -29,12 +30,13 @@ class SaveRelationsBehavior extends Behavior
private $_transaction;
private $_transaction;
private $_relationsScenario = [];
private $_relationsScenario = [];
private $_relationsCascadeDelete = [];
//private $_relationsCascadeDelete = []; //TODO
public function init()
public function init()
{
{
parent::init();
parent::init();
$allowedProperties = ['scenario', 'cascadeDelete' ];
$allowedProperties = ['scenario'];
foreach ($this->relations as $key => $value) {
foreach ($this->relations as $key => $value) {
if (is_int($key)) {
if (is_int($key)) {
$this->_relations[] = $value;
$this->_relations[] = $value;
@ -102,8 +104,16 @@ class SaveRelationsBehavior extends Behavior
if (in_array($name, $this->_relations)) {
if (in_array($name, $this->_relations)) {
Yii::trace("Setting {$name} relation value", __METHOD__);
Yii::trace("Setting {$name} relation value", __METHOD__);
if (!isset($this->_oldRelationValue[$name])) {
if (!isset($this->_oldRelationValue[$name])) {
if ($this->owner->isNewRecord) {
if ($this->owner->getRelation($name)->multiple === true) {
$this->_oldRelationValue[$name] = [];
} else {
$this->_oldRelationValue[$name] = null;
}
} else {
$this->_oldRelationValue[$name] = $this->owner->{$name};
$this->_oldRelationValue[$name] = $this->owner->{$name};
}
}
}
if ($this->owner->getRelation($name)->multiple === true) {
if ($this->owner->getRelation($name)->multiple === true) {
$this->setMultipleRelation($name, $value);
$this->setMultipleRelation($name, $value);
} else {
} else {
@ -174,8 +184,7 @@ class SaveRelationsBehavior extends Behavior
$fks[$modelAttribute] = $data[$modelAttribute];
$fks[$modelAttribute] = $data[$modelAttribute];
}
}
}
}
if (empty($fks)) {
if (!$fks) {
// Get the right link definition
// Get the right link definition
if ($relation->via instanceof BaseActiveRecord) {
if ($relation->via instanceof BaseActiveRecord) {
$viaQuery = $relation->via;
$viaQuery = $relation->via;
@ -186,7 +195,6 @@ class SaveRelationsBehavior extends Behavior
} else {
} else {
$link = $relation->link;
$link = $relation->link;
}
}
foreach ($link as $relatedAttribute => $modelAttribute) {
foreach ($link as $relatedAttribute => $modelAttribute) {
if (array_key_exists($modelAttribute, $data) & & !empty($data[$modelAttribute])) {
if (array_key_exists($modelAttribute, $data) & & !empty($data[$modelAttribute])) {
$fks[$modelAttribute] = $data[$modelAttribute];
$fks[$modelAttribute] = $data[$modelAttribute];
@ -317,9 +325,9 @@ class SaveRelationsBehavior extends Behavior
/** @var BaseActiveRecord $model */
/** @var BaseActiveRecord $model */
$model = $this->owner;
$model = $this->owner;
if (!is_null($relationModel) & & ($relationModel->isNewRecord || count($relationModel->getDirtyAttributes()))) {
if (!is_null($relationModel) & & ($relationModel->isNewRecord || count($relationModel->getDirtyAttributes()))) {
// if (key_exists($relationModel , $this->_relationsScenario)) {
if (key_exists($relationName , $this->_relationsScenario)) {
// $relationModel->setScenario($this->_relationsScenario[$relationModel ]);
$relationModel->setScenario($this->_relationsScenario[$relationName ]);
// }
}
Yii::trace("Validating {$pettyRelationName} relation model using " . $relationModel->scenario . " scenario", __METHOD__);
Yii::trace("Validating {$pettyRelationName} relation model using " . $relationModel->scenario . " scenario", __METHOD__);
if (!$relationModel->validate()) {
if (!$relationModel->validate()) {
foreach ($relationModel->errors as $attributeErrors) {
foreach ($relationModel->errors as $attributeErrors) {
@ -343,6 +351,7 @@ class SaveRelationsBehavior extends Behavior
/** @var BaseActiveRecord $model */
/** @var BaseActiveRecord $model */
$model = $this->owner;
$model = $this->owner;
$this->_relationsSaveStarted = true;
$this->_relationsSaveStarted = true;
try {
foreach ($this->_relations as $relationName) {
foreach ($this->_relations as $relationName) {
if (array_key_exists($relationName, $this->_oldRelationValue)) { // Relation was not set, do nothing...
if (array_key_exists($relationName, $this->_oldRelationValue)) { // Relation was not set, do nothing...
Yii::trace("Linking {$relationName} relation", __METHOD__);
Yii::trace("Linking {$relationName} relation", __METHOD__);
@ -350,18 +359,23 @@ class SaveRelationsBehavior extends Behavior
if ($relation->multiple === true) { // Has many relation
if ($relation->multiple === true) { // Has many relation
// Process new relations
// Process new relations
$existingRecords = [];
$existingRecords = [];
/** @var BaseActiveRecord $relationModel */
/** @var BaseActiveRecord $relationModel */
foreach ($model->{$relationName} as $relationModel) {
foreach ($model->{$relationName} as $relationModel) {
if ($relationModel->isNewRecord) {
if ($relationModel->isNewRecord) {
if ($relation->via !== null) {
if ($relation->via !== null) {
$relationModel->save(false);
if (!$relationModel->save()) {
throw new Exception('Related model ' . $relationName . ' could not be saved (' . VarDumper::dumpAsString($relationModel->getErrors()) . ')');
}
}
}
$model->link($relationName, $relationModel);
$model->link($relationName, $relationModel);
} else {
} else {
$existingRecords[] = $relationModel;
$existingRecords[] = $relationModel;
}
}
if (count($relationModel->dirtyAttributes)) {
if (count($relationModel->dirtyAttributes)) {
$relationModel->save(false);
if (!$relationModel->save()) {
throw new Exception('Related model ' . $relationName . ' could not be saved (' . VarDumper::dumpAsString($relationModel->getErrors()) . ')');
}
}
}
}
}
// Process existing added and deleted relations
// Process existing added and deleted relations
@ -394,6 +408,14 @@ class SaveRelationsBehavior extends Behavior
unset($this->_oldRelationValue[$relationName]);
unset($this->_oldRelationValue[$relationName]);
}
}
}
}
} catch (Exception $e) {
Yii::warning(get_class($e) . " was thrown during the saving of related records : " . $e->getMessage(), __METHOD__);
if (($this->_transaction instanceof Transaction) & & $this->_transaction->isActive) {
$this->_transaction->rollBack(); // If anything goes wrong, transaction will be rolled back
Yii::info("Rolling back", __METHOD__);
}
throw $e;
}
$model->refresh();
$model->refresh();
$this->_relationsSaveStarted = false;
$this->_relationsSaveStarted = false;
if (($this->_transaction instanceof Transaction) & & $this->_transaction->isActive) {
if (($this->_transaction instanceof Transaction) & & $this->_transaction->isActive) {