diff --git a/src/Model/Join.php b/src/Model/Join.php index ca95b2972..4ffcfa041 100644 --- a/src/Model/Join.php +++ b/src/Model/Join.php @@ -73,6 +73,12 @@ abstract class Join */ public ?string $foreignField = null; + /** + * Field to be used as foreign model ID field. + * By default it's 'id'. + */ + public ?string $foreignIdField = null; + /** * When $prefix is set, then all the fields generated through * our wrappers will be automatically prefixed inside the model. @@ -107,6 +113,7 @@ protected function createFakeForeignModel(): Model { $fakeModel = new Model($this->getOwner()->getPersistence(), [ 'table' => $this->foreignTable, + 'idField' => $this->foreignIdField, ]); foreach ($this->getOwner()->getFields() as $ownerField) { if ($ownerField->hasJoin() && $ownerField->getJoin()->shortName === $this->shortName) { diff --git a/tests/JoinSqlTest.php b/tests/JoinSqlTest.php index f88d80e3f..c52c1b772 100644 --- a/tests/JoinSqlTest.php +++ b/tests/JoinSqlTest.php @@ -40,7 +40,8 @@ public function testDirection(): void self::assertTrue($m->hasJoin('contact2')); self::assertFalse($m->hasJoin('contact8')); - $this->expectException(Exception::class); // TODO not implemented yet, see https://github.com/atk4/data/issues/803 + $this->expectException(Exception::class); + $this->expectExceptionMessage('Joining tables on non-id fields is not implemented yet'); // https://github.com/atk4/data/issues/803 $j4 = $m->join('contact4.foo_id', ['masterField' => 'test_id', 'reverse' => true]); // self::assertTrue($this->getProtected($j4, 'reverse')); // self::assertSame('test_id', $this->getProtected($j4, 'masterField')); @@ -52,6 +53,7 @@ public function testDirectionException(): void $m = new Model($this->db, ['table' => 'user']); $this->expectException(Exception::class); + $this->expectExceptionMessage('Joining tables on non-id fields is not implemented yet'); $m->join('contact.foo_id', ['masterField' => 'test_id']); } @@ -762,4 +764,78 @@ public function testJoinActualFieldNamesAndPrefix(): void ], ], $this->getDb()); } + + /** + * @param array $joinDefaults + * + * @return array{ Model, Model, Model } + */ + protected function setupJoinWithNonDefaultForeignIdField(array $joinDefaults = []): array + { + $masterModel = new Model($this->db, ['table' => 'user']); + $masterModel->addField('name'); + $masterModel->addField('foo', ['type' => 'integer']); + + $joinedModel = new Model($this->db, ['table' => 'contact', 'idField' => 'uid']); + $joinedModel->addField('bar', ['type' => 'integer']); + $joinedModel->addField('contact_phone'); + + $this->createMigrator($masterModel)->create(); + $this->createMigrator($joinedModel)->create(); + + $masterModel->import([ + ['id' => 1, 'name' => 'John', 'foo' => 21], + ['id' => 2, 'name' => 'Roman', 'foo' => 22], + ]); + + $joinedModel->import([ + ['uid' => 1, 'bar' => 22, 'contact_phone' => '+123'], + ['uid' => 2, 'bar' => 21, 'contact_phone' => '+200'], + ]); + + $user = new Model($this->db, ['table' => 'user']); + $user->addField('name'); + $j = $user->join('contact', array_merge([ + 'masterField' => 'foo', + 'foreignField' => 'bar', + 'foreignIdField' => 'uid', + ], $joinDefaults)); + $this->createMigrator()->createForeignKey($j); + $j->addField('contact_phone'); + + return [$masterModel, $joinedModel, $user]; + } + + public function testJoinCustomForeignIdFieldSaving(): void + { + [$masterModel, $joinedModel, $user] = $this->setupJoinWithNonDefaultForeignIdField(); + + $user = $user->load(1); + $user->set('name', 'Karl'); + $user->set('contact_phone', '+321'); + $user->save(); + + self::assertSameExportUnordered([ + ['id' => 1, 'name' => 'Karl', 'foo' => 21], + ['id' => 2, 'name' => 'Roman', 'foo' => 22], + ], $masterModel->export()); + self::assertSameExportUnordered([ + ['uid' => 1, 'bar' => 22, 'contact_phone' => '+123'], + ['uid' => 2, 'bar' => 21, 'contact_phone' => '+321'], + ], $joinedModel->export()); + } + + public function testJoinCustomForeignIdFieldDelete(): void + { + [$masterModel, $joinedModel, $user] = $this->setupJoinWithNonDefaultForeignIdField(); + + $user->delete(1); + + self::assertSameExportUnordered([ + ['id' => 2, 'name' => 'Roman', 'foo' => 22], + ], $masterModel->export()); + self::assertSameExportUnordered([ + ['uid' => 1, 'bar' => 22, 'contact_phone' => '+123'], + ], $joinedModel->export()); + } }