From c8cd72a7fa6bf0c45d49ae9fb0c9d44fb2b0a6ce Mon Sep 17 00:00:00 2001 From: Alban Jubert Date: Sun, 3 Apr 2016 11:46:06 +0200 Subject: [PATCH] Fix #2 issue: hasMany() relations are correctly saved now --- README.md | 6 +++++- src/SaveRelationsBehavior.php | 25 +++++++++++++++++++++---- tests/SaveRelationsBehaviorTest.php | 1 + 3 files changed, 27 insertions(+), 5 deletions(-) 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 @@ class SaveRelationsBehavior extends Behavior 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 @@ class SaveRelationsBehavior extends Behavior } } 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 @@ class SaveRelationsBehavior extends Behavior 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 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase $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/"'); }