Skip to content

Commit 7077b85

Browse files
Will Leonardiwleona3
authored andcommitted
Added polymorphic many-to-many relationships.
1 parent efafd09 commit 7077b85

File tree

8 files changed

+537
-12
lines changed

8 files changed

+537
-12
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php namespace Propel\Common\Util;
2+
3+
class CartesianProduct
4+
{
5+
public static function get($input) {
6+
$result = array();
7+
8+
while (list($key, $values) = each($input)) {
9+
if (empty($values)) {
10+
continue;
11+
}
12+
13+
if (empty($result)) {
14+
if (!is_array($values)) {
15+
$values = [$values];
16+
}
17+
foreach($values as $value) {
18+
$result[] = array($key => $value);
19+
}
20+
}
21+
else {
22+
$append = array();
23+
24+
foreach($result as &$product) {
25+
$product[$key] = array_shift($values);
26+
27+
$copy = $product;
28+
29+
foreach($values as $item) {
30+
$copy[$key] = $item;
31+
$append[] = $copy;
32+
}
33+
34+
array_unshift($values, $product[$key]);
35+
}
36+
37+
$result = array_merge($result, $append);
38+
}
39+
}
40+
41+
return $result;
42+
}
43+
}

src/Propel/Generator/Builder/Om/ObjectBuilder.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4458,21 +4458,33 @@ protected function addCrossFkScheduledForDeletion(&$script, CrossForeignKeys $cr
44584458
foreach ($crossFKs->getIncomingForeignKey()->getColumnObjectsMapping() as $reference) {
44594459
$local = $reference['local'];
44604460
$foreign = $reference['foreign'];
4461+
$value = $reference['value'];
44614462

44624463
$idx = array_search($local, $crossPks, true);
4463-
$script .= "
4464+
if ($value) {
4465+
$script .= "
4466+
\$entryPk[$idx] = '$value';";
4467+
} else {
4468+
$script .= "
44644469
\$entryPk[$idx] = \$this->get{$foreign->getPhpName()}();";
4470+
}
44654471
}
44664472

44674473
$combinationIdx = 0;
44684474
foreach ($crossFKs->getCrossForeignKeys() as $crossFK) {
44694475
foreach ($crossFK->getColumnObjectsMapping() as $reference) {
44704476
$local = $reference['local'];
44714477
$foreign = $reference['foreign'];
4478+
$value = $reference['value'];
44724479

44734480
$idx = array_search($local, $crossPks, true);
4474-
$script .= "
4481+
if ($value) {
4482+
$script .= "
4483+
\$entryPk[$idx] = '$value';";
4484+
} else {
4485+
$script .= "
44754486
\$entryPk[$idx] = \$combination[$combinationIdx]->get{$foreign->getPhpName()}();";
4487+
}
44764488
}
44774489
$combinationIdx++;
44784490
}
@@ -4506,20 +4518,32 @@ protected function addCrossFkScheduledForDeletion(&$script, CrossForeignKeys $cr
45064518
foreach ($crossFKs->getIncomingForeignKey()->getColumnObjectsMapping() as $reference) {
45074519
$local = $reference['local'];
45084520
$foreign = $reference['foreign'];
4521+
$value = $reference['value'];
45094522

45104523
$idx = array_search($local, $crossPks, true);
4511-
$script .= "
4524+
if ($value) {
4525+
$script .= "
4526+
\$entryPk[$idx] = '$value';";
4527+
} else {
4528+
$script .= "
45124529
\$entryPk[$idx] = \$this->get{$foreign->getPhpName()}();";
4530+
}
45134531
}
45144532

45154533
$crossFK = $crossFKs->getCrossForeignKeys()[0];
45164534
foreach ($crossFK->getColumnObjectsMapping() as $reference) {
45174535
$local = $reference['local'];
45184536
$foreign = $reference['foreign'];
4537+
$value = $reference['value'];
45194538

45204539
$idx = array_search($local, $crossPks, true);
4521-
$script .= "
4540+
if ($value) {
4541+
$script .= "
4542+
\$entryPk[$idx] = '$value';";
4543+
} else {
4544+
$script .= "
45224545
\$entryPk[$idx] = \$entry->get{$foreign->getPhpName()}();";
4546+
}
45234547
}
45244548

45254549
$script .= "

src/Propel/Generator/Model/CrossForeignKeys.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public function isAtLeastOneLocalPrimaryKeyNotCovered(ForeignKey $fk)
136136
foreach ($primaryKeys as $primaryKey) {
137137
$covered = false;
138138
foreach ($this->getCrossForeignKeys() as $crossFK) {
139-
if ($crossFK->hasLocalColumn($primaryKey)) {
139+
if ($crossFK->hasLocalColumn($primaryKey) && $crossFK->getLocalValues() == $fk->getLocalValues()) {
140140
$covered = true;
141141
break;
142142
}
@@ -150,6 +150,17 @@ public function isAtLeastOneLocalPrimaryKeyNotCovered(ForeignKey $fk)
150150
return false;
151151
}
152152

153+
public function isNotContainingColumnSignature(ForeignKey $fk)
154+
{
155+
foreach ($this->getCrossForeignKeys() as $crossFK) {
156+
if ($crossFK->isTheSameColumnSignature($fk)) {
157+
return false;
158+
}
159+
}
160+
161+
return true;
162+
}
163+
153164
/**
154165
* Returns all primary keys of middle-table which are not already covered by at least on of our cross foreignKey collection.
155166
*

src/Propel/Generator/Model/ForeignKey.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,4 +1019,30 @@ public function isAtLeastOneLocalPrimaryKey()
10191019

10201020
return 0 !== count($cols);
10211021
}
1022+
1023+
public function isTheSamePolymorphicSignature(ForeignKey $fk)
1024+
{
1025+
return $this->getPolymorphicSignature() == $fk->getPolymorphicSignature();
1026+
}
1027+
1028+
public function isAPolymorphicSiblingRelationship(ForeignKey $fk)
1029+
{
1030+
if (!($this->isPolymorphic() && $fk->isPolymorphic())) {
1031+
return false;
1032+
}
1033+
1034+
return $this->isTheSamePolymorphicSignature($fk);
1035+
}
1036+
1037+
public function getPolymorphicSignature()
1038+
{
1039+
if (!$this->isPolymorphic()) {
1040+
return false;
1041+
}
1042+
1043+
$sortedCols = $this->getLocalColumns();
1044+
sort($sortedCols);
1045+
1046+
return sha1(implode('|||', $sortedCols));
1047+
}
10221048
}

src/Propel/Generator/Model/Table.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Propel\Generator\Model;
1212

13+
use Propel\Common\Util\CartesianProduct;
1314
use Propel\Generator\Config\GeneratorConfig;
1415
use Propel\Generator\Exception\BuildException;
1516
use Propel\Generator\Exception\EngineException;
@@ -819,15 +820,39 @@ public function getCrossFks()
819820
$crossFks = [];
820821
foreach ($this->referrers as $refFK) {
821822
if ($refFK->getTable()->isCrossRef()) {
822-
$crossFK = new CrossForeignKeys($refFK, $this);
823-
foreach ($refFK->getOtherFks() as $fk) {
824-
if ($fk->isAtLeastOneLocalPrimaryKeyIsRequired() &&
825-
$crossFK->isAtLeastOneLocalPrimaryKeyNotCovered($fk)) {
826-
$crossFK->addCrossForeignKey($fk);
823+
// group all polymorphic fks
824+
$refFKOtherFKs = $refFK->getOtherFks();
825+
$havePMFKs = false;
826+
foreach ($refFKOtherFKs as $k => $refFKOtherFk) {
827+
if ($refFKOtherFk->isPolymorphic()) {
828+
$havePMFKs = true;
829+
$refFKOtherFKs[$refFKOtherFk->getPolymorphicSignature()][] = $refFKOtherFk;
830+
unset($refFKOtherFKs[$k]);
827831
}
828832
}
829-
if ($crossFK->hasCrossForeignKeys()) {
830-
$crossFks[] = $crossFK;
833+
834+
if (!$havePMFKs) {
835+
$combinations = [$refFKOtherFKs];
836+
} else {
837+
// Calculate cartesian product
838+
$combinations = CartesianProduct::get(array_values($refFKOtherFKs));
839+
}
840+
841+
foreach ($combinations as $combination) {
842+
$crossFK = new CrossForeignKeys($refFK, $this);
843+
foreach ($combination as $fk) {
844+
/** @var ForeignKey $fk */
845+
if ($fk->isAtLeastOneLocalPrimaryKeyIsRequired() &&
846+
$crossFK->isAtLeastOneLocalPrimaryKeyNotCovered($fk) &&
847+
!$crossFK->getIncomingForeignKey()->isAPolymorphicSiblingRelationship($fk)
848+
) {
849+
$crossFK->addCrossForeignKey($fk);
850+
}
851+
}
852+
853+
if ($crossFK->hasCrossForeignKeys()) {
854+
$crossFks[] = $crossFK;
855+
}
831856
}
832857
}
833858
}

0 commit comments

Comments
 (0)