Skip to content

Commit 906daf1

Browse files
authored
Merge pull request #151 from homersimpsons/feature/one-to-one-inverse-getter
OneToOne inverse getter
2 parents 3633fe0 + ec64197 commit 906daf1

File tree

5 files changed

+100
-20
lines changed

5 files changed

+100
-20
lines changed

src/Utils/BeanDescriptor.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ private function getDirectForeignKeysDescriptors(): array
356356
$descriptors = [];
357357

358358
foreach ($fks as $fk) {
359-
$descriptors[] = new DirectForeignKeyMethodDescriptor($fk, $this->table, $this->namingStrategy, $this->annotationParser);
359+
$descriptors[] = new DirectForeignKeyMethodDescriptor($fk, $this->table, $this->namingStrategy, $this->annotationParser, $this->beanNamespace);
360360
}
361361

362362
return $descriptors;
@@ -1119,7 +1119,11 @@ private function removeDuplicateIndexes(array $indexes): array
11191119
{
11201120
$indexesByKey = [];
11211121
foreach ($indexes as $index) {
1122-
$indexesByKey[implode('__`__', $index->getUnquotedColumns())] = $index;
1122+
$key = implode('__`__', $index->getUnquotedColumns());
1123+
// Unique Index have precedence over non unique one
1124+
if (!isset($indexesByKey[$key]) || $index->isUnique()) {
1125+
$indexesByKey[$key] = $index;
1126+
}
11231127
}
11241128

11251129
return array_values($indexesByKey);

src/Utils/DirectForeignKeyMethodDescriptor.php

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,30 @@ class DirectForeignKeyMethodDescriptor implements MethodDescriptorInterface
4040
* @var AnnotationParser
4141
*/
4242
private $annotationParser;
43+
/**
44+
* @var string
45+
*/
46+
private $beanNamespace;
4347

4448
/**
4549
* @param ForeignKeyConstraint $fk The foreign key pointing to our bean
4650
* @param Table $mainTable The main table that is pointed to
4751
* @param NamingStrategyInterface $namingStrategy
52+
* @param AnnotationParser $annotationParser
53+
* @param string $beanNamespace
4854
*/
49-
public function __construct(ForeignKeyConstraint $fk, Table $mainTable, NamingStrategyInterface $namingStrategy, AnnotationParser $annotationParser)
50-
{
55+
public function __construct(
56+
ForeignKeyConstraint $fk,
57+
Table $mainTable,
58+
NamingStrategyInterface $namingStrategy,
59+
AnnotationParser $annotationParser,
60+
string $beanNamespace
61+
) {
5162
$this->foreignKey = $fk;
5263
$this->mainTable = $mainTable;
5364
$this->namingStrategy = $namingStrategy;
5465
$this->annotationParser = $annotationParser;
66+
$this->beanNamespace = $beanNamespace;
5567
}
5668

5769
/**
@@ -100,23 +112,37 @@ public function useAlternativeName(): void
100112
public function getCode() : array
101113
{
102114
$beanClass = $this->getBeanClassName();
115+
$tdbmFk = ForeignKey::createFromFk($this->foreignKey);
103116

104117
$getter = new MethodGenerator($this->getName());
105-
$getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
106-
$getter->getDocBlock()->setTag(new ReturnTag([
107-
$beanClass.'[]',
108-
'\\'.AlterableResultIterator::class
109-
]));
110-
$getter->setReturnType(AlterableResultIterator::class);
111118

112-
$tdbmFk = ForeignKey::createFromFk($this->foreignKey);
119+
if ($this->hasLocalUniqueIndex()) {
120+
$getter->setDocBlock(sprintf('Returns the %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
121+
$classType = '\\' . $this->beanNamespace . '\\' . $beanClass;
122+
$getter->getDocBlock()->setTag(new ReturnTag([$classType . '|null']));
123+
$getter->setReturnType('?' . $classType);
113124

114-
$code = sprintf(
115-
'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s);',
116-
var_export($this->foreignKey->getLocalTableName(), true),
117-
var_export($tdbmFk->getCacheKey(), true),
118-
$this->getFilters($this->foreignKey)
119-
);
125+
$code = sprintf(
126+
'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s)->first();',
127+
var_export($this->foreignKey->getLocalTableName(), true),
128+
var_export($tdbmFk->getCacheKey(), true),
129+
$this->getFilters($this->foreignKey)
130+
);
131+
} else {
132+
$getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
133+
$getter->getDocBlock()->setTag(new ReturnTag([
134+
$beanClass . '[]',
135+
'\\' . AlterableResultIterator::class
136+
]));
137+
$getter->setReturnType(AlterableResultIterator::class);
138+
139+
$code = sprintf(
140+
'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s);',
141+
var_export($this->foreignKey->getLocalTableName(), true),
142+
var_export($tdbmFk->getCacheKey(), true),
143+
$this->getFilters($this->foreignKey)
144+
);
145+
}
120146

121147
$getter->setBody($code);
122148

@@ -144,6 +170,25 @@ private function getFilters(ForeignKeyConstraint $fk) : string
144170
return $parametersCode;
145171
}
146172

173+
/**
174+
* Check if the ForeignKey have an unique index
175+
*
176+
* @return bool
177+
*/
178+
private function hasLocalUniqueIndex(): bool
179+
{
180+
foreach ($this->getForeignKey()->getLocalTable()->getIndexes() as $index) {
181+
if (
182+
$index->isUnique()
183+
&& count($index->getUnquotedColumns()) === count($this->getForeignKey()->getUnquotedLocalColumns())
184+
&& !array_diff($index->getUnquotedColumns(), $this->getForeignKey()->getUnquotedLocalColumns()) // Check for permuted columns too
185+
) {
186+
return true;
187+
}
188+
}
189+
return false;
190+
}
191+
147192
/**
148193
* Returns an array of classes that needs a "use" for this method.
149194
*
@@ -181,17 +226,21 @@ public function getJsonSerializeCode() : string
181226
$class = $this->getBeanClassName();
182227
$variableName = '$' . TDBMDaoGenerator::toVariableName($class);
183228
$getter = $this->getName();
184-
$code = <<<PHP
229+
if ($this->hasLocalUniqueIndex()) {
230+
$code = "\$array['$index'] = (\$object = \$this->$getter()) ? \$object->$format : null;";
231+
} else {
232+
$code = <<<PHP
185233
\$array['$index'] = array_map(function ($class $variableName) {
186234
return ${variableName}->$format;
187235
}, \$this->$getter()->toArray());
188236
PHP;
237+
}
189238
if (!$isIncluded) {
190239
$code = preg_replace('(\n)', '\0 ', $code);
191240
$code = <<<PHP
192241
if (!\$stopRecursion) {
193242
$code
194-
};
243+
}
195244
PHP;
196245
}
197246
return $code;

tests/TDBMAbstractServiceTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,13 @@ private static function initSchema(Connection $connection): void
437437
->column('track_id')->references('tracks')
438438
->column('artist_id')->references('artists')->comment('@JsonKey("feat") @JsonInclude');
439439

440+
$db->table('object_base')
441+
->column('id')->integer()->primaryKey()->autoIncrement()
442+
->column('label')->string();
443+
$db->table('object_inherited')
444+
->column('id')->integer()->primaryKey()->autoIncrement()
445+
->column('object_base_id')->references('object_base')->unique()->comment('@JsonCollection');
446+
440447
$sqlStmts = $toSchema->getMigrateFromSql($fromSchema, $connection->getDatabasePlatform());
441448

442449
foreach ($sqlStmts as $sqlStmt) {

tests/TDBMDaoGeneratorTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\FileBaseBean;
6060
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\UserBaseBean;
6161
use TheCodingMachine\TDBM\Test\Dao\Bean\NodeBean;
62+
use TheCodingMachine\TDBM\Test\Dao\Bean\ObjectBaseBean;
63+
use TheCodingMachine\TDBM\Test\Dao\Bean\ObjectInheritedBean;
6264
use TheCodingMachine\TDBM\Test\Dao\Bean\PersonBean;
6365
use TheCodingMachine\TDBM\Test\Dao\Bean\RefNoPrimKeyBean;
6466
use TheCodingMachine\TDBM\Test\Dao\Bean\RoleBean;
@@ -74,6 +76,8 @@
7476
use TheCodingMachine\TDBM\Test\Dao\Generated\ContactBaseDao;
7577
use TheCodingMachine\TDBM\Test\Dao\Generated\UserBaseDao;
7678
use TheCodingMachine\TDBM\Test\Dao\NodeDao;
79+
use TheCodingMachine\TDBM\Test\Dao\ObjectBaseDao;
80+
use TheCodingMachine\TDBM\Test\Dao\ObjectInheritedDao;
7781
use TheCodingMachine\TDBM\Test\Dao\RefNoPrimKeyDao;
7882
use TheCodingMachine\TDBM\Test\Dao\RoleDao;
7983
use TheCodingMachine\TDBM\Test\Dao\StateDao;
@@ -2074,4 +2078,20 @@ public function testLazyLoad(): void
20742078
$roleBean->getId();
20752079
$this->assertSame(TDBMObjectStateEnum::STATE_NOT_LOADED, $roleBean->_getDbRows()['roles']->_getStatus());
20762080
}
2081+
2082+
/**
2083+
* @depends testDaoGeneration
2084+
*/
2085+
public function testOneToOneInverseRelationGetter(): void
2086+
{
2087+
$objectBaseDao = new ObjectBaseDao($this->tdbmService);
2088+
$objectInheritedDao = new ObjectInheritedDao($this->tdbmService);
2089+
$objectBase = new ObjectBaseBean('label');
2090+
$objectBaseDao->save($objectBase);
2091+
$this->assertNull($objectBase->getObjectInherited());
2092+
$objectInherited = new ObjectInheritedBean($objectBase);
2093+
$objectInheritedDao->save($objectInherited);
2094+
$this->assertInstanceOf(ObjectInheritedBean::class, $objectBase->getObjectInherited());
2095+
$this->assertEquals(1, $objectBase->jsonSerialize()['objectInherited']['id']);
2096+
}
20772097
}

tests/Utils/DirectForeignKeyMethodDescriptorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function testGetForeignKey(): void
1515
$table = $this->createMock(Table::class);
1616
$ns = $this->createMock(DefaultNamingStrategy::class);
1717
$ap = $this->createMock(AnnotationParser::class);
18-
$descriptor = new DirectForeignKeyMethodDescriptor($fk, $table, $ns, $ap);
18+
$descriptor = new DirectForeignKeyMethodDescriptor($fk, $table, $ns, $ap, '');
1919

2020
$this->assertSame($fk, $descriptor->getForeignKey());
2121
$this->assertSame($table, $descriptor->getMainTable());

0 commit comments

Comments
 (0)