Skip to content

Commit bbab48b

Browse files
author
Will Leonardi
committed
Added polymorphic many-to-many relationships.
1 parent 21c75aa commit bbab48b

File tree

8 files changed

+539
-14
lines changed

8 files changed

+539
-14
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: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,21 +4423,33 @@ protected function addCrossFkScheduledForDeletion(&$script, CrossForeignKeys $cr
44234423
foreach ($crossFKs->getIncomingForeignKey()->getColumnObjectsMapping() as $reference) {
44244424
$local = $reference['local'];
44254425
$foreign = $reference['foreign'];
4426+
$value = $reference['value'];
44264427

44274428
$idx = array_search($local, $crossPks, true);
4428-
$script .= "
4429+
if ($value) {
4430+
$script .= "
4431+
\$entryPk[$idx] = '$value';";
4432+
} else {
4433+
$script .= "
44294434
\$entryPk[$idx] = \$this->get{$foreign->getPhpName()}();";
4435+
}
44304436
}
44314437

44324438
$combinationIdx = 0;
44334439
foreach ($crossFKs->getCrossForeignKeys() as $crossFK) {
44344440
foreach ($crossFK->getColumnObjectsMapping() as $reference) {
44354441
$local = $reference['local'];
44364442
$foreign = $reference['foreign'];
4443+
$value = $reference['value'];
44374444

44384445
$idx = array_search($local, $crossPks, true);
4439-
$script .= "
4440-
\$entryPk[$idx] = \$combination[$combinationIdx]->get{$foreign->getPhpName()}();";
4446+
if ($value) {
4447+
$script .= "
4448+
\$entryPk[$idx] = '$value';";
4449+
} else {
4450+
$script .= "
4451+
\$entryPk[$idx] = \$combination[$combinationIdx]->get{$foreign->getPhpName()}();";
4452+
}
44414453
}
44424454
$combinationIdx++;
44434455
}
@@ -4471,20 +4483,32 @@ protected function addCrossFkScheduledForDeletion(&$script, CrossForeignKeys $cr
44714483
foreach ($crossFKs->getIncomingForeignKey()->getColumnObjectsMapping() as $reference) {
44724484
$local = $reference['local'];
44734485
$foreign = $reference['foreign'];
4486+
$value = $reference['value'];
44744487

44754488
$idx = array_search($local, $crossPks, true);
4476-
$script .= "
4477-
\$entryPk[$idx] = \$this->get{$foreign->getPhpName()}();";
4489+
if ($value) {
4490+
$script .= "
4491+
\$entryPk[$idx] = '$value';";
4492+
} else {
4493+
$script .= "
4494+
\$entryPk[$idx] = \$this->get{$foreign->getPhpName()}();";
4495+
}
44784496
}
44794497

44804498
$crossFK = $crossFKs->getCrossForeignKeys()[0];
44814499
foreach ($crossFK->getColumnObjectsMapping() as $reference) {
44824500
$local = $reference['local'];
44834501
$foreign = $reference['foreign'];
4502+
$value = $reference['value'];
44844503

44854504
$idx = array_search($local, $crossPks, true);
4486-
$script .= "
4505+
if ($value) {
4506+
$script .= "
4507+
\$entryPk[$idx] = '$value';";
4508+
} else {
4509+
$script .= "
44874510
\$entryPk[$idx] = \$entry->get{$foreign->getPhpName()}();";
4511+
}
44884512
}
44894513

44904514
$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;
@@ -800,15 +801,39 @@ public function getCrossFks()
800801
$crossFks = [];
801802
foreach ($this->referrers as $refFK) {
802803
if ($refFK->getTable()->isCrossRef()) {
803-
$crossFK = new CrossForeignKeys($refFK, $this);
804-
foreach ($refFK->getOtherFks() as $fk) {
805-
if ($fk->isAtLeastOneLocalPrimaryKeyIsRequired() &&
806-
$crossFK->isAtLeastOneLocalPrimaryKeyNotCovered($fk)) {
807-
$crossFK->addCrossForeignKey($fk);
804+
// group all polymorphic fks
805+
$refFKOtherFKs = $refFK->getOtherFks();
806+
$havePMFKs = false;
807+
foreach ($refFKOtherFKs as $k => $refFKOtherFk) {
808+
if ($refFKOtherFk->isPolymorphic()) {
809+
$havePMFKs = true;
810+
$refFKOtherFKs[$refFKOtherFk->getPolymorphicSignature()][] = $refFKOtherFk;
811+
unset($refFKOtherFKs[$k]);
808812
}
809813
}
810-
if ($crossFK->hasCrossForeignKeys()) {
811-
$crossFks[] = $crossFK;
814+
815+
if (!$havePMFKs) {
816+
$combinations = [$refFKOtherFKs];
817+
} else {
818+
// Calculate cartesian product
819+
$combinations = CartesianProduct::get(array_values($refFKOtherFKs));
820+
}
821+
822+
foreach ($combinations as $combination) {
823+
$crossFK = new CrossForeignKeys($refFK, $this);
824+
foreach ($combination as $fk) {
825+
/** @var ForeignKey $fk */
826+
if ($fk->isAtLeastOneLocalPrimaryKeyIsRequired() &&
827+
$crossFK->isAtLeastOneLocalPrimaryKeyNotCovered($fk) &&
828+
!$crossFK->getIncomingForeignKey()->isAPolymorphicSiblingRelationship($fk)
829+
) {
830+
$crossFK->addCrossForeignKey($fk);
831+
}
832+
}
833+
834+
if ($crossFK->hasCrossForeignKeys()) {
835+
$crossFks[] = $crossFK;
836+
}
812837
}
813838
}
814839
}

0 commit comments

Comments
 (0)