Skip to content

Commit 18f4b45

Browse files
authored
Merge pull request #146 from Kharhamel/autoPivot
WIP: rewrote the way relationships are fetched in bean methods to enable autopivot relationships
2 parents 888f281 + 717dec1 commit 18f4b45

14 files changed

+457
-147
lines changed

src/AbstractTDBMObject.php

Lines changed: 37 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->_getManyToManyRelationshipDescriptor($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->_getManyToManyRelationshipDescriptor($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): \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);
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): \SplObjec
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): array
415+
public function _getRelationships(string $pathKey): array
416+
{
417+
$pathModel = $this->_getManyToManyRelationshipDescriptor($pathKey);
418+
return $this->_getRelationshipsFromModel($pathModel);
419+
}
420+
421+
/**
422+
* @return AbstractTDBMObject[]
423+
*/
424+
public function _getRelationshipsFromModel(ManyToManyRelationshipPathDescriptor $pathModel): array
418425
{
419-
return $this->relationshipStorageToArray($this->retrieveRelationshipsStorage($pivotTableName));
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,17 @@ protected static function getForeignKeys(string $tableName): ForeignKeys
672660
{
673661
return new ForeignKeys([]);
674662
}
663+
664+
public function _getManyToManyRelationshipDescriptor(string $pathKey): ManyToManyRelationshipPathDescriptor
665+
{
666+
throw new TDBMException('Could not find many to many relationship descriptor key for "'.$pathKey.'"');
667+
}
668+
669+
/**
670+
* @return string[]
671+
*/
672+
public function _getManyToManyRelationshipDescriptorKeys(): array
673+
{
674+
return [];
675+
}
675676
}

src/TDBMService.php

Lines changed: 7 additions & 46 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;
@@ -311,11 +312,11 @@ public function delete(AbstractTDBMObject $object): void
311312
private function deleteManyToManyRelationships(AbstractTDBMObject $object): void
312313
{
313314
foreach ($object->_getDbRows() as $tableName => $dbRow) {
314-
$pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
315-
foreach ($pivotTables as $pivotTable) {
316-
$remoteBeans = $object->_getRelationships($pivotTable);
315+
foreach ($object->_getManyToManyRelationshipDescriptorKeys() as $pathKey) {
316+
$pathModel = $object->_getManyToManyRelationshipDescriptor($pathKey);
317+
$remoteBeans = $object->_getRelationshipsFromModel($pathModel);
317318
foreach ($remoteBeans as $remoteBean) {
318-
$object->_removeRelationship($pivotTable, $remoteBean);
319+
$object->_removeRelationship($pathModel->getPivotName(), $remoteBean);
319320
}
320321
}
321322
}
@@ -1431,26 +1432,11 @@ private function fromCache(string $key, callable $closure)
14311432
}
14321433

14331434
/**
1434-
* @param string $pivotTableName
1435-
* @param AbstractTDBMObject $bean
1436-
*
14371435
* @return AbstractTDBMObject[]|ResultIterator
14381436
*/
1439-
public function _getRelatedBeans(string $pivotTableName, AbstractTDBMObject $bean): ResultIterator
1437+
public function _getRelatedBeans(ManyToManyRelationshipPathDescriptor $pathDescriptor, AbstractTDBMObject $bean): ResultIterator
14401438
{
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-
$columnNames = array_map(function ($name) use ($pivotTableName) {
1448-
return $pivotTableName.'.'.$name;
1449-
}, $localFk->getUnquotedLocalColumns());
1450-
1451-
$filter = SafeFunctions::arrayCombine($columnNames, $primaryKeys);
1452-
1453-
return $this->findObjects($remoteTable, $filter);
1439+
return $this->findObjectsFromSql($pathDescriptor->getTargetName(), $pathDescriptor->getPivotFrom(), $pathDescriptor->getPivotWhere(), $pathDescriptor->getPivotParams($this->getPrimaryKeyValues($bean)));
14541440
}
14551441

14561442
/**
@@ -1480,31 +1466,6 @@ private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMOb
14801466
}
14811467
}
14821468

1483-
/**
1484-
* Returns a list of pivot tables linked to $bean.
1485-
*
1486-
* @param AbstractTDBMObject $bean
1487-
*
1488-
* @return string[]
1489-
*/
1490-
public function _getPivotTablesLinkedToBean(AbstractTDBMObject $bean): array
1491-
{
1492-
$junctionTables = [];
1493-
$allJunctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
1494-
foreach ($bean->_getDbRows() as $dbRow) {
1495-
foreach ($allJunctionTables as $table) {
1496-
// There are exactly 2 FKs since this is a pivot table.
1497-
$fks = array_values($table->getForeignKeys());
1498-
1499-
if ($fks[0]->getForeignTableName() === $dbRow->_getDbTableName() || $fks[1]->getForeignTableName() === $dbRow->_getDbTableName()) {
1500-
$junctionTables[] = $table->getName();
1501-
}
1502-
}
1503-
}
1504-
1505-
return $junctionTables;
1506-
}
1507-
15081469
/**
15091470
* Array of types for tables.
15101471
* Key: table name

0 commit comments

Comments
 (0)