Browse Source

Fixes error on update `hasMany` relation with composite key (#36)

Fixes error on update `hasMany` relation with composite key
tags/1.6.0
Leandro Gehlen 6 years ago committed by Alban Jubert
parent
commit
cd14cbcd04
  1. 1
      .gitignore
  2. 30
      src/SaveRelationsBehavior.php
  3. 37
      tests/SaveRelationsBehaviorTest.php
  4. 9
      tests/models/Project.php
  5. 28
      tests/models/ProjectContact.php

1
.gitignore vendored

@ -1,6 +1,7 @@
*.lock *.lock
.idea/ .idea/
.vscode/
/vendor/ /vendor/
/tests/report/ /tests/report/
/phpunit.phar /phpunit.phar

30
src/SaveRelationsBehavior.php

@ -201,27 +201,33 @@ class SaveRelationsBehavior extends Behavior
{ {
$fks = []; $fks = [];
if (is_array($data)) { 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 // search PK
foreach ($modelClass::primaryKey() as $modelAttribute) { foreach ($modelClass::primaryKey() as $modelAttribute) {
if (array_key_exists($modelAttribute, $data) && !empty($data[$modelAttribute])) { if (isset($data[$modelAttribute])) {
$fks[$modelAttribute] = $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 { } else {
$fks = []; $fks = [];
break; break;
} }
} }
if (empty($fks)) { 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) { foreach ($link as $relatedAttribute => $modelAttribute) {
if (array_key_exists($modelAttribute, $data) && !empty($data[$modelAttribute])) { if (isset($data[$modelAttribute])) {
$fks[$modelAttribute] = $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" * 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 * 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[] $initialRelations
* @param BaseActiveRecord[] $updatedRelations * @param BaseActiveRecord[] $updatedRelations
* @param bool $forceSave * @param bool $forceSave

37
tests/SaveRelationsBehaviorTest.php

@ -40,6 +40,7 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase
$db->createCommand()->dropTable('project_tags')->execute(); $db->createCommand()->dropTable('project_tags')->execute();
$db->createCommand()->dropTable('tags')->execute(); $db->createCommand()->dropTable('tags')->execute();
$db->createCommand()->dropTable('project_link')->execute(); $db->createCommand()->dropTable('project_link')->execute();
$db->createCommand()->dropTable('project_contact')->execute();
$db->createCommand()->dropTable('dummy')->execute(); $db->createCommand()->dropTable('dummy')->execute();
parent::tearDown(); parent::tearDown();
} }
@ -120,6 +121,14 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase
'PRIMARY KEY(project_id, user_id)' 'PRIMARY KEY(project_id, user_id)'
])->execute(); ])->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 // Dummy
$db->createCommand()->createTable('dummy', [ $db->createCommand()->createTable('dummy', [
'id' => $migration->primaryKey(), 'id' => $migration->primaryKey(),
@ -170,6 +179,10 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase
['en', 'mac_os_x', 1] ['en', 'mac_os_x', 1]
])->execute(); ])->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'], [ $db->createCommand()->batchInsert('project_user', ['project_id', 'user_id'], [
[1, 1], [1, 1],
[1, 4], [1, 4],
@ -501,6 +514,30 @@ class SaveRelationsBehaviorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($project->links[1]->link, 'http://www.yiiframework.fr'); $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() public function testAssignSingleObjectToHasManyRelationShouldSucceed()
{ {
$project = new Project(); $project = new Project();

9
tests/models/Project.php

@ -25,6 +25,7 @@ class Project extends \yii\db\ActiveRecord
'relations' => [ 'relations' => [
'company', 'company',
'users', 'users',
'contacts',
'links' => ['scenario' => Link::SCENARIO_FIRST], 'links' => ['scenario' => Link::SCENARIO_FIRST],
'projectLinks' => ['cascadeDelete' => true], 'projectLinks' => ['cascadeDelete' => true],
'tags' => [ 'tags' => [
@ -98,6 +99,14 @@ class Project extends \yii\db\ActiveRecord
/** /**
* @return \yii\db\ActiveQuery * @return \yii\db\ActiveQuery
*/ */
public function getContacts()
{
return $this->hasMany(ProjectContact::className(), ['project_id' => 'id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getLinks() public function getLinks()
{ {
return $this->hasMany(Link::className(), ['language' => 'language', 'name' => 'name'])->via('projectLinks'); return $this->hasMany(Link::className(), ['language' => 'language', 'name' => 'name'])->via('projectLinks');

28
tests/models/ProjectContact.php

@ -0,0 +1,28 @@
<?php
namespace tests\models;
class ProjectContact extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'project_contact';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['project_id'], 'integer'],
[['email'], 'required'],
[['email', 'phone'], 'string']
];
}
}
Loading…
Cancel
Save