Skip to content

Commit fbcba88

Browse files
committed
rewrote __getRelatedBeans()
1 parent 695968e commit fbcba88

7 files changed

+233
-83
lines changed

src/AbstractTDBMObject.php

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
use JsonSerializable;
2525
use TheCodingMachine\TDBM\Schema\ForeignKeys;
26+
use TheCodingMachine\TDBM\Utils\ManyToManyRelationshipPathDescriptor;
2627

2728
/**
2829
* Instances of this class represent a "bean". Usually, a bean is mapped to a row of one table.
@@ -315,14 +316,12 @@ protected function addRelationship(string $pivotTableName, AbstractTDBMObject $r
315316
/**
316317
* Returns true if there is a relationship to this bean.
317318
*
318-
* @param string $pivotTableName
319-
* @param AbstractTDBMObject $remoteBean
320-
*
321319
* @return bool
322320
*/
323-
protected function hasRelationship(string $pivotTableName, AbstractTDBMObject $remoteBean): bool
321+
protected function hasRelationship(string $pathKey, AbstractTDBMObject $remoteBean): bool
324322
{
325-
$storage = $this->retrieveRelationshipsStorage($pivotTableName);
323+
$pathModel = $this->_getRelationshipModelFromKey($pathKey);
324+
$storage = $this->retrieveRelationshipsStorage($pathModel);
326325

327326
if ($storage->contains($remoteBean)) {
328327
if ($storage[$remoteBean]['status'] !== 'delete') {
@@ -353,12 +352,13 @@ public function _removeRelationship(string $pivotTableName, AbstractTDBMObject $
353352
* Sets many to many relationships for this bean.
354353
* Adds new relationships and removes unused ones.
355354
*
356-
* @param string $pivotTableName
357355
* @param AbstractTDBMObject[] $remoteBeans
358356
*/
359-
protected function setRelationships(string $pivotTableName, array $remoteBeans): void
357+
protected function setRelationships(string $pathKey, array $remoteBeans): void
360358
{
361-
$storage = $this->retrieveRelationshipsStorage($pivotTableName);
359+
$pathModel = $this->_getRelationshipModelFromKey($pathKey);
360+
$pivotTableName = $pathModel->getPivotName();
361+
$storage = $this->retrieveRelationshipsStorage($pathModel);
362362

363363
foreach ($storage as $oldRemoteBean) {
364364
/* @var $oldRemoteBean AbstractTDBMObject */
@@ -379,18 +379,18 @@ protected function setRelationships(string $pivotTableName, array $remoteBeans):
379379
/**
380380
* Returns the list of objects linked to this bean via $pivotTableName.
381381
*
382-
* @param string $pivotTableName
383-
*
384382
* @return \SplObjectStorage
385383
*/
386-
private function retrieveRelationshipsStorage(string $pivotTableName, ?string $from = null, ?string $where = null): \SplObjectStorage
384+
private function retrieveRelationshipsStorage(ManyToManyRelationshipPathDescriptor $pathModel): \SplObjectStorage
387385
{
388-
$storage = $this->getRelationshipStorage($pivotTableName);
386+
$pivotTableName = $pathModel->getPivotName();
387+
388+
$storage = $this->getRelationshipStorage($pathModel->getPivotName());
389389
if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->loadedRelationships[$pivotTableName]) && $this->loadedRelationships[$pivotTableName])) {
390390
return $storage;
391391
}
392392

393-
$beans = $this->tdbmService->_getRelatedBeans($pivotTableName, $this, $from, $where);
393+
$beans = $this->tdbmService->_getRelatedBeans($pathModel, $this);
394394
$this->loadedRelationships[$pivotTableName] = true;
395395

396396
foreach ($beans as $bean) {
@@ -410,13 +410,20 @@ private function retrieveRelationshipsStorage(string $pivotTableName, ?string $f
410410
/**
411411
* Internal TDBM method. Returns the list of objects linked to this bean via $pivotTableName.
412412
*
413-
* @param string $pivotTableName
414-
*
415413
* @return AbstractTDBMObject[]
416414
*/
417-
public function _getRelationships(string $pivotTableName, ?string $from = null, ?string $where = null): array
415+
public function _getRelationships(string $pathKey): array
418416
{
419-
return $this->relationshipStorageToArray($this->retrieveRelationshipsStorage($pivotTableName, $from, $where));
417+
$pathModel = $this->_getRelationshipModelFromKey($pathKey);
418+
return $this->_getRelationshipsFromModel($pathModel);
419+
}
420+
421+
/**
422+
* @return AbstractTDBMObject[]
423+
*/
424+
public function _getRelationshipsFromModel(ManyToManyRelationshipPathDescriptor $pathModel): array
425+
{
426+
return $this->relationshipStorageToArray($this->retrieveRelationshipsStorage($pathModel));
420427
}
421428

422429
/**
@@ -577,25 +584,6 @@ public function _getStatus() : string
577584
*/
578585
public function __clone()
579586
{
580-
// Let's clone the many to many relationships
581-
if ($this->status === TDBMObjectStateEnum::STATE_DETACHED) {
582-
$pivotTableList = array_keys($this->relationships);
583-
} else {
584-
$pivotTableList = $this->tdbmService->_getPivotTablesLinkedToBean($this);
585-
}
586-
587-
foreach ($pivotTableList as $pivotTable) {
588-
$storage = $this->retrieveRelationshipsStorage($pivotTable);
589-
590-
// Let's duplicate the reverse side of the relationship // This is useless: already done by "retrieveRelationshipsStorage"!!!
591-
/*foreach ($storage as $remoteBean) {
592-
$metadata = $storage[$remoteBean];
593-
594-
$remoteStorage = $remoteBean->getRelationshipStorage($pivotTable);
595-
$remoteStorage->attach($this, ['status' => $metadata['status'], 'reverse' => !$metadata['reverse']]);
596-
}*/
597-
}
598-
599587
// Let's clone each row
600588
foreach ($this->dbRows as $key => &$dbRow) {
601589
$dbRow = clone $dbRow;
@@ -672,4 +660,22 @@ protected static function getForeignKeys(string $tableName): ForeignKeys
672660
{
673661
return new ForeignKeys([]);
674662
}
663+
664+
/**
665+
* @return mixed[]
666+
*/
667+
abstract protected function _getRelationshipPathArray(): array;
668+
669+
public function _getRelationshipModelFromKey(string $pathKey): ManyToManyRelationshipPathDescriptor
670+
{
671+
return ManyToManyRelationshipPathDescriptor::createFromModelArray($this->_getRelationshipPathArray()[$pathKey]);
672+
}
673+
674+
/**
675+
* @return string[]
676+
*/
677+
public function _getRelationshipsPathKeys(): array
678+
{
679+
return array_keys($this->_getRelationshipPathArray());
680+
}
675681
}

src/TDBMObject.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,12 @@ protected function getUsedTables() : array
8181

8282
return $tableNames;
8383
}
84+
85+
/**
86+
* @return mixed[]
87+
*/
88+
protected function _getRelationshipPathArray(): array
89+
{
90+
return [];
91+
}
8492
}

src/TDBMService.php

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
use TheCodingMachine\TDBM\QueryFactory\FindObjectsFromSqlQueryFactory;
4040
use TheCodingMachine\TDBM\QueryFactory\FindObjectsQueryFactory;
4141
use TheCodingMachine\TDBM\QueryFactory\FindObjectsFromRawSqlQueryFactory;
42+
use TheCodingMachine\TDBM\Utils\ManyToManyRelationshipPathDescriptor;
4243
use TheCodingMachine\TDBM\Utils\NamingStrategyInterface;
4344
use TheCodingMachine\TDBM\Utils\TDBMDaoGenerator;
4445
use Phlib\Logger\Decorator\LevelFilter;
@@ -312,10 +313,11 @@ private function deleteManyToManyRelationships(AbstractTDBMObject $object): void
312313
{
313314
foreach ($object->_getDbRows() as $tableName => $dbRow) {
314315
$pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
315-
foreach ($pivotTables as $pivotTable) {
316-
$remoteBeans = $object->_getRelationships($pivotTable);
316+
foreach ($object->_getRelationshipsPathKeys() as $pathKey) {
317+
$pathModel = $object->_getRelationshipModelFromKey($pathKey);
318+
$remoteBeans = $object->_getRelationshipsFromModel($pathModel);
317319
foreach ($remoteBeans as $remoteBean) {
318-
$object->_removeRelationship($pivotTable, $remoteBean);
320+
$object->_removeRelationship($pathModel->getPivotName(), $remoteBean);
319321
}
320322
}
321323
}
@@ -1431,33 +1433,11 @@ private function fromCache(string $key, callable $closure)
14311433
}
14321434

14331435
/**
1434-
* @param string $pivotTableName
1435-
* @param AbstractTDBMObject $bean
1436-
*
14371436
* @return AbstractTDBMObject[]|ResultIterator
14381437
*/
1439-
public function _getRelatedBeans(string $pivotTableName, AbstractTDBMObject $bean, ?string $from = null, ?string $where = null): ResultIterator
1438+
public function _getRelatedBeans(ManyToManyRelationshipPathDescriptor $pathDescriptor, AbstractTDBMObject $bean): ResultIterator
14401439
{
1441-
list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $bean);
1442-
/* @var $localFk ForeignKeyConstraint */
1443-
/* @var $remoteFk ForeignKeyConstraint */
1444-
$remoteTable = $remoteFk->getForeignTableName();
1445-
1446-
$primaryKeys = $this->getPrimaryKeyValues($bean);
1447-
1448-
if ($from && $where) {
1449-
$where .= $primaryKeys[0];
1450-
1451-
return $this->findObjectsFromSql($remoteTable, $from, $where);
1452-
} else {
1453-
1454-
$columnNames = array_map(function ($name) use ($pivotTableName) {
1455-
return $pivotTableName.'.'.$name;
1456-
}, $localFk->getUnquotedLocalColumns());
1457-
1458-
$filter = SafeFunctions::arrayCombine($columnNames, $primaryKeys);
1459-
return $this->findObjects($remoteTable, $filter);
1460-
}
1440+
return $this->findObjectsFromSql($pathDescriptor->getTargetName(), $pathDescriptor->getPivotFrom(), $pathDescriptor->getPivotWhere(), $pathDescriptor->getPivotParams($this->getPrimaryKeyValues($bean)));
14611441
}
14621442

14631443
/**

src/Utils/BeanDescriptor.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,6 @@ public function generatePhpCode(): ?FileGenerator
531531
$file->setUse(ForeignKeys::class);
532532

533533
$class->setName($baseClassName);
534-
$class->setAbstract(true);
535534

536535
$file->setDocBlock(new DocBlockGenerator(
537536
'This file has been automatically generated by TDBM.',
@@ -576,6 +575,7 @@ public function generatePhpCode(): ?FileGenerator
576575
}
577576
}
578577

578+
$relationshipsPathModel = [];
579579
foreach ($this->getMethodDescriptors() as $methodDescriptor) {
580580
if ($methodDescriptor instanceof DirectForeignKeyMethodDescriptor) {
581581
[$method] = $methodDescriptor->getCode();
@@ -584,6 +584,8 @@ public function generatePhpCode(): ?FileGenerator
584584
$class->addMethodFromGenerator($method);
585585
}
586586
} elseif ($methodDescriptor instanceof PivotTableMethodsDescriptor) {
587+
[$index, $value] = $methodDescriptor->getRelationshipPathDescriptor();
588+
$relationshipsPathModel[$index] = $value;
587589
[ $getter, $adder, $remover, $has, $setter ] = $methodDescriptor->getCode();
588590
$methods = $this->codeGeneratorListener->onBaseBeanManyToManyGenerated($getter, $adder, $remover, $has, $setter, $methodDescriptor, $this, $this->configuration, $class);
589591
foreach ($methods as $method) {
@@ -596,6 +598,9 @@ public function generatePhpCode(): ?FileGenerator
596598
}
597599
}
598600

601+
$pathCode = $this->generatePathModelCode($relationshipsPathModel);
602+
$class->addMethodFromGenerator($pathCode);
603+
599604
$foreignKeysProperty = new PropertyGenerator('foreignKeys');
600605
$foreignKeysProperty->setStatic(true);
601606
$foreignKeysProperty->setVisibility(AbstractMemberGenerator::VISIBILITY_PRIVATE);
@@ -1321,17 +1326,38 @@ private function generateOnDeleteCode(): ?MethodGenerator
13211326
return $method;
13221327
}
13231328

1329+
/**
1330+
* @param mixed[] $data
1331+
* @return MethodGenerator
1332+
*/
1333+
private function generatePathModelCode(array $data): MethodGenerator
1334+
{
1335+
$method = new MethodGenerator('_getRelationshipPathArray');
1336+
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
1337+
$method->setReturnType('array');
1338+
$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).';');
1341+
1342+
return $method;
1343+
}
1344+
13241345
private function generateCloneCode(): MethodGenerator
13251346
{
1326-
$code = '';
1347+
$precode = '';
1348+
$postcode = '';
13271349

13281350
foreach ($this->beanPropertyDescriptors as $beanPropertyDescriptor) {
1329-
$code .= $beanPropertyDescriptor->getCloneRule();
1351+
$postcode .= $beanPropertyDescriptor->getCloneRule();
1352+
}
1353+
1354+
//cloning many to many relationships
1355+
foreach ($this->getPivotTableDescriptors() as $beanMethodDescriptor) {
1356+
$precode .= $beanMethodDescriptor->getCloneRule()."\n";
13301357
}
13311358

13321359
$method = new MethodGenerator('__clone');
1333-
$method->setBody('parent::__clone();
1334-
'.$code);
1360+
$method->setBody($precode."parent::__clone();\n".$postcode);
13351361

13361362
return $method;
13371363
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace TheCodingMachine\TDBM\Utils;
4+
5+
6+
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
7+
8+
class ManyToManyRelationshipPathDescriptor
9+
{
10+
private $targetTable;
11+
private $pivotTable;
12+
private $joinForeignKeys;
13+
private $joinLocalKeys;
14+
private $whereKeys;
15+
16+
/**
17+
* @return mixed[]
18+
*/
19+
public static function generateModelArray(ForeignKeyConstraint $remoteFk, ForeignKeyConstraint $localFk): array
20+
{
21+
return [$remoteFk->getForeignTableName(), $remoteFk->getLocalTableName(), $remoteFk->getUnquotedForeignColumns(), $remoteFk->getUnquotedLocalColumns(), $localFk->getUnquotedLocalColumns()];
22+
}
23+
24+
public static function generateModelKey(ForeignKeyConstraint $remoteFk, ForeignKeyConstraint $localFk): string
25+
{
26+
return $remoteFk->getLocalTableName().".".implode("__", $localFk->getUnquotedLocalColumns());
27+
}
28+
29+
/**
30+
* @param mixed[] $modelArray
31+
*/
32+
public static function createFromModelArray(array $modelArray): self
33+
{
34+
$obj = new self();
35+
$obj->targetTable = $modelArray[0];
36+
$obj->pivotTable = $modelArray[1];
37+
38+
$obj->joinForeignKeys = $modelArray[2];
39+
$obj->joinLocalKeys= $modelArray[3];
40+
$obj->whereKeys = $modelArray[4];
41+
42+
return $obj;
43+
}
44+
45+
private function __construct()
46+
{
47+
}
48+
49+
public function getPivotName(): string
50+
{
51+
return $this->pivotTable;
52+
}
53+
54+
public function getTargetName(): string
55+
{
56+
return $this->targetTable;
57+
}
58+
59+
public function getPivotFrom(): string
60+
{
61+
$mainTable = $this->targetTable;
62+
$pivotTable = $this->pivotTable;
63+
64+
$join = [];
65+
foreach ($this->joinForeignKeys as $key => $column) {
66+
$join[] = $mainTable.'.'.$column.' = pivot.'.$this->joinLocalKeys[$key];
67+
}
68+
69+
return $mainTable.' JOIN '.$pivotTable.' pivot ON '.implode(' AND ', $join);
70+
71+
}
72+
73+
public function getPivotWhere(): string
74+
{
75+
$paramList = [];
76+
foreach ($this->whereKeys as $key => $column) {
77+
$paramList[] = ' pivot.'.$column." = :param$key";
78+
}
79+
return implode(" AND ", $paramList);
80+
81+
}
82+
83+
public function getPivotParams(array $primaryKeys): array
84+
{
85+
$params = [];
86+
foreach ($primaryKeys as $key => $primaryKeyValue) {
87+
$params["param$key"] = $primaryKeyValue;
88+
}
89+
return $params;
90+
91+
}
92+
93+
}

0 commit comments

Comments
 (0)