From cd14cbcd0429dcb1a794c99c567100099381f991 Mon Sep 17 00:00:00 2001 From: Leandro Gehlen Date: Mon, 13 Aug 2018 12:51:34 -0300 Subject: [PATCH] Fixes error on update `hasMany` relation with composite key (#36) Fixes error on update `hasMany` relation with composite key --- .gitignore | 1 + src/SaveRelationsBehavior.php | 30 ++++++++++++++++++------------ tests/SaveRelationsBehaviorTest.php | 37 +++++++++++++++++++++++++++++++++++++ tests/models/Project.php | 9 +++++++++ tests/models/ProjectContact.php | 28 ++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 tests/models/ProjectContact.php diff --git a/.gitignore b/.gitignore index 028dc1f..cdc599c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.lock .idea/ +.vscode/ /vendor/ /tests/report/ /phpunit.phar diff --git a/src/SaveRelationsBehavior.php b/src/SaveRelationsBehavior.php index 75bebd9..8c6e0e1 100644 --- a/src/SaveRelationsBehavior.php +++ b/src/SaveRelationsBehavior.php @@ -201,27 +201,33 @@ class SaveRelationsBehavior extends Behavior { $fks = []; if (is_array($data)) { + // Get the right link definition + if ($relation->via instanceof BaseActiveRecord) { + $link = $relation->via->link; + } elseif (is_array($relation->via)) { + list($viaName, $viaQuery) = $relation->via; + $link = $viaQuery->link; + } else { + $link = $relation->link; + } // search PK foreach ($modelClass::primaryKey() as $modelAttribute) { - if (array_key_exists($modelAttribute, $data) && !empty($data[$modelAttribute])) { + if (isset($data[$modelAttribute])) { $fks[$modelAttribute] = $data[$modelAttribute]; + } elseif ($relation->multiple && !$relation->via) { + foreach ($link as $relatedAttribute => $modelAttribute) { + if (!isset($data[$relatedAttribute])) { + $fks[$relatedAttribute] = $this->owner->{$modelAttribute}; + } + } } else { $fks = []; break; } } if (empty($fks)) { - // Get the right link definition - if ($relation->via instanceof BaseActiveRecord) { - $link = $relation->via->link; - } elseif (is_array($relation->via)) { - list($viaName, $viaQuery) = $relation->via; - $link = $viaQuery->link; - } else { - $link = $relation->link; - } foreach ($link as $relatedAttribute => $modelAttribute) { - if (array_key_exists($modelAttribute, $data) && !empty($data[$modelAttribute])) { + if (isset($data[$modelAttribute])) { $fks[$modelAttribute] = $data[$modelAttribute]; } } @@ -626,7 +632,7 @@ class SaveRelationsBehavior extends Behavior /** * Compute the difference between two set of records using primary keys "tokens" * If third parameter is set to true all initial related records will be marked for removal even if their - * properties did not change. This can be handy in a many-to-many relation involving a junction table. + * properties did not change. This can be handy in a many-to-many relation_ involving a junction table. * @param BaseActiveRecord[] $initialRelations * @param BaseActiveRecord[] $updatedRelations * @param bool $forceSave diff --git a/tests/SaveRelationsBehaviorTest.php b/tests/SaveRelationsBehaviorTest.php index c2260b6..3c3c787 100644 --- a/tests/SaveRelationsBehaviorTest.php +++ b/tests/SaveRelationsBehaviorTest.php @@ -40,6 +40,7 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase $db->createCommand()->dropTable('project_tags')->execute(); $db->createCommand()->dropTable('tags')->execute(); $db->createCommand()->dropTable('project_link')->execute(); + $db->createCommand()->dropTable('project_contact')->execute(); $db->createCommand()->dropTable('dummy')->execute(); parent::tearDown(); } @@ -120,6 +121,14 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase 'PRIMARY KEY(project_id, user_id)' ])->execute(); + // Project Contact + $db->createCommand()->createTable('project_contact', [ + 'project_id' => $migration->integer()->notNull(), + 'email' => $migration->text()->notNull(), + 'phone' => $migration->text(), + 'PRIMARY KEY(project_id, email)' + ])->execute(); + // Dummy $db->createCommand()->createTable('dummy', [ 'id' => $migration->primaryKey(), @@ -170,6 +179,10 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase ['en', 'mac_os_x', 1] ])->execute(); + $db->createCommand()->batchInsert('project_contact', ['email', 'phone', 'project_id'], [ + ['admin@apple.com', '(123) 456–7890', 1] + ])->execute(); + $db->createCommand()->batchInsert('project_user', ['project_id', 'user_id'], [ [1, 1], [1, 4], @@ -501,6 +514,30 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase $this->assertEquals($project->links[1]->link, 'http://www.yiiframework.fr'); } + public function testLoadHasManyWithCompositeKeyShouldSucceed() + { + $project = Project::findOne(1); + $data = [ + 'ProjectContact' => [ + [ + 'email' => 'admin@apple.com', + 'phone' => '(999) 999–9999' + ], + [ + 'email' => 'new@apple.com', + 'phone' => '(987) 654–3210' + ] + ] + ]; + $project->loadRelations($data); + $this->assertTrue($project->save(), 'Project could not be saved'); + $this->assertCount(2, $project->contacts, "Project should have 2 contacts"); + $this->assertEquals($project->contacts[0]->phone, '(999) 999–9999'); + + $this->assertEquals($project->contacts[1]->email, 'new@apple.com'); + $this->assertEquals($project->contacts[1]->phone, '(987) 654–3210'); + } + public function testAssignSingleObjectToHasManyRelationShouldSucceed() { $project = new Project(); diff --git a/tests/models/Project.php b/tests/models/Project.php index 5077c30..d1b6bcd 100644 --- a/tests/models/Project.php +++ b/tests/models/Project.php @@ -25,6 +25,7 @@ class Project extends \yii\db\ActiveRecord 'relations' => [ 'company', 'users', + 'contacts', 'links' => ['scenario' => Link::SCENARIO_FIRST], 'projectLinks' => ['cascadeDelete' => true], 'tags' => [ @@ -98,6 +99,14 @@ class Project extends \yii\db\ActiveRecord /** * @return \yii\db\ActiveQuery */ + public function getContacts() + { + return $this->hasMany(ProjectContact::className(), ['project_id' => 'id']); + } + + /** + * @return \yii\db\ActiveQuery + */ public function getLinks() { return $this->hasMany(Link::className(), ['language' => 'language', 'name' => 'name'])->via('projectLinks'); diff --git a/tests/models/ProjectContact.php b/tests/models/ProjectContact.php new file mode 100644 index 0000000..4fe1ebf --- /dev/null +++ b/tests/models/ProjectContact.php @@ -0,0 +1,28 @@ +