diff --git a/lib/Doctrine/Import/Builder.php b/lib/Doctrine/Import/Builder.php index 7dc64d4ab..6d51c53f3 100644 --- a/lib/Doctrine/Import/Builder.php +++ b/lib/Doctrine/Import/Builder.php @@ -179,6 +179,14 @@ class Doctrine_Import_Builder extends Doctrine_Builder */ protected $_phpDocEmail = '##EMAIL##'; + + /** + * Contains the actAs columns after running buildSetUp + * + * @var array + */ + private $_actAsColumns = array(); + /** * _tpl * @@ -396,9 +404,7 @@ public function buildTableDefinition(array $definition) /** * buildSetUp * - * @param array $options - * @param array $columns - * @param array $relations + * @param array $definition * @return string */ public function buildSetUp(array $definition) @@ -857,21 +863,33 @@ public function buildPhpDocs(array $definition) return $ret; } + /** + * find class matching $name + * + * @param $name + * @return string + */ + private function findTemplateClassMatchingName($name) + { + $classname = $name; + if (class_exists("Doctrine_Template_$name", true)) { + $classname = "Doctrine_Template_$name"; + } + + return $classname; + } + /** * emit a behavior assign * * @param int $level * @param string $name * @param string $option + * @param string $classname * @return string assignation code */ - private function emitAssign($level, $name, $option) + private function emitAssign($level, $name, $option, $classname) { - // find class matching $name - $classname = $name; - if (class_exists("Doctrine_Template_$name", true)) { - $classname = "Doctrine_Template_$name"; - } return " \$" . strtolower($name) . "$level = new $classname($option);". PHP_EOL; } @@ -943,6 +961,7 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi $currentParent = $parent; if (is_array($actAs)) { foreach($actAs as $template => $options) { + $className = $this->findTemplateClassMatchingName($template); if ($template == 'actAs') { // found another actAs $build .= $this->innerBuildActAs($options, $level + 1, $parent, $emittedActAs); @@ -959,7 +978,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi } $optionPHP = $this->varExport($realOptions); - $build .= $this->emitAssign($level, $template, $optionPHP); + $build .= $this->emitAssign($level, $template, $optionPHP, $className); + $this->addActAsColumnsToDefinition($className, $realOptions); if ($level == 0) { $emittedActAs[] = $this->emitActAs($level, $template); } else { @@ -969,7 +989,8 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi $parent = $template; $build .= $this->innerBuildActAs($leftActAs, $level, $template, $emittedActAs); } else { - $build .= $this->emitAssign($level, $template, null); + $build .= $this->emitAssign($level, $template, null, $className); + $this->addActAsColumnsToDefinition($className, array($options)); if ($level == 0) { $emittedActAs[] = $this->emitActAs($level, $template); } else { @@ -979,7 +1000,9 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi } } } else { - $build .= $this->emitAssign($level, $actAs, null); + $className = $this->findTemplateClassMatchingName($actAs); + $build .= $this->emitAssign($level, $actAs, null, $className); + $this->addActAsColumnsToDefinition($className, array()); if ($level == 0) { $emittedActAs[] = $this->emitActAs($level, $actAs); } else { @@ -990,6 +1013,55 @@ private function innerBuildActAs($actAs, $level = 0, $parent = null, array &$emi return $build; } + /** + * Adds the columns of the used actAs behaviors to the comment block. + * + * @param string $className + * @param array $instanceOptions + * + * @throws Doctrine_Import_Builder_Exception + */ + private function addActAsColumnsToDefinition($className, $instanceOptions) + { + // No class specified or class does not exist. + if (!$className || !class_exists($className)) { + return; + } + + $actAsInstance = new $className($instanceOptions); + $options = $actAsInstance->getOptions(); + + if (count($options) == 0) { + return; + } + + // Some behaviors do not contain an array of columns, e.g. SoftDelete. + if (!is_array(reset($options))) { + $options = array($options); + } + + foreach ($options as $name => $column) { + if (!is_array($column) || !array_key_exists('name', $column) || !array_key_exists('type', $column)) { + // 'name' or 'type' not found. Unfortunately there is no logger. What is the best way to abort here? + continue; + } + + if (array_key_exists('disabled', $column) && $column['disabled']) { + // Column has been disabled. + continue; + } + + // Add field, if it does not exist already. + if ( + !array_key_exists($name, $this->_actAsColumns) + && !array_key_exists($column['name'], $this->_actAsColumns) + ) { + $this->_actAsColumns[$name] = $column; + } + } + } + + /** * Build php code for adding record listeners * @@ -1122,6 +1194,8 @@ public function buildDefinition(array $definition) $className = $definition['className']; $extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName; + // Clear actAsColumns + $this->_actAsColumns = array(); if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) { $tableDefinitionCode = $this->buildTableDefinition($definition); $setUpCode = $this->buildSetUp($definition); @@ -1136,6 +1210,7 @@ public function buildDefinition(array $definition) $setUpCode.= $this->buildToString($definition); + $definition['columns'] = array_merge($definition['columns'], $this->_actAsColumns); $docs = PHP_EOL . $this->buildPhpDocs($definition); $content = sprintf(self::$_tpl, $docs, $abstract, diff --git a/tests/Ticket/gh110TestCase.php b/tests/Ticket/gh110TestCase.php new file mode 100644 index 000000000..baee0161c --- /dev/null +++ b/tests/Ticket/gh110TestCase.php @@ -0,0 +1,31 @@ +buildDefinition( + array( + 'className' => 'Ticket_gh110_TestRecord', + 'topLevelClassName' => 'Ticket_gh110_TestRecord', + 'is_base_class' => true, + 'columns' => array( + 'id' => array( + 'type' => 'integer', + 'length' => 4, + ) + ), + 'actAs' => array( + 'SoftDelete' => array(), + 'Timestampable' => array(), + ) + ) + ); + + $this->assertTrue(preg_match('/@property int\s*\$id/', $class)); + $this->assertTrue(preg_match('/@property string\s*\$deleted_at/', $class)); + $this->assertTrue(preg_match('/@property string\s*\$created_at/', $class)); + $this->assertTrue(preg_match('/@property string\s*\$updated_at/', $class)); + } +}