diff --git a/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnBehavior.php b/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnBehavior.php index 53276b46ba..42533ade8c 100644 --- a/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnBehavior.php +++ b/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnBehavior.php @@ -28,6 +28,16 @@ class AggregateColumnBehavior extends Behavior 'foreign_schema' => null, ); + /** + * Multiple aggregates on the same table is OK. + * + * @return bool + */ + public function allowMultiple() + { + return true; + } + /** * Add the aggregate key to the current table */ @@ -51,7 +61,9 @@ public function modifyTable() if (!$foreignTable->hasBehavior('concrete_inheritance_parent')) { $relationBehavior = new AggregateColumnRelationBehavior(); $relationBehavior->setName('aggregate_column_relation'); + $relationBehavior->setId('aggregate_column_relation_'.$this->getId()); $relationBehavior->addParameter(array('name' => 'foreign_table', 'value' => $table->getName())); + $relationBehavior->addParameter(array('name' => 'aggregate_name', 'value' => $this->getColumn()->getPhpName())); $relationBehavior->addParameter(array('name' => 'update_method', 'value' => 'update' . $this->getColumn()->getPhpName())); $foreignTable->addBehavior($relationBehavior); } diff --git a/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnRelationBehavior.php b/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnRelationBehavior.php index 555a153cbf..ecf99b764d 100644 --- a/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnRelationBehavior.php +++ b/src/Propel/Generator/Behavior/AggregateColumn/AggregateColumnRelationBehavior.php @@ -21,15 +21,22 @@ class AggregateColumnRelationBehavior extends Behavior { // default parameters value protected $parameters = array( - 'foreign_table' => '', - 'update_method' => '', + 'foreign_table' => '', + 'update_method' => '', + 'aggregate_name' => '', ); + public function allowMultiple() + { + return true; + } + public function postSave($builder) { $relationName = $this->getRelationName($builder); + $aggregateName = $this->getParameter('aggregate_name'); - return "\$this->updateRelated{$relationName}(\$con);"; + return "\$this->updateRelated{$relationName}{$aggregateName}(\$con);"; } // no need for a postDelete() hook, since delete() uses Query::delete(), @@ -39,11 +46,12 @@ public function objectAttributes($builder) { $relationName = $this->getRelationName($builder); $relatedClass = $builder->getClassNameFromBuilder($builder->getNewStubObjectBuilder($this->getForeignTable())); + $aggregateName = $this->getParameter('aggregate_name'); return "/** * @var $relatedClass */ -protected \$old{$relationName}; +protected \$old{$relationName}{$aggregateName}; "; } @@ -58,6 +66,7 @@ protected function addObjectUpdateRelated($builder) return $this->renderTemplate('objectUpdateRelated', array( 'relationName' => $relationName, + 'aggregateName' => $this->getParameter('aggregate_name'), 'variableName' => lcfirst($relationName), 'updateMethodName' => $this->getParameter('update_method'), )); @@ -66,13 +75,14 @@ protected function addObjectUpdateRelated($builder) public function objectFilter(&$script, $builder) { $relationName = $this->getRelationName($builder); + $aggregateName = $this->getParameter('aggregate_name'); $relatedClass = $builder->getClassNameFromBuilder($builder->getNewStubObjectBuilder($this->getForeignTable())); $search = " public function set{$relationName}({$relatedClass} \$v = null) {"; $replace = $search . " // aggregate_column_relation behavior if (null !== \$this->a{$relationName} && \$v !== \$this->a{$relationName}) { - \$this->old{$relationName} = \$this->a{$relationName}; + \$this->old{$relationName}{$aggregateName} = \$this->a{$relationName}; }"; $script = str_replace($search, $replace, $script); } @@ -90,8 +100,9 @@ public function preDeleteQuery($builder) protected function getFindRelated($builder) { $relationName = $this->getRelationName($builder); + $aggregateName = $this->getParameter('aggregate_name'); - return "\$this->findRelated{$relationName}s(\$con);"; + return "\$this->findRelated{$relationName}{$aggregateName}s(\$con);"; } public function postUpdateQuery($builder) @@ -107,13 +118,15 @@ public function postDeleteQuery($builder) protected function getUpdateRelated($builder) { $relationName = $this->getRelationName($builder); + $aggregateName = $this->getParameter('aggregate_name'); - return "\$this->updateRelated{$relationName}s(\$con);"; + return "\$this->updateRelated{$relationName}{$aggregateName}s(\$con);"; } public function queryMethods($builder) { $script = ''; + $script .= $this->addQueryFindRelated($builder); $script .= $this->addQueryUpdateRelated($builder); @@ -134,7 +147,8 @@ protected function addQueryFindRelated($builder) return $this->renderTemplate('queryFindRelated', array( 'foreignTable' => $this->getForeignTable(), 'relationName' => $relationName, - 'variableName' => lcfirst($relationName), + 'aggregateName' => $this->getParameter('aggregate_name'), + 'variableName' => lcfirst($relationName.$this->getParameter('aggregate_name')), 'foreignQueryName' => $foreignQueryBuilder->getClassName(), 'refRelationName' => $builder->getRefFKPhpNameAffix($foreignKey), )); @@ -146,7 +160,8 @@ protected function addQueryUpdateRelated($builder) return $this->renderTemplate('queryUpdateRelated', array( 'relationName' => $relationName, - 'variableName' => lcfirst($relationName), + 'aggregateName' => $this->getParameter('aggregate_name'), + 'variableName' => lcfirst($relationName.$this->getParameter('aggregate_name')), 'updateMethodName' => $this->getParameter('update_method'), )); } diff --git a/src/Propel/Generator/Behavior/AggregateColumn/templates/objectUpdateRelated.php b/src/Propel/Generator/Behavior/AggregateColumn/templates/objectUpdateRelated.php index 5875294c63..b594c7c185 100644 --- a/src/Propel/Generator/Behavior/AggregateColumn/templates/objectUpdateRelated.php +++ b/src/Propel/Generator/Behavior/AggregateColumn/templates/objectUpdateRelated.php @@ -4,13 +4,13 @@ * * @param ConnectionInterface $con A connection object */ -protected function updateRelated=$relationName?>(ConnectionInterface $con) +protected function updateRelated=$relationName.$aggregateName?>(ConnectionInterface $con) { if ($=$variableName?> = $this->get=$relationName?>()) { $=$variableName?>->=$updateMethodName?>($con); } - if ($this->old=$relationName?>) { - $this->old=$relationName?>->=$updateMethodName?>($con); - $this->old=$relationName?> = null; + if ($this->old=$relationName.$aggregateName?>) { + $this->old=$relationName.$aggregateName?>->=$updateMethodName?>($con); + $this->old=$relationName.$aggregateName?> = null; } } diff --git a/src/Propel/Generator/Behavior/AggregateColumn/templates/queryFindRelated.php b/src/Propel/Generator/Behavior/AggregateColumn/templates/queryFindRelated.php index 4a9d0f8108..46d01d5559 100644 --- a/src/Propel/Generator/Behavior/AggregateColumn/templates/queryFindRelated.php +++ b/src/Propel/Generator/Behavior/AggregateColumn/templates/queryFindRelated.php @@ -4,7 +4,7 @@ * * @param ConnectionInterface $con A connection object */ -protected function findRelated=$relationName?>s($con) +protected function findRelated=$relationName.$aggregateName?>s($con) { $criteria = clone $this; if ($this->useAliasInSQL) { diff --git a/src/Propel/Generator/Behavior/AggregateColumn/templates/queryUpdateRelated.php b/src/Propel/Generator/Behavior/AggregateColumn/templates/queryUpdateRelated.php index 8707f987a3..2c54b4b461 100644 --- a/src/Propel/Generator/Behavior/AggregateColumn/templates/queryUpdateRelated.php +++ b/src/Propel/Generator/Behavior/AggregateColumn/templates/queryUpdateRelated.php @@ -1,5 +1,5 @@ -protected function updateRelated=$relationName?>s($con) +protected function updateRelated=$relationName.$aggregateName?>s($con) { foreach ($this->=$variableName?>s as $=$variableName?>) { $=$variableName?>->= $updateMethodName ?>($con); diff --git a/src/Propel/Generator/Behavior/Archivable/ArchivableBehavior.php b/src/Propel/Generator/Behavior/Archivable/ArchivableBehavior.php index 9805916f0b..c6d3b2c448 100644 --- a/src/Propel/Generator/Behavior/Archivable/ArchivableBehavior.php +++ b/src/Propel/Generator/Behavior/Archivable/ArchivableBehavior.php @@ -42,7 +42,7 @@ class ArchivableBehavior extends Behavior public function modifyDatabase() { foreach ($this->getDatabase()->getTables() as $table) { - if ($table->hasBehavior($this->getName())) { + if ($table->hasBehavior($this->getId())) { // don't add the same behavior twice continue; } diff --git a/src/Propel/Generator/Behavior/Versionable/VersionableBehavior.php b/src/Propel/Generator/Behavior/Versionable/VersionableBehavior.php index 266a86cffb..4d39cde42c 100644 --- a/src/Propel/Generator/Behavior/Versionable/VersionableBehavior.php +++ b/src/Propel/Generator/Behavior/Versionable/VersionableBehavior.php @@ -43,7 +43,7 @@ class VersionableBehavior extends Behavior public function modifyDatabase() { foreach ($this->getDatabase()->getTables() as $table) { - if ($table->hasBehavior($this->getName())) { + if ($table->hasBehavior($this->getId())) { // don't add the same behavior twice continue; } diff --git a/src/Propel/Generator/Behavior/Versionable/VersionableBehaviorObjectBuilderModifier.php b/src/Propel/Generator/Behavior/Versionable/VersionableBehaviorObjectBuilderModifier.php index 77edb7dd27..df9c9e9bad 100644 --- a/src/Propel/Generator/Behavior/Versionable/VersionableBehaviorObjectBuilderModifier.php +++ b/src/Propel/Generator/Behavior/Versionable/VersionableBehaviorObjectBuilderModifier.php @@ -377,7 +377,7 @@ public function populateFromVersion(\$version, \$con = null, &\$loadedObjects = $plural = false; foreach ($this->behavior->getVersionableFks() as $fk) { $foreignTable = $fk->getForeignTable(); - $foreignVersionTable = $fk->getForeignTable()->getBehavior($this->behavior->getName())->getVersionTable(); + $foreignVersionTable = $fk->getForeignTable()->getBehavior($this->behavior->getId())->getVersionTable(); $relatedClassName = $this->builder->getClassNameFromBuilder($this->builder->getNewStubObjectBuilder($foreignTable)); $relatedVersionQueryClassName = $this->builder->getClassNameFromBuilder($this->builder->getNewStubQueryBuilder($foreignVersionTable)); $fkColumnName = $fk->getLocalColumnName(); @@ -407,7 +407,7 @@ public function populateFromVersion(\$version, \$con = null, &\$loadedObjects = $plural = false; $fkPhpName = $this->builder->getRefFKPhpNameAffix($fk, $plural); $foreignTable = $fk->getTable(); - $foreignBehavior = $foreignTable->getBehavior($this->behavior->getName()); + $foreignBehavior = $foreignTable->getBehavior($this->behavior->getId()); $foreignVersionTable = $foreignBehavior->getVersionTable(); $fkColumnIds = $this->behavior->getReferrerIdsColumn($fk); $fkColumnVersions = $this->behavior->getReferrerVersionsColumn($fk); diff --git a/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php b/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php index 8ca689d532..b5e354e314 100644 --- a/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php +++ b/src/Propel/Generator/Builder/Om/AbstractOMBuilder.php @@ -789,7 +789,7 @@ public function applyBehaviorModifierBase($hookName, $modifier, &$script, $tab = continue; } $script .= " -" . $tab . '// ' . $behavior->getName() . " behavior +" . $tab . '// ' . $behavior->getId() . " behavior "; $script .= preg_replace('/^/m', $tab, $addedScript); } diff --git a/src/Propel/Generator/Builder/Om/TableMapBuilder.php b/src/Propel/Generator/Builder/Om/TableMapBuilder.php index 88fedc9415..aa5fcdb585 100644 --- a/src/Propel/Generator/Builder/Om/TableMapBuilder.php +++ b/src/Propel/Generator/Builder/Om/TableMapBuilder.php @@ -614,7 +614,7 @@ public function getBehaviors() return array("; foreach ($behaviors as $behavior) { $script .= " - '{$behavior->getName()}' => array("; + '{$behavior->getId()}' => array("; foreach ($behavior->getParameters() as $key => $value) { $script .= "'$key' => "; if (is_array($value)) { diff --git a/src/Propel/Generator/Model/Behavior.php b/src/Propel/Generator/Model/Behavior.php index 5ebafc0031..e1c1a12f37 100644 --- a/src/Propel/Generator/Model/Behavior.php +++ b/src/Propel/Generator/Model/Behavior.php @@ -11,6 +11,7 @@ namespace Propel\Generator\Model; use Propel\Generator\Builder\Util\PropelTemplate; +use Propel\Generator\Exception\LogicException; /** * Information about behaviors of a table. @@ -34,6 +35,13 @@ class Behavior extends MappingModel */ protected $database; + /** + * The behavior id. + * + * @var string + */ + protected $id; + /** * The behavior name. * @@ -88,6 +96,41 @@ class Behavior extends MappingModel public function setName($name) { $this->name = $name; + + if ($this->id === null) { + $this->id = $name; + } + } + + /** + * Sets the id of the Behavior + * + * @param string $id The id of the behavior + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * Indicates whether the behavior can be applied several times on the same + * table or not. + * + * @return bool + */ + public function allowMultiple() + { + return false; + } + + /** + * Returns the id of the Behavior + * + * @return string + */ + public function getId() + { + return $this->id; } /** @@ -226,7 +269,7 @@ public function getTableModificationOrder() public function modifyDatabase() { foreach ($this->getTables() as $table) { - if ($table->hasBehavior($this->getName())) { + if ($table->hasBehavior($this->getId())) { // don't add the same behavior twice continue; } @@ -335,7 +378,13 @@ public function getColumnForParameter($name) protected function setupObject() { - $this->name = $this->getAttribute("name"); + $this->setName($this->getAttribute('name')); + + if (!$this->allowMultiple() && $id = $this->getAttribute('id')) { + throw new LogicException(sprintf('Defining an ID (%s) on a behavior which does not allow multiple instances makes no sense', $id)); + } + + $this->id = $this->getAttribute('id', $this->name); } /** diff --git a/src/Propel/Generator/Model/BehaviorableTrait.php b/src/Propel/Generator/Model/BehaviorableTrait.php index 9a8fefbbe1..e4e098a5d5 100644 --- a/src/Propel/Generator/Model/BehaviorableTrait.php +++ b/src/Propel/Generator/Model/BehaviorableTrait.php @@ -59,8 +59,19 @@ public function addBehavior($bdata) { if ($bdata instanceof Behavior) { $behavior = $bdata; + + // the new behavior is already registered + if ($this->hasBehavior($behavior->getId()) && $behavior->allowMultiple()) { + // the user probably just forgot to specify the "id" attribute + if ($behavior->getId() === $behavior->getName()) { + throw new BuildException(sprintf('Behavior "%s" is already registered. Specify a different ID attribute to register the same behavior several times.', $behavior->getName())); + } else { // or he copy-pasted it and forgot to update it. + throw new BuildException(sprintf('A behavior with ID "%s" is already registered.', $behavior->getId())); + } + } + $this->registerBehavior($behavior); - $this->behaviors[$behavior->getName()] = $behavior; + $this->behaviors[$behavior->getId()] = $behavior; return $behavior; } @@ -92,24 +103,24 @@ public function getBehaviors() /** * check if the given behavior exists * - * @param string $name the behavior name + * @param string $id the behavior id * @return boolean True if the behavior exists */ - public function hasBehavior($name) + public function hasBehavior($id) { - return array_key_exists($name, $this->behaviors); + return array_key_exists($id, $this->behaviors); } /** - * Get behavior by name + * Get behavior by id * - * @param string $name the behavior name + * @param string $id the behavior id * @return Behavior a behavior object or null if the behavior doesn't exist */ - public function getBehavior($name) + public function getBehavior($id) { - if ($this->hasBehavior($name)) { - return $this->behaviors[$name]; + if ($this->hasBehavior($id)) { + return $this->behaviors[$id]; } return null; diff --git a/src/Propel/Generator/Model/Schema.php b/src/Propel/Generator/Model/Schema.php index 7d7a55a438..6ead874db1 100644 --- a/src/Propel/Generator/Model/Schema.php +++ b/src/Propel/Generator/Model/Schema.php @@ -274,7 +274,7 @@ public function joinSchemas(array $schemas) } // join database behaviors foreach ($addDb->getBehaviors() as $addBehavior) { - if (!$db->hasBehavior($addBehavior->getName())) { + if (!$db->hasBehavior($addBehavior->getId())) { $db->addBehavior($addBehavior); } } diff --git a/src/Propel/Generator/Schema/Dumper/XmlDumper.php b/src/Propel/Generator/Schema/Dumper/XmlDumper.php index e8e8315df0..66531b99f1 100644 --- a/src/Propel/Generator/Schema/Dumper/XmlDumper.php +++ b/src/Propel/Generator/Schema/Dumper/XmlDumper.php @@ -314,6 +314,10 @@ private function appendBehaviorNode(Behavior $behavior, \DOMNode $parentNode) $behaviorNode = $parentNode->appendChild($this->document->createElement('behavior')); $behaviorNode->setAttribute('name', $behavior->getName()); + if ($behavior->allowMultiple()) { + $behaviorNode->setAttribute('id', $behavior->getId()); + } + foreach ($behavior->getParameters() as $name => $value) { $parameterNode = $behaviorNode->appendChild($this->document->createElement('parameter')); $parameterNode->setAttribute('name', $name); diff --git a/tests/Fixtures/bookstore/behavior-aggregate-schema.xml b/tests/Fixtures/bookstore/behavior-aggregate-schema.xml index 4397775ec6..b039190960 100644 --- a/tests/Fixtures/bookstore/behavior-aggregate-schema.xml +++ b/tests/Fixtures/bookstore/behavior-aggregate-schema.xml @@ -20,11 +20,18 @@