Skip to content

Commit f450476

Browse files
committed
Fixing many-to-many relationships with parent tables
Refactored generated many to many code
1 parent b97f468 commit f450476

File tree

5 files changed

+123
-57
lines changed

5 files changed

+123
-57
lines changed

src/AbstractTDBMObject.php

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ protected function addRelationship(string $pivotTableName, AbstractTDBMObject $r
320320
*/
321321
protected function hasRelationship(string $pathKey, AbstractTDBMObject $remoteBean): bool
322322
{
323-
$pathModel = $this->_getRelationshipModelFromKey($pathKey);
323+
$pathModel = $this->_getManyToManyRelationshipDescriptor($pathKey);
324324
$storage = $this->retrieveRelationshipsStorage($pathModel);
325325

326326
if ($storage->contains($remoteBean)) {
@@ -356,7 +356,7 @@ public function _removeRelationship(string $pivotTableName, AbstractTDBMObject $
356356
*/
357357
protected function setRelationships(string $pathKey, array $remoteBeans): void
358358
{
359-
$pathModel = $this->_getRelationshipModelFromKey($pathKey);
359+
$pathModel = $this->_getManyToManyRelationshipDescriptor($pathKey);
360360
$pivotTableName = $pathModel->getPivotName();
361361
$storage = $this->retrieveRelationshipsStorage($pathModel);
362362

@@ -414,7 +414,7 @@ private function retrieveRelationshipsStorage(ManyToManyRelationshipPathDescript
414414
*/
415415
public function _getRelationships(string $pathKey): array
416416
{
417-
$pathModel = $this->_getRelationshipModelFromKey($pathKey);
417+
$pathModel = $this->_getManyToManyRelationshipDescriptor($pathKey);
418418
return $this->_getRelationshipsFromModel($pathModel);
419419
}
420420

@@ -661,21 +661,16 @@ protected static function getForeignKeys(string $tableName): ForeignKeys
661661
return new ForeignKeys([]);
662662
}
663663

664-
/**
665-
* @return mixed[]
666-
*/
667-
abstract protected function _getRelationshipPathArray(): array;
668-
669-
public function _getRelationshipModelFromKey(string $pathKey): ManyToManyRelationshipPathDescriptor
664+
public function _getManyToManyRelationshipDescriptor(string $pathKey): ManyToManyRelationshipPathDescriptor
670665
{
671-
return ManyToManyRelationshipPathDescriptor::createFromModelArray($this->_getRelationshipPathArray()[$pathKey]);
666+
throw new TDBMException('Could not find many to many relationship descriptor key for "'.$pathKey.'"');
672667
}
673668

674669
/**
675670
* @return string[]
676671
*/
677-
public function _getRelationshipsPathKeys(): array
672+
public function _getManyToManyRelationshipDescriptorKeys(): array
678673
{
679-
return array_keys($this->_getRelationshipPathArray());
674+
return [];
680675
}
681676
}

src/TDBMService.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ private function deleteManyToManyRelationships(AbstractTDBMObject $object): void
313313
{
314314
foreach ($object->_getDbRows() as $tableName => $dbRow) {
315315
$pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
316-
foreach ($object->_getRelationshipsPathKeys() as $pathKey) {
317-
$pathModel = $object->_getRelationshipModelFromKey($pathKey);
316+
foreach ($object->_getManyToManyRelationshipDescriptorKeys() as $pathKey) {
317+
$pathModel = $object->_getManyToManyRelationshipDescriptor($pathKey);
318318
$remoteBeans = $object->_getRelationshipsFromModel($pathModel);
319319
foreach ($remoteBeans as $remoteBean) {
320320
$object->_removeRelationship($pathModel->getPivotName(), $remoteBean);

src/Utils/BeanDescriptor.php

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
use TheCodingMachine\TDBM\Utils\Annotation\AddInterface;
3131
use Zend\Code\Generator\AbstractMemberGenerator;
3232
use Zend\Code\Generator\ClassGenerator;
33+
use Zend\Code\Generator\DocBlock\Tag;
34+
use Zend\Code\Generator\DocBlock\Tag\GenericTag;
3335
use Zend\Code\Generator\DocBlock\Tag\ParamTag;
3436
use Zend\Code\Generator\DocBlock\Tag\ReturnTag;
3537
use Zend\Code\Generator\DocBlock\Tag\ThrowsTag;
@@ -39,6 +41,8 @@
3941
use Zend\Code\Generator\MethodGenerator;
4042
use Zend\Code\Generator\ParameterGenerator;
4143
use Zend\Code\Generator\PropertyGenerator;
44+
use function implode;
45+
use function var_export;
4246

4347
/**
4448
* This class represents a bean.
@@ -575,7 +579,7 @@ public function generatePhpCode(): ?FileGenerator
575579
}
576580
}
577581

578-
$relationshipsPathModel = [];
582+
$pivotTableMethodsDescriptors = [];
579583
foreach ($this->getMethodDescriptors() as $methodDescriptor) {
580584
if ($methodDescriptor instanceof DirectForeignKeyMethodDescriptor) {
581585
[$method] = $methodDescriptor->getCode();
@@ -584,8 +588,7 @@ public function generatePhpCode(): ?FileGenerator
584588
$class->addMethodFromGenerator($method);
585589
}
586590
} elseif ($methodDescriptor instanceof PivotTableMethodsDescriptor) {
587-
[$index, $value] = $methodDescriptor->getRelationshipPathDescriptor();
588-
$relationshipsPathModel[$index] = $value;
591+
$pivotTableMethodsDescriptors[] = $methodDescriptor;
589592
[ $getter, $adder, $remover, $has, $setter ] = $methodDescriptor->getCode();
590593
$methods = $this->codeGeneratorListener->onBaseBeanManyToManyGenerated($getter, $adder, $remover, $has, $setter, $methodDescriptor, $this, $this->configuration, $class);
591594
foreach ($methods as $method) {
@@ -598,8 +601,14 @@ public function generatePhpCode(): ?FileGenerator
598601
}
599602
}
600603

601-
$pathCode = $this->generatePathModelCode($relationshipsPathModel);
602-
$class->addMethodFromGenerator($pathCode);
604+
$manyToManyRelationshipCode = $this->generateGetManyToManyRelationshipDescriptorCode($pivotTableMethodsDescriptors);
605+
if ($manyToManyRelationshipCode !== null) {
606+
$class->addMethodFromGenerator($manyToManyRelationshipCode);
607+
}
608+
$manyToManyRelationshipKeysCode = $this->generateGetManyToManyRelationshipDescriptorKeysCode($pivotTableMethodsDescriptors);
609+
if ($manyToManyRelationshipKeysCode !== null) {
610+
$class->addMethodFromGenerator($manyToManyRelationshipKeysCode);
611+
}
603612

604613
$foreignKeysProperty = new PropertyGenerator('foreignKeys');
605614
$foreignKeysProperty->setStatic(true);
@@ -1327,17 +1336,64 @@ private function generateOnDeleteCode(): ?MethodGenerator
13271336
}
13281337

13291338
/**
1330-
* @param mixed[] $data
1339+
* @param PivotTableMethodsDescriptor[] $pivotTableMethodsDescriptors
13311340
* @return MethodGenerator
13321341
*/
1333-
private function generatePathModelCode(array $data): MethodGenerator
1342+
private function generateGetManyToManyRelationshipDescriptorCode(array $pivotTableMethodsDescriptors): ?MethodGenerator
13341343
{
1335-
$method = new MethodGenerator('_getRelationshipPathArray');
1336-
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
1337-
$method->setReturnType('array');
1344+
if (empty($pivotTableMethodsDescriptors)) {
1345+
return null;
1346+
}
1347+
1348+
$method = new MethodGenerator('_getManyToManyRelationshipDescriptor');
1349+
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PUBLIC);
13381350
$method->setDocBlock('Get the paths used for many to many relationships methods.');
1339-
$method->getDocBlock()->setTag(new ReturnTag(['mixed[]']));
1340-
$method->setBody('return ' . $this->psr2VarExport($data).';');
1351+
$method->getDocBlock()->setTag(new GenericTag('internal'));
1352+
$method->setReturnType(ManyToManyRelationshipPathDescriptor::class);
1353+
1354+
$parameter = new ParameterGenerator('pathKey');
1355+
$parameter->setType('string');
1356+
$method->setParameter($parameter);
1357+
1358+
$code = 'switch ($pathKey) {'."\n";
1359+
foreach ($pivotTableMethodsDescriptors as $pivotTableMethodsDescriptor) {
1360+
$code .= ' case '.var_export($pivotTableMethodsDescriptor->getManyToManyRelationshipKey(), true).":\n";
1361+
$code .= ' return '.$pivotTableMethodsDescriptor->getManyToManyRelationshipInstantiationCode().";\n";
1362+
}
1363+
$code .= " default:\n";
1364+
$code .= " return parent::_getManyToManyRelationshipDescriptor(\$pathKey);\n";
1365+
$code .= "}\n";
1366+
1367+
$method->setBody($code);
1368+
1369+
return $method;
1370+
}
1371+
1372+
/**
1373+
* @param PivotTableMethodsDescriptor[] $pivotTableMethodsDescriptors
1374+
* @return MethodGenerator
1375+
*/
1376+
private function generateGetManyToManyRelationshipDescriptorKeysCode(array $pivotTableMethodsDescriptors): ?MethodGenerator
1377+
{
1378+
if (empty($pivotTableMethodsDescriptors)) {
1379+
return null;
1380+
}
1381+
1382+
$method = new MethodGenerator('_getManyToManyRelationshipDescriptorKeys');
1383+
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PUBLIC);
1384+
$method->setReturnType('array');
1385+
$method->setDocBlock('Returns the list of keys supported for many to many relationships');
1386+
$method->getDocBlock()->setTag(new GenericTag('internal'));
1387+
$method->getDocBlock()->setTag(new ReturnTag('string[]'));
1388+
1389+
$keys = [];
1390+
foreach ($pivotTableMethodsDescriptors as $pivotTableMethodsDescriptor) {
1391+
$keys[] = var_export($pivotTableMethodsDescriptor->getManyToManyRelationshipKey(), true);
1392+
}
1393+
1394+
$code = 'return array_merge(parent::_getManyToManyRelationshipDescriptorKeys(), ['.implode(', ', $keys).']);';
1395+
1396+
$method->setBody($code);
13411397

13421398
return $method;
13431399
}

src/Utils/ManyToManyRelationshipPathDescriptor.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,41 @@
44

55

66
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
7+
use function var_export;
78

89
class ManyToManyRelationshipPathDescriptor
910
{
11+
12+
/**
13+
* @var string
14+
*/
1015
private $targetTable;
16+
/**
17+
* @var string
18+
*/
1119
private $pivotTable;
20+
/**
21+
* @var array
22+
*/
1223
private $joinForeignKeys;
24+
/**
25+
* @var array
26+
*/
1327
private $joinLocalKeys;
28+
/**
29+
* @var array
30+
*/
1431
private $whereKeys;
1532

33+
public function __construct(string $targetTable, string $pivotTable, array $joinForeignKeys, array $joinLocalKeys, array $whereKeys)
34+
{
35+
$this->targetTable = $targetTable;
36+
$this->pivotTable = $pivotTable;
37+
$this->joinForeignKeys = $joinForeignKeys;
38+
$this->joinLocalKeys = $joinLocalKeys;
39+
$this->whereKeys = $whereKeys;
40+
}
41+
1642
/**
1743
* @return mixed[]
1844
*/
@@ -42,10 +68,6 @@ public static function createFromModelArray(array $modelArray): self
4268
return $obj;
4369
}
4470

45-
private function __construct()
46-
{
47-
}
48-
4971
public function getPivotName(): string
5072
{
5173
return $this->pivotTable;

src/Utils/PivotTableMethodsDescriptor.php

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
use Doctrine\DBAL\Schema\Column;
77
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
88
use Doctrine\DBAL\Schema\Table;
9+
use function implode;
910
use function sprintf;
1011
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
1112
use TheCodingMachine\TDBM\Utils\Annotation\Annotations;
1213
use Zend\Code\Generator\DocBlock\Tag\ParamTag;
1314
use Zend\Code\Generator\DocBlock\Tag\ReturnTag;
1415
use Zend\Code\Generator\MethodGenerator;
1516
use Zend\Code\Generator\ParameterGenerator;
17+
use function var_export;
1618

1719
class PivotTableMethodsDescriptor implements MethodDescriptorInterface
1820
{
@@ -148,41 +150,32 @@ private function isAutoPivot(): bool
148150
return $this->localFk->getForeignTableName() === $this->remoteFk->getForeignTableName();
149151
}
150152

153+
public function getManyToManyRelationshipInstantiationCode(): string
154+
{
155+
return 'new \TheCodingMachine\TDBM\Utils\ManyToManyRelationshipPathDescriptor('.var_export($this->remoteFk->getForeignTableName(), true).
156+
', '.var_export($this->remoteFk->getLocalTableName(), true).
157+
', '.$this->getArrayInlineCode($this->remoteFk->getUnquotedForeignColumns()).
158+
', '.$this->getArrayInlineCode($this->remoteFk->getUnquotedLocalColumns()).
159+
', '.$this->getArrayInlineCode($this->localFk->getUnquotedLocalColumns()).
160+
')';
161+
}
151162

152163
/**
153-
* return the list of couples tableName.columnName needed for the sql query
164+
* @param string[] $values
165+
* @return string
154166
*/
155-
private function getAutoPivotFrom(): string
167+
private function getArrayInlineCode(array $values): string
156168
{
157-
$mainTable = $this->remoteFk->getForeignTableName();
158-
$pivotTable = $this->remoteFk->getLocalTableName();
159-
160-
$join = [];
161-
foreach ($this->remoteFk->getUnquotedForeignColumns() as $key => $column) {
162-
$join[] = $mainTable.'.'.$column.' = pivot.'.$this->remoteFk->getUnquotedLocalColumns()[$key];
169+
$items = [];
170+
foreach ($values as $value) {
171+
$items[] = var_export($value, true);
163172
}
164-
165-
return $mainTable.' JOIN '.$pivotTable.' pivot ON '.implode(' AND ', $join);
166-
167-
}
168-
private function getAutoPivotWhere(): string
169-
{
170-
$paramList = [];
171-
foreach ($this->localFk->getUnquotedLocalColumns() as $key => $column) {
172-
$paramList[] = ' pivot.'.$column." = :param$key";
173-
}
174-
return implode(" AND ", $paramList);
175-
173+
return '['.implode(', ', $items).']';
176174
}
177175

178-
/**
179-
* Return
180-
*
181-
* @return mixed[]
182-
*/
183-
public function getRelationshipPathDescriptor(): array
176+
public function getManyToManyRelationshipKey(): string
184177
{
185-
return [$this->pathKey, $this->pathModel];
178+
return $this->remoteFk->getLocalTableName().".".implode("__", $this->localFk->getUnquotedLocalColumns());
186179
}
187180

188181
/**

0 commit comments

Comments
 (0)