From d1df55e3162f492e4bffee40a1217bf65afaf830 Mon Sep 17 00:00:00 2001 From: "Jonathan.Wage" Date: Wed, 19 Dec 2007 22:41:11 +0000 Subject: [PATCH] Refactorings for schema files and Doctrine_Record builder. More to come but got held up on some things that zYne- must fix first. --- lib/Doctrine.php | 68 ++-- lib/Doctrine/Configurable.php | 3 +- lib/Doctrine/Data/Import.php | 6 +- lib/Doctrine/Import.php | 12 +- lib/Doctrine/Import/Builder.php | 491 +++++++++++------------- lib/Doctrine/Import/Schema.php | 328 +++++++--------- package.xml | 4 +- tests/Import/SchemaTestCase.php | 8 +- tools/sandbox/config.php.dist | 2 + tools/sandbox/{cli => doctrine} | 2 +- tools/sandbox/{cli.php => doctrine.php} | 0 11 files changed, 443 insertions(+), 481 deletions(-) rename tools/sandbox/{cli => doctrine} (68%) rename tools/sandbox/{cli.php => doctrine.php} (100%) diff --git a/lib/Doctrine.php b/lib/Doctrine.php index c592a294e..3233f45eb 100644 --- a/lib/Doctrine.php +++ b/lib/Doctrine.php @@ -192,6 +192,7 @@ final class Doctrine const ATTR_QUERY_CACHE = 157; const ATTR_QUERY_CACHE_LIFESPAN = 158; const ATTR_AUTOLOAD_TABLE_CLASSES = 160; + const ATTR_MODEL_LOADING = 161; /** * LIMIT CONSTANTS @@ -425,6 +426,23 @@ final class Doctrine */ const IDENTIFIER_COMPOSITE = 4; + /** + * MODEL_LOADING_AGRESSIVE + * + * Constant for agressive model loading + * Will require_once() all found model files + */ + const MODEL_LOADING_AGRESSIVE = 1; + + /** + * MODEL_LOADING_CONSERVATIVE + * + * Constant for conservative model loading + * Will not require_once() found model files inititally instead it will build an array + * and reference it in autoload() when a class is needed it will require_once() it + */ + const MODEL_LOADING_CONSERVATIVE= 2; + /** * Path * @@ -505,45 +523,44 @@ final class Doctrine * Recursively load all models from a directory or array of directories * * @param string $directory Path to directory of models or array of directory paths + * @param bool $aggressive Bool true/false for whether to load models aggressively. + * If true it will require_once() all found .php files * @return array $loadedModels */ - public static function loadModels($directory) + public static function loadModels($directory, $agressive = true) { + $loadedModels = array(); + if ($directory !== null) { $manager = Doctrine_Manager::getInstance(); - + foreach ((array) $directory as $dir) { $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::LEAVES_ONLY); foreach ($it as $file) { $e = explode('.', $file->getFileName()); if (end($e) === 'php' && strpos($file->getFileName(), '.inc') === false) { - self::$_loadedModelFiles[] = array( - 'filename' => $e[0], - 'filepath' => $file->getPathName() - ); - } - } - } - - $loadedModels = array(); - - $modelFiles = self::$_loadedModelFiles; - - foreach ($modelFiles as $key => $model) { - $declaredBefore = get_declared_classes(); - require_once $model['filepath']; - $declaredAfter = get_declared_classes(); - // Using array_slice because array_diff is broken is some PHP versions - $foundClasses = array_slice($declaredAfter, count($declaredBefore) - 1); - if ($foundClasses) { - foreach ($foundClasses as $className) { - if (self::isValidModelClass($className) && !in_array($className, $loadedModels)) { - $loadedModels[] = $className; + + if ($manager->getAttribute(Doctrine::ATTR_MODEL_LOADING) == Doctrine::MODEL_LOADING_CONSERVATIVE) { + self::$_loadedModelFiles[$e[0]] = $file->getPathName(); + $loadedModels[] = $e[0]; + } else { + $declaredBefore = get_declared_classes(); + require_once($file->getPathName()); + + $declaredAfter = get_declared_classes(); + // Using array_slice because array_diff is broken is some PHP versions + $foundClasses = array_slice($declaredAfter, count($declaredBefore) - 1); + if ($foundClasses) { + foreach ($foundClasses as $className) { + if (self::isValidModelClass($className) && !in_array($className, $loadedModels)) { + $loadedModels[] = $className; + } + } + } } } } - } } @@ -567,6 +584,7 @@ final class Doctrine $classes = get_declared_classes(); $classes = array_merge($classes, array_keys(self::$_loadedModelFiles)); } + return self::filterInvalidModels($classes); } diff --git a/lib/Doctrine/Configurable.php b/lib/Doctrine/Configurable.php index f7c9b7213..1be3e4697 100644 --- a/lib/Doctrine/Configurable.php +++ b/lib/Doctrine/Configurable.php @@ -89,7 +89,7 @@ abstract class Doctrine_Configurable extends Doctrine_Locator_Injectable } switch ($attribute) { case Doctrine::ATTR_FETCHMODE: - throw new Doctrine_Exception('Deprecated attribute. See http://doctrine.pengus.net/doctrine/manual/new/?chapter=configuration'); + throw new Doctrine_Exception('Deprecated attribute. See http://www.phpdoctrine.org/documentation/manual?chapter=configuration'); case Doctrine::ATTR_LISTENER: $this->setEventListener($value); break; @@ -125,6 +125,7 @@ abstract class Doctrine_Configurable extends Doctrine_Locator_Injectable case Doctrine::ATTR_THROW_EXCEPTIONS: case Doctrine::ATTR_DEFAULT_PARAM_NAMESPACE: case Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES: + case Doctrine::ATTR_MODEL_LOADING: break; case Doctrine::ATTR_SEQCOL_NAME: diff --git a/lib/Doctrine/Data/Import.php b/lib/Doctrine/Data/Import.php index 5afbaa4a8..65693d3f1 100644 --- a/lib/Doctrine/Data/Import.php +++ b/lib/Doctrine/Data/Import.php @@ -130,9 +130,6 @@ class Doctrine_Data_Import extends Doctrine_Data foreach ($row as $key => $value) { if ($obj->getTable()->hasField($key)) { $obj->set($key, $value); - } else if (method_exists($obj, 'set' . Doctrine::classify($key))) { - $func = 'set' . Doctrine::classify($key); - $obj->$func($value); } else if ($obj->getTable()->hasRelation($key)) { if (is_array($value)) { if (isset($value[0])) { @@ -152,6 +149,9 @@ class Doctrine_Data_Import extends Doctrine_Data } else { $obj->set($key, $this->_getImportedObject($value)); } + } else if (method_exists($obj, 'set' . Doctrine::classify($key))) { + $func = 'set' . Doctrine::classify($key); + $obj->$func($value); } } } diff --git a/lib/Doctrine/Import.php b/lib/Doctrine/Import.php index 57229c342..7b12a2d1d 100644 --- a/lib/Doctrine/Import.php +++ b/lib/Doctrine/Import.php @@ -213,12 +213,14 @@ class Doctrine_Import extends Doctrine_Connection_Module $classes = array(); foreach ($connection->import->listTables() as $table) { - $builder->buildRecord(array('tableName' => $table, - 'className' => Doctrine::classify($table)), - $connection->import->listTableColumns($table), - array()); + $definition = array(); + $definition['tableName'] = $table; + $definition['className'] = Doctrine_Inflector::classify($table); + $definition['columns'] = $connection->import->listTableColumns($table); + + $builder->buildRecord($definition); - $classes[] = Doctrine::classify($table); + $classes[] = $definition['className']; } } diff --git a/lib/Doctrine/Import/Builder.php b/lib/Doctrine/Import/Builder.php index adc49c1d6..e26eba0c5 100644 --- a/lib/Doctrine/Import/Builder.php +++ b/lib/Doctrine/Import/Builder.php @@ -78,14 +78,7 @@ class Doctrine_Import_Builder * @var string $suffix */ protected $_generateBaseClasses = true; - - /** - * generateTableClasses - * - * @var string - */ - protected $_generateTableClasses = true; - + /** * baseClassesDirectory * @@ -174,23 +167,6 @@ class Doctrine_Import_Builder return $this->_generateBaseClasses; } - - /** - * generateTableClasses - * - * Specify whether or not to generate table classes which extend from Doctrine_Table - * - * @param string $bool - * @return void - */ - public function generateTableClasses($bool = null) - { - if ($bool !== null) { - $this->_generateTableClasses = $bool; - } - - return $this->_generateTableClasses; - } /** * setBaseClassesDirectory @@ -293,62 +269,176 @@ class Doctrine_Import_Builder END; } - /* - * Build the accessors - * - * @param string $table - * @param array $columns - */ - public function buildAccessors(array $options, array $columns) - { - $ret = ''; - foreach ($columns as $name => $column) { - // getters - $ret .= "\n public function get".Doctrine::classify($name)."(\$load = true)\n"; - $ret .= " {\n"; - $ret .= " return \$this->get('{$name}', \$load);\n"; - $ret .= " }\n"; - - // setters - $ret .= "\n public function set".Doctrine::classify($name)."(\${$name}, \$load = true)\n"; - $ret .= " {\n"; - $ret .= " return \$this->set('{$name}', \${$name}, \$load);\n"; - $ret .= " }\n"; - } - - return $ret; - } - /* * Build the table definition of a Doctrine_Record object * * @param string $table * @param array $tableColumns */ - public function buildTableDefinition(array $options, array $columns, array $relations, array $indexes, array $attributes, array $tableOptions, array $templates, array $actAs) + public function buildTableDefinition(array $definition) { + // If the inheritance type if simple or column aggregation then we do not need a table definition + if (isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'aggregation')) { + return; + } + $ret = array(); $i = 0; - if (isset($options['inheritance']['extends']) && !(isset($options['override_parent']) && $options['override_parent'] == false)) { + if (isset($definition['inheritance']['extends']) && !(isset($definition['override_parent']) && $definition['override_parent'] == false)) { $ret[$i] = " parent::setTableDefinition();"; $i++; } - if (isset($options['tableName']) && !empty($options['tableName'])) { - $ret[$i] = " ".'$this->setTableName(\''. $options['tableName'].'\');'; + if (isset($definition['tableName']) && !empty($definition['tableName'])) { + $ret[$i] = " ".'$this->setTableName(\''. $definition['tableName'].'\');'; $i++; } - foreach ($columns as $name => $column) { - $ret[$i] = " ".'$this->hasColumn(\'' . $name . '\', \'' . $column['type'] . '\''; + if (isset($definition['columns']) && is_array($definition['columns']) && !empty($definition['columns'])) { + $ret[$i] = $this->buildColumns($definition['columns']); + $i++; + } + + if (isset($definition['indexes']) && is_array($definition['indexes']) && !empty($definition['indexes'])) { + $ret[$i] = $this->buildIndexes($definition['indexes']); + $i++; + } + + if (isset($definition['attributes']) && is_array($definition['attributes']) && !empty($definition['attributes'])) { + $ret[$i] = $this->buildAttributes($definition['attributes']); + $i++; + } + + if (isset($definition['options']) && is_array($definition['options']) && !empty($definition['options'])) { + $ret[$i] = $this->buildOptions($definition['options']); + $i++; + } + + if (isset($definition['subclasses']) && is_array($definition['subclasses']) && !empty($definition['subclasses'])) { + $ret[$i] = ' $this->setSubclasses(' . var_export($definition['subclasses'], true) . ');'; + $i++; + } + + $code = implode("\n", $ret); + $code = trim($code); + + if ($code) { + return "\n public function setTableDefinition()"."\n {\n ".$code."\n }"; + } + } + + /** + * buildSetUp + * + * @param array $options + * @param array $columns + * @param array $relations + * @return string + */ + public function buildSetUp(array $definition) + { + $ret = array(); + $i = 0; + + if (isset($definition['inheritance']['extends']) && !(isset($definition['override_parent']) && $definition['override_parent'] == false)) { + $ret[$i] = " parent::setUp();"; + $i++; + } + + if (isset($definition['relations']) && is_array($definition['relations']) && !empty($definition['relations'])) { + foreach ($definition['relations'] as $name => $relation) { + $class = isset($relation['class']) ? $relation['class']:$name; + $alias = (isset($relation['alias']) && $relation['alias'] !== $relation['class']) ? ' as ' . $relation['alias'] : ''; + + if ( ! isset($relation['type'])) { + $relation['type'] = Doctrine_Relation::ONE; + } + + if ($relation['type'] === Doctrine_Relation::ONE || + $relation['type'] === Doctrine_Relation::ONE_COMPOSITE) { + $ret[$i] = " ".'$this->hasOne(\'' . $class . $alias . '\''; + } else { + $ret[$i] = " ".'$this->hasMany(\'' . $class . $alias . '\''; + } + $a = array(); + + if (isset($relation['refClass'])) { + $a[] = '\'refClass\' => ' . var_export($relation['refClass'], true); + } + + if (isset($relation['deferred']) && $relation['deferred']) { + $a[] = '\'default\' => ' . var_export($relation['deferred'], true); + } + + if (isset($relation['local']) && $relation['local']) { + $a[] = '\'local\' => ' . var_export($relation['local'], true); + } + + if (isset($relation['foreign']) && $relation['foreign']) { + $a[] = '\'foreign\' => ' . var_export($relation['foreign'], true); + } + + if (isset($relation['onDelete']) && $relation['onDelete']) { + $a[] = '\'onDelete\' => ' . var_export($relation['onDelete'], true); + } + + if (isset($relation['onUpdate']) && $relation['onUpdate']) { + $a[] = '\'onUpdate\' => ' . var_export($relation['onUpdate'], true); + } + + if (isset($relation['equal']) && $relation['equal']) { + $a[] = '\'equal\' => ' . var_export($relation['equal'], true); + } + + if ( ! empty($a)) { + $ret[$i] .= ', ' . 'array('; + $length = strlen($ret[$i]); + $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')'; + } + + $ret[$i] .= ');'."\n"; + $i++; + } + } + + if (isset($definition['templates']) && is_array($definition['templates']) && !empty($definition['templates'])) { + $ret[$i] = $this->buildTemplates($definition['templates']); + $i++; + } + + if (isset($definition['actAs']) && is_array($definition['actAs']) && !empty($definition['actAs'])) { + $ret[$i] = $this->buildActAs($definition['actAs']); + $i++; + } + + $code = implode("\n", $ret); + $code = trim($code); + + if ($code) { + return "\n public function setUp()\n {\n ".$code."\n }"; + } + } + + /** + * buildColumns + * + * @param string $array + * @return void + */ + public function buildColumns(array $columns) + { + $build = null; + foreach ($columns as $name => $column) { + $build .= " ".'$this->hasColumn(\'' . $name . '\', \'' . $column['type'] . '\''; + if ($column['length']) { - $ret[$i] .= ', ' . $column['length']; + $build .= ', ' . $column['length']; } else { - $ret[$i] .= ', null'; + $build .= ', null'; } $options = $column; @@ -359,37 +449,49 @@ END; } } - $ret[$i] .= ', ' . var_export($options, true); - - $ret[$i] .= ');'; - - if ($i < (count($columns) - 1)) { - $ret[$i] .= PHP_EOL; + if (is_array($options) && !empty($options)) { + $build .= ', ' . var_export($options, true); } - $i++; + + $build .= ");\n"; } - $ret[$i] = $this->buildIndexes($indexes); - $i++; - - $ret[$i] = $this->buildAttributes($attributes); - $i++; - - $ret[$i] = $this->buildTableOptions($tableOptions); - $i++; - - $ret[$i] = $this->buildTemplates($templates); - $i++; - - $ret[$i] = $this->buildActAs($actAs); - $i++; - - $code = implode("\n", $ret); - $code = trim($code); - - if ($code) { - return "\n public function setTableDefinition()"."\n {\n ".$code."\n }"; + return $build; + } + + /* + * Build the accessors + * + * @param string $table + * @param array $columns + */ + public function buildAccessors(array $definition) + { + $accessors = array(); + foreach (array_keys($definition['columns']) as $name) { + $accessors[] = $name; } + + foreach ($definition['relations'] as $relation) { + $accessors[] = $relation['alias']; + } + + $ret = ''; + foreach ($accessors as $name) { + // getters + $ret .= "\n public function get" . Doctrine_Inflector::classify(Doctrine_Inflector::tableize($name)) . "(\$load = true)\n"; + $ret .= " {\n"; + $ret .= " return \$this->get('{$name}', \$load);\n"; + $ret .= " }\n"; + + // setters + $ret .= "\n public function set" . Doctrine_Inflector::classify(Doctrine_Inflector::tableize($name)) . "(\${$name}, \$load = true)\n"; + $ret .= " {\n"; + $ret .= " return \$this->set('{$name}', \${$name}, \$load);\n"; + $ret .= " }\n"; + } + + return $ret; } /** @@ -485,7 +587,7 @@ END; * @param string $array * @return void */ - public function buildTableOptions(array $options) + public function buildOptions(array $options) { $build = ''; foreach ($options as $name => $value) { @@ -514,130 +616,38 @@ END; return $build; } - /** - * buildSetUp - * - * @param array $options - * @param array $columns - * @param array $relations - * @return string - */ - public function buildSetUp(array $options, array $columns, array $relations, array $templates, array $actAs) - { - $ret = array(); - $i = 0; - - if (isset($options['inheritance']['extends']) && !(isset($options['override_parent']) && $options['override_parent'] == false)) { - $ret[$i] = " parent::setUp();"; - $i++; - } - - foreach ($relations as $name => $relation) { - $class = isset($relation['class']) ? $relation['class']:$name; - $alias = (isset($relation['alias']) && $relation['alias'] !== $relation['class']) ? ' as ' . $relation['alias'] : ''; - - if ( ! isset($relation['type'])) { - $relation['type'] = Doctrine_Relation::ONE; - } - - if ($relation['type'] === Doctrine_Relation::ONE || - $relation['type'] === Doctrine_Relation::ONE_COMPOSITE) { - $ret[$i] = " ".'$this->hasOne(\'' . $class . $alias . '\''; - } else { - $ret[$i] = " ".'$this->hasMany(\'' . $class . $alias . '\''; - } - - $a = array(); - - if (isset($relation['refClass'])) { - $a[] = '\'refClass\' => ' . var_export($relation['refClass'], true); - } - - if (isset($relation['deferred']) && $relation['deferred']) { - $a[] = '\'default\' => ' . var_export($relation['deferred'], true); - } - - if (isset($relation['local']) && $relation['local']) { - $a[] = '\'local\' => ' . var_export($relation['local'], true); - } - - if (isset($relation['foreign']) && $relation['foreign']) { - $a[] = '\'foreign\' => ' . var_export($relation['foreign'], true); - } - - if (isset($relation['onDelete']) && $relation['onDelete']) { - $a[] = '\'onDelete\' => ' . var_export($relation['onDelete'], true); - } - - if (isset($relation['onUpdate']) && $relation['onUpdate']) { - $a[] = '\'onUpdate\' => ' . var_export($relation['onUpdate'], true); - } - - if (isset($relation['equal']) && $relation['equal']) { - $a[] = '\'equal\' => ' . var_export($relation['equal'], true); - } - - if ( ! empty($a)) { - $ret[$i] .= ', ' . 'array('; - $length = strlen($ret[$i]); - $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')'; - } - - $ret[$i] .= ');'."\n"; - $i++; - } - - if (isset($options['inheritance']['keyField']) && isset($options['inheritance']['keyValue'])) { - $i++; - $ret[$i] = " ".'$this->setInheritanceMap(array(\''.$options['inheritance']['keyField'].'\' => \''.$options['inheritance']['keyValue'].'\'));'; - } - - $code = implode("\n", $ret); - $code = trim($code); - - if ($code) { - return "\n public function setUp()\n {\n ".$code."\n }"; - } - } - /** * buildDefinition * - * @param array $options - * @param array $columns - * @param array $relations - * @param array $indexes - * @param array $attributes - * @param array $templates - * @param array $actAs + * @param array $definition * @return string */ - public function buildDefinition(array $options, array $columns, array $relations = array(), array $indexes = array(), $attributes = array(), array $templates = array(), array $actAs = array(), array $tableOptions = array()) + public function buildDefinition(array $definition) { - if ( ! isset($options['className'])) { + if ( ! isset($definition['className'])) { throw new Doctrine_Import_Builder_Exception('Missing class name.'); } - $abstract = isset($options['abstract']) && $options['abstract'] === true ? 'abstract ':null; - $className = $options['className']; - $extends = isset($options['inheritance']['extends']) ? $options['inheritance']['extends']:$this->_baseClassName; + $abstract = isset($definition['abstract']) && $definition['abstract'] === true ? 'abstract ':null; + $className = $definition['className']; + $extends = isset($definition['inheritance']['extends']) ? $definition['inheritance']['extends']:$this->_baseClassName; - if ( ! (isset($options['no_definition']) && $options['no_definition'] === true)) { - $definition = $this->buildTableDefinition($options, $columns, $relations, $indexes, $attributes, $tableOptions, $templates, $actAs); - $setUp = $this->buildSetUp($options, $columns, $relations, $templates, $actAs); + if ( ! (isset($definition['no_definition']) && $definition['no_definition'] === true)) { + $tableDefinitionCode = $this->buildTableDefinition($definition); + $setUpCode = $this->buildSetUp($definition); } else { - $definition = null; - $setUp = null; + $tableDefinitionCode = null; + $setUpCode = null; } - $accessors = (isset($options['generate_accessors']) && $options['generate_accessors'] === true) ? $this->buildAccessors($options, $columns):null; + $accessorsCode = (isset($definition['generate_accessors']) && $definition['generate_accessors'] === true) ? $this->buildAccessors($definition):null; $content = sprintf(self::$_tpl, $abstract, $className, $extends, - $definition, - $setUp, - $accessors); + $tableDefinitionCode, + $setUpCode, + $accessorsCode); return $content; } @@ -654,25 +664,25 @@ END; * @param array $actAs * @return void= */ - public function buildRecord(array $options, array $columns, array $relations = array(), array $indexes = array(), array $attributes = array(), array $templates = array(), array $actAs = array(), array $tableOptions = array()) + public function buildRecord(array $definition) { - if ( !isset($options['className'])) { + if ( !isset($definition['className'])) { throw new Doctrine_Import_Builder_Exception('Missing class name.'); } if ($this->generateBaseClasses()) { - $options['is_package'] = (isset($options['package']) && $options['package']) ? true:false; + $definition['is_package'] = (isset($definition['package']) && $definition['package']) ? true:false; - if ($options['is_package']) { - $e = explode('.', $options['package']); - $options['package_name'] = $e[0]; + if ($definition['is_package']) { + $e = explode('.', $definition['package']); + $definition['package_name'] = $e[0]; unset($e[0]); - $options['package_path'] = implode(DIRECTORY_SEPARATOR, $e); + $definition['package_path'] = implode(DIRECTORY_SEPARATOR, $e); } // Top level definition that extends from all the others - $topLevel = $options; + $topLevel = $definition; unset($topLevel['tableName']); // If we have a package then we need to make this extend the package definition and not the base definition @@ -684,9 +694,9 @@ END; unset($topLevel['connection']); // Package level definition that extends from the base definition - if (isset($options['package'])) { + if (isset($definition['package'])) { - $packageLevel = $options; + $packageLevel = $definition; $packageLevel['className'] = $topLevel['inheritance']['extends']; $packageLevel['inheritance']['extends'] = 'Base' . $topLevel['className']; $packageLevel['no_definition'] = true; @@ -697,13 +707,13 @@ END; unset($packageLevel['connection']); } - $baseClass = $options; + $baseClass = $definition; $baseClass['className'] = 'Base' . $baseClass['className']; $baseClass['abstract'] = true; $baseClass['override_parent'] = true; $baseClass['is_base_class'] = true; - $this->writeDefinition($baseClass, $columns, $relations, $indexes, $attributes, $templates, $actAs, $tableOptions); + $this->writeDefinition($baseClass); if (!empty($packageLevel)) { $this->writeDefinition($packageLevel); @@ -711,37 +721,10 @@ END; $this->writeDefinition($topLevel); } else { - $this->writeDefinition($options, $columns, $relations, $indexes, $attributes, $templates, $actAs, $tableOptions); + $this->writeDefinition($definition); } } - - /** - * writeTableDefinition - * - * @return void - */ - public function writeTableDefinition($className, $path, $options = array()) - { - $className = $className . 'Table'; - - $content = '_suffix; - - if (!file_exists($writePath)) { - file_put_contents($writePath, $content); - } - } - + /** * writeDefinition * @@ -754,43 +737,37 @@ END; * @param array $actAs * @return void */ - public function writeDefinition(array $options, array $columns = array(), array $relations = array(), array $indexes = array(), array $attributes = array(), array $templates = array(), array $actAs = array(), array $tableOptions = array()) + public function writeDefinition(array $definition) { - $definition = $this->buildDefinition($options, $columns, $relations, $indexes, $attributes, $templates, $actAs, $tableOptions); + $definitionCode = $this->buildDefinition($definition); - $fileName = $options['className'] . $this->_suffix; + $fileName = $definition['className'] . $this->_suffix; $packagesPath = $this->_packagesPath ? $this->_packagesPath:$this->_path; // If this is a main class that either extends from Base or Package class - if (isset($options['is_main_class']) && $options['is_main_class']) { + if (isset($definition['is_main_class']) && $definition['is_main_class']) { // If is package then we need to put it in a package subfolder - if (isset($options['is_package']) && $options['is_package']) { - $writePath = $this->_path . DIRECTORY_SEPARATOR . $options['package_name']; - - $this->writeTableDefinition($options['className'], $writePath, array('extends' => $options['inheritance']['extends'] . 'Table')); + if (isset($definition['is_package']) && $definition['is_package']) { + $writePath = $this->_path . DIRECTORY_SEPARATOR . $definition['package_name']; // Otherwise lets just put it in the root of the path } else { $writePath = $this->_path; - - $this->writeTableDefinition($options['className'], $writePath); } } // If is the package class then we need to make the path to the complete package - if (isset($options['is_package_class']) && $options['is_package_class']) { - $path = str_replace('.', DIRECTORY_SEPARATOR, trim($options['package'])); + if (isset($definition['is_package_class']) && $definition['is_package_class']) { + $path = str_replace('.', DIRECTORY_SEPARATOR, trim($definition['package'])); $writePath = $packagesPath . DIRECTORY_SEPARATOR . $path; - - $this->writeTableDefinition($options['className'], $writePath); } // If it is the base class of the doctrine record definition - if (isset($options['is_base_class']) && $options['is_base_class']) { + if (isset($definition['is_base_class']) && $definition['is_base_class']) { // If it is a part of a package then we need to put it in a package subfolder - if (isset($options['is_package']) && $options['is_package']) { - $writePath = $this->_path . DIRECTORY_SEPARATOR . $options['package_name'] . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory; + if (isset($definition['is_package']) && $definition['is_package']) { + $writePath = $this->_path . DIRECTORY_SEPARATOR . $definition['package_name'] . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory; // Otherwise lets just put it in the root generated folder } else { $writePath = $this->_path . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory; @@ -809,14 +786,14 @@ END; $code = "bindComponent('" . $options['connectionClassName'] . "', '" . $options['connection'] . "');\n"; + $code .= "Doctrine_Manager::getInstance()->bindComponent('" . $definition['connectionClassName'] . "', '" . $definition['connection'] . "');\n"; } - $code .= PHP_EOL . $definition; + $code .= PHP_EOL . $definitionCode; - if (isset($options['generate_once']) && $options['generate_once'] === true) { + if (isset($definition['generate_once']) && $definition['generate_once'] === true) { if (!file_exists($writePath)) { $bytes = file_put_contents($writePath, $code); } @@ -828,4 +805,4 @@ END; throw new Doctrine_Import_Builder_Exception("Couldn't write file " . $writePath); } } -} +} \ No newline at end of file diff --git a/lib/Doctrine/Import/Schema.php b/lib/Doctrine/Import/Schema.php index 1a622fded..9588451c5 100644 --- a/lib/Doctrine/Import/Schema.php +++ b/lib/Doctrine/Import/Schema.php @@ -43,7 +43,6 @@ class Doctrine_Import_Schema protected $_options = array('packagesPrefix' => 'Package', 'packagesPath' => '', 'generateBaseClasses' => true, - 'generateTableClasses' => true, 'baseClassesDirectory' => 'generated', 'baseClassName' => 'Doctrine_Record', 'suffix' => '.php'); @@ -60,7 +59,17 @@ class Doctrine_Import_Schema return $this->_options[$name]; } } - + + /** + * getOptions + * + * @return void + */ + public function getOptions() + { + return $this->_options; + } + /** * setOption * @@ -119,7 +128,7 @@ class Doctrine_Import_Schema } } - $this->_buildRelationships($array); + $array = $this->_buildRelationships($array); return $array; } @@ -139,162 +148,19 @@ class Doctrine_Import_Schema { $builder = new Doctrine_Import_Builder(); $builder->setTargetPath($directory); - - foreach ($this->_options as $key => $value) { - if ($value) { - $builder->setOption($key, $value); - } - } + $builder->setOptions($this->getOptions()); $array = $this->buildSchema($schema, $format); - foreach ($array as $name => $properties) { - if ( ! empty($models) && !in_array($properties['className'], $models)) { + foreach ($array as $name => $definition) { + if ( ! empty($models) && !in_array($definition['className'], $models)) { continue; } - $options = $this->getOptions($properties); - $columns = $this->getColumns($properties); - $relations = $this->getRelations($properties); - $indexes = $this->getIndexes($properties); - $attributes = $this->getAttributes($properties); - $templates = $this->getTemplates($properties); - $actAs = $this->getActAs($properties); - $tableOptions = $this->getTableOptions($properties); - - $builder->buildRecord($options, $columns, $relations, $indexes, $attributes, $templates, $actAs, $tableOptions); + $builder->buildRecord($definition); } } - /** - * getOptions - * - * @param string $properties Array of table properties - * @param string $directory Directory we are writing the class to - * @return array $options Array of options from a parse schemas properties - */ - public function getOptions($properties) - { - $options = array(); - $options['className'] = $properties['className']; - $options['tableName'] = isset($properties['tableName']) ? $properties['tableName']:null; - $options['connection'] = isset($properties['connection']) ? $properties['connection']:null; - $options['connectionClassName'] = isset($properties['connection']) ? $properties['className']:null; - $options['package'] = $properties['package']; - - if (isset($properties['inheritance'])) { - $options['inheritance'] = $properties['inheritance']; - } - - return $options; - } - - /** - * getColumns - * - * Get array of columns from table properties - * - * @param string $properties Array of table properties - * @return array $columns Array of columns - */ - public function getColumns($properties) - { - return isset($properties['columns']) ? $properties['columns']:array(); - } - - /** - * getRelations - * - * Get array of relations from table properties - * - * @param string $properties Array of tables properties - * @return array $relations Array of relations - */ - public function getRelations($properties) - { - $allRelations = isset($this->_relations[$properties['className']]) ? $this->_relations[$properties['className']]:array(); - - // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations - $existingRelations = array(); - $uniqueRelations = array(); - foreach ($allRelations as $relation) { - if ( ! in_array($relation['key'], $existingRelations)) { - $existingRelations[] = $relation['key']; - $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation)); - } else { - // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it - if (!isset($relation['autogenerated']) || $relation['autogenerated'] != true) { - $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation)); - } - } - } - - return $uniqueRelations; - } - - /** - * getIndexes - * - * Get array of indexes from table properties - * - * @param string $properties Array of table properties - * @return array $index - */ - public function getIndexes($properties) - { - return isset($properties['indexes']) ? $properties['indexes']:array();; - } - - /** - * getAttributes - * - * Get array of attributes from table properties - * - * @param string $properties Array of tables properties - * @return array $attributes - */ - public function getAttributes($properties) - { - return isset($properties['attributes']) ? $properties['attributes']:array(); - } - - /** - * getTemplates - * - * Get array of templates from table properties - * - * @param string $properties Array of table properties - * @return array $templates Array of table templates - */ - public function getTemplates($properties) - { - return isset($properties['templates']) ? $properties['templates']:array(); - } - - /** - * getActAs - * - * Get array of actAs definitions from table properties - * - * @param string $properties Array of table properties - * @return array $actAs Array of actAs definitions from table properties - */ - public function getActAs($properties) - { - return isset($properties['actAs']) ? $properties['actAs']:array(); - } - - /** - * getTableOptions - * - * @param string $properties - * @return void - */ - public function getTableOptions($properties) - { - return isset($properties['options']) ? $properties['options']:array(); - } - /** * parseSchema * @@ -306,33 +172,84 @@ class Doctrine_Import_Schema */ public function parseSchema($schema, $type) { + $defaults = array('className' => null, + 'tableName' => null, + 'connection' => null, + 'relations' => array(), + 'indexes' => array(), + 'attributes' => array(), + 'templates' => array(), + 'actAs' => array(), + 'options' => array(), + 'package' => null, + 'inheritance' => array(), + 'subclasses' => array(), + 'detect_relations' => false, + 'generate_accessors' => false); + $array = Doctrine_Parser::load($schema, $type); - + + // Go through the schema and look for global values so we can assign them to each table/class + $globals = array(); + $globalKeys = array('connection', + 'attributes', + 'templates', + 'actAs', + 'options', + 'package', + 'inheritance', + 'detect_relations', + 'generate_accessors'); + + // Loop over and build up all the global values and remove them from the array + foreach ($array as $key => $value) { + if (in_array($key, $globalKeys)) { + unset($array[$key]); + $globals[$key] = $value; + } + } + + // Apply the globals to each table if it does not have a custom value set already + foreach ($array as $className => $table) { + foreach ($globals as $key => $value) { + if (!isset($array[$className][$key])) { + $array[$className][$key] = $value; + } + } + } + $build = array(); - + foreach ($array as $className => $table) { $columns = array(); - + $className = isset($table['className']) ? (string) $table['className']:(string) $className; - + if (isset($table['tableName']) && $table['tableName']) { $tableName = $table['tableName']; } else { - if (isset($table['inheritance']['extends']) && isset($table['inheritance']['extends']['keyType']) && isset($table['inheritance']['extends']['keyValue'])) { + if (isset($table['inheritance']['extends'])) { $tableName = null; } else { $tableName = Doctrine::tableize($className); } } - + $columns = isset($table['columns']) ? $table['columns']:array(); - $columns = isset($table['fields']) ? $table['fields']:$columns; - + if ( ! empty($columns)) { foreach ($columns as $columnName => $field) { + // Support short syntax: my_column: integer(4) + if (!is_array($field)) { + $original = $field; + $field = array(); + $field['type'] = $original; + } + $colDesc = array(); $colDesc['name'] = $columnName; - + + // Support short type(length) syntax: my_column: { type: integer(4) } $e = explode('(', $field['type']); if (isset($e[0]) && isset($e[1])) { $colDesc['type'] = $e[0]; @@ -342,45 +259,47 @@ class Doctrine_Import_Schema $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null; $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length']; } - - $colDesc['ptype'] = isset($field['ptype']) ? (string) $field['ptype']:(string) $colDesc['type']; + $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null; $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null; $colDesc['default'] = isset($field['default']) ? $field['default']:null; $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null; - $colDesc['autoincrement'] = isset($field['autoinc']) ? (bool) (isset($field['autoinc']) && $field['autoinc']):$colDesc['autoincrement']; $colDesc['sequence'] = isset($field['sequence']) ? (string) $field['sequence']:null; $colDesc['values'] = isset($field['values']) ? (array) $field['values']:null; - + + // Include all the specified and valid validators in the colDesc $validators = Doctrine_Lib::getValidators(); - + foreach ($validators as $validator) { if (isset($field[$validator])) { $colDesc[$validator] = $field[$validator]; } } - + $columns[(string) $colDesc['name']] = $colDesc; } } - - $build[$className]['connection'] = isset($table['connection']) ? $table['connection']:null; + + // Apply the default values + foreach ($defaults as $key => $defaultValue) { + if (isset($table[$key]) && !isset($build[$className][$key])) { + $build[$className][$key] = $table[$key]; + } else { + $build[$className][$key] = isset($build[$className][$key]) ? $build[$className][$key]:$defaultValue; + } + } + $build[$className]['className'] = $className; $build[$className]['tableName'] = $tableName; $build[$className]['columns'] = $columns; - $build[$className]['relations'] = isset($table['relations']) ? $table['relations']:array(); - $build[$className]['indexes'] = isset($table['indexes']) ? $table['indexes']:array(); - $build[$className]['attributes'] = isset($table['attributes']) ? $table['attributes']:array(); - $build[$className]['templates'] = isset($table['templates']) ? $table['templates']:array(); - $build[$className]['actAs'] = isset($table['actAs']) ? $table['actAs']:array(); - $build[$className]['options'] = isset($table['options']) ? $table['options']:array(); - $build[$className]['package'] = isset($table['package']) ? $table['package']:null; + + // Make sure that anything else that is specified in the schema makes it to the final array + $build[$className] = Doctrine_Lib::arrayDeepMerge($table, $build[$className]); - if (isset($table['inheritance'])) { - $build[$className]['inheritance'] = $table['inheritance']; - } + // We need to keep track of the className for the connection + $build[$className]['connectionClassName'] = $build[$className]['className']; } - + return $build; } @@ -395,6 +314,19 @@ class Doctrine_Import_Schema */ protected function _buildRelationships($array) { + // Handle auto detecting relations by the names of columns + // User.contact_id will automatically create User hasOne Contact local => contact_id, foreign => id + foreach ($array as $className => $properties) { + if (isset($properties['columns']) && !empty($properties['columns']) && isset($properties['detect_relations']) && $properties['detect_relations']) { + foreach ($properties['columns'] as $column) { + $columnClassName = Doctrine_Inflector::classify(str_replace('_id', '', $column['name'])); + if (isset($array[$columnClassName]) && !isset($array[$className]['relations'][$columnClassName])) { + $array[$className]['relations'][$columnClassName] = array(); + } + } + } + } + foreach ($array as $name => $properties) { if ( ! isset($properties['relations'])) { continue; @@ -438,18 +370,28 @@ class Doctrine_Import_Schema } } - // Now we fix all the relationships and auto-complete opposite ends of relationships - $this->_fixRelationships(); + // Now we auto-complete opposite ends of relationships + $this->_autoCompleteOppositeRelations(); + + // Make sure we do not have any duplicate relations + $this->_fixDuplicateRelations(); + + foreach ($this->_relations as $className => $relations) { + $array[$className]['relations'] = $relations; + } + + return $array; } /** * fixRelationships * * Loop through all relationships building the opposite ends of each relationship + * and make sure no duplicate relations exist * * @return void */ - protected function _fixRelationships() + protected function _autoCompleteOppositeRelations() { foreach($this->_relations as $className => $relations) { foreach ($relations AS $alias => $relation) { @@ -474,13 +416,35 @@ class Doctrine_Import_Schema } } - if (!isset($this->_relations[$relation['class']][$newRelation['alias']])) { + if ( ! isset($this->_relations[$relation['class']][$newRelation['alias']])) { $newRelation['key'] = $this->_buildUniqueRelationKey($newRelation); $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation; } } } } + + protected function _fixDuplicateRelations() + { + foreach($this->_relations as $className => $relations) { + // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations + $existingRelations = array(); + $uniqueRelations = array(); + foreach ($relations as $relation) { + if ( ! in_array($relation['key'], $existingRelations)) { + $existingRelations[] = $relation['key']; + $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation)); + } else { + // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it + if (!isset($relation['autogenerated']) || $relation['autogenerated'] != true) { + $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation)); + } + } + } + + $this->_relations[$className] = $uniqueRelations; + } + } /** * _buildUniqueRelationKey diff --git a/package.xml b/package.xml index 14a8e6bf5..01c05f98a 100644 --- a/package.xml +++ b/package.xml @@ -10,7 +10,7 @@ kvesteri@cc.hut.fi yes - 2007-11-30 + 2007-12-08 1.0.0 1.0.0 @@ -21,7 +21,7 @@ MIT license - - + diff --git a/tests/Import/SchemaTestCase.php b/tests/Import/SchemaTestCase.php index 4c7abcc47..0ce8ff288 100644 --- a/tests/Import/SchemaTestCase.php +++ b/tests/Import/SchemaTestCase.php @@ -75,10 +75,8 @@ class Doctrine_Import_Schema_TestCase extends Doctrine_UnitTestCase $this->schema = $this->buildSchema->buildSchema('schema.yml', 'yml'); foreach ($this->schema as $name => $properties) { - $relations = $this->buildSchema->getRelations($properties); - - foreach ($relations as $alias => $relation) { - if (!$this->_verifyMultiDirectionalRelationship($name, $alias, $relation)) { + foreach ($properties['relations'] as $alias => $relation) { + if ( ! $this->_verifyMultiDirectionalRelationship($name, $alias, $relation)) { $this->fail(); return false; @@ -94,7 +92,7 @@ class Doctrine_Import_Schema_TestCase extends Doctrine_UnitTestCase $foreignClass = $relation['class']; $foreignAlias = isset($relation['foreignAlias']) ? $relation['foreignAlias']:$class; - $foreignClassRelations = $this->buildSchema->getRelations($this->schema[$foreignClass]); + $foreignClassRelations = $this->schema[$foreignClass]['relations']; // Check to see if the foreign class has the opposite end defined for the class/foreignAlias if (isset($foreignClassRelations[$foreignAlias])) { diff --git a/tools/sandbox/config.php.dist b/tools/sandbox/config.php.dist index 9be27f73d..a30fcbd2e 100644 --- a/tools/sandbox/config.php.dist +++ b/tools/sandbox/config.php.dist @@ -50,3 +50,5 @@ spl_autoload_register(array('Doctrine', 'autoload')); $pdo = new PDO(DSN); Doctrine_Manager::connection($pdo, 'sandbox'); + +Doctrine_Manager::getInstance()->setAttribute(Doctrine::ATTR_MODEL_LOADING, Doctrine::MODEL_LOADING_CONSERVATIVE); \ No newline at end of file diff --git a/tools/sandbox/cli b/tools/sandbox/doctrine similarity index 68% rename from tools/sandbox/cli rename to tools/sandbox/doctrine index a8bd6e2c9..8edb14848 100755 --- a/tools/sandbox/cli +++ b/tools/sandbox/doctrine @@ -1,4 +1,4 @@ #!/usr/bin/env php