diff --git a/README.md b/README.md index 9fb3484..cdce6bb 100644 --- a/README.md +++ b/README.md @@ -131,5 +131,9 @@ Validation ---------- Every declared related models will be validated prior to be saved. If any validation fails, for each related model attribute in error, an error associated with the named relation will be added to the owner model. -For `hasMany()` relations, the index of the related model will be used to identifiy the associated error. +For `hasMany()` relations, the index of the related model will be used to identifiy the associated error message. + + +> **Tips :** +> For relations not involving a junction table by using the `via()` or `viaTable()` methods, you should remove the attributes pointing to the owner model to be able to pass the validations. diff --git a/src/SaveRelationsBehavior.php b/src/SaveRelationsBehavior.php index af5f854..6bdf654 100644 --- a/src/SaveRelationsBehavior.php +++ b/src/SaveRelationsBehavior.php @@ -196,14 +196,14 @@ public function _saveRelatedRecords(ActiveRecord $model, ModelEvent $event) if ($relation->multiple === false) { // Save Has one relation new record $pettyRelationName = Inflector::camel2words($relationName, true); - $this->_saveModelRecord($model->{$relationName}, $event, $pettyRelationName, - $relationName); + $this->_saveModelRecord($model->{$relationName}, $event, $pettyRelationName, $relationName); } else { // Save Has many relations new records /** @var ActiveRecord $relationModel */ foreach ($model->{$relationName} as $i => $relationModel) { $pettyRelationName = Inflector::camel2words($relationName, true) . " #{$i}"; - $this->_saveModelRecord($relationModel, $event, $pettyRelationName, $relationName); + $this->_validateRelationModel($pettyRelationName, $relationName, $relationModel, + $event); } } } @@ -214,6 +214,7 @@ public function _saveRelatedRecords(ActiveRecord $model, ModelEvent $event) } } catch (Exception $e) { $this->_transaction->rollBack(); // If anything goes wrong, transaction will be rolled back + $event->isValid = false; // Stop saving, something went wrong return false; } return true; @@ -281,8 +282,24 @@ public function afterSave() Yii::trace("Linking {$relationName} relation", __METHOD__); $relation = $model->getRelation($relationName); if ($relation->multiple === true) { // Has many relation + // Process new relations + $existingRecords = []; + foreach ($model->{$relationName} as $relationModel) { + if ($relationModel->isNewRecord) { + if ($relation->via !== null) { + $relationModel->save(false); + } + $model->link($relationName, $relationModel); + } else { + $existingRecords[] = $relationModel; + } + if (count($relationModel->dirtyAttributes)) { + $relationModel->save(false); + } + } + // Process existing added and deleted relations list($addedPks, $deletedPks) = $this->_computePkDiff($this->_oldRelationValue[$relationName], - $model->{$relationName}); + $existingRecords); // Deleted relations $initialModels = ArrayHelper::index($this->_oldRelationValue[$relationName], function (ActiveRecord $model) { diff --git a/tests/SaveRelationsBehaviorTest.php b/tests/SaveRelationsBehaviorTest.php index 76e949c..4c3e0a6 100644 --- a/tests/SaveRelationsBehaviorTest.php +++ b/tests/SaveRelationsBehaviorTest.php @@ -320,6 +320,7 @@ public function testSaveUpdatedHasManyRelationWithCompositeFksAsArrayShouldSucce $links[1]->link = "http://www.otherlink.com/"; $project->links = $links; $this->assertTrue($project->save(), 'Project could not be saved'); + $this->assertEquals(2, count($project->links), 'Project should have 2 links before save'); $this->assertEquals("http://www.otherlink.com/", $project->links[1]->link, 'Second link "Link" attribute should be "http://www.otherlink.com/"'); }