diff --git a/lib/Doctrine/Import/Builder.php b/lib/Doctrine/Import/Builder.php index a9707d147..0bd75c817 100644 --- a/lib/Doctrine/Import/Builder.php +++ b/lib/Doctrine/Import/Builder.php @@ -1,367 +1,421 @@ -. - */ - -/** - * Doctrine_Import_Builder - * Import builder is responsible of building Doctrine ActiveRecord classes - * based on a database schema. - * - * @package Doctrine - * @subpackage Import - * @link www.phpdoctrine.com - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @since 1.0 - * @version $Revision$ - * @author Konsta Vesterinen - * @author Jukka Hassinen - * @author Nicolas Bérard-Nault - */ -class Doctrine_Import_Builder -{ - /** - * @var string $path the path where imported files are being generated - */ - private $path = ''; - - private $suffix = '.class.php'; - - private $generateBaseClasses = false; - - private $baseClassesDirectory = 'generated'; - - private static $tpl; - - public function __construct() - { - $this->loadTemplate(); - } - - /** - * setTargetPath - * - * @param string path the path where imported files are being generated - * @return - */ - public function setTargetPath($path) - { - if ( ! file_exists($path)) { - mkdir($path, 0777); - } - - $this->path = $path; - } - - /** - * generateBaseClasses - * - * Specify whether or not to generate classes which extend from generated base classes - * - * @param string $bool - * @return void - * @author Jonathan H. Wage - */ - public function generateBaseClasses($bool = null) - { - if ($bool !== null) { - $this->generateBaseClasses = $bool; - } - - return $this->generateBaseClasses; - } - - /** - * getTargetPath - * - * @return string the path where imported files are being generated - */ - public function getTargetPath() - { - return $this->path; - } - - /** - * This is a template that was previously in Builder/Record.tpl. Due to the fact - * that it was not bundled when compiling, it had to be moved here. - * - * @return void - */ - public function loadTemplate() - { - if (isset(self::$tpl)) { - return; - } - - self::$tpl =<<setTableName(\''. $options['tableName'].'\');'; - - $i++; - } - - foreach ($columns as $name => $column) { - $ret[$i] = ' $this->hasColumn(\'' . $name . '\', \'' . $column['type'] . '\''; - - if ($column['length']) { - $ret[$i] .= ', ' . $column['length']; - } else { - $ret[$i] .= ', null'; - } - - $a = array(); - - if (isset($column['default']) && $column['default']) { - $a[] = '\'default\' => ' . var_export($column['default'], true); - } - if (isset($column['notnull']) && $column['notnull']) { - $a[] = '\'notnull\' => true'; - } - if (isset($column['primary']) && $column['primary']) { - $a[] = '\'primary\' => true'; - } - if ((isset($column['autoinc']) && $column['autoinc']) || isset($column['autoincrement']) && $column['autoincrement']) { - $a[] = '\'autoincrement\' => true'; - } - if (isset($column['unique']) && $column['unique']) { - $a[] = '\'unique\' => true'; - } - if (isset($column['unsigned']) && $column['unsigned']) { - $a[] = '\'unsigned\' => true'; - } - if ($column['type'] == 'enum' && isset($column['values']) ) { - $a[] = '\'values\' => array(\'' . implode('\',\'', $column['values']) . '\')'; - } - - if ( ! empty($a)) { - $ret[$i] .= ', ' . 'array('; - $length = strlen($ret[$i]); - $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')'; - } - - $ret[$i] .= ');'; - - if ($i < (count($columns) - 1)) { - $ret[$i] .= PHP_EOL; - } - $i++; - } - - if (!empty($ret)) { - return "\n\tpublic function setTableDefinition()"."\n\t{\n".implode("\n", $ret)."\n\t}"; - } - } - public function buildSetUp(array $options, array $columns, array $relations) - { - $ret = array(); - - $i = 0; - - if (isset($options['inheritance']['extends']) && !isset($options['override_parent'])) { - $ret[$i] = "\t\t\t\tparent::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 ( ! empty($a)) { - $ret[$i] .= ', ' . 'array('; - $length = strlen($ret[$i]); - $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')'; - } - - $ret[$i] .= ');'; - $i++; - } - - if (isset($options['inheritance']['keyField']) && isset($options['inheritance']['keyValue'])) { - $i++; - $ret[$i] = "\t\t".'$this->setInheritanceMap(array(\''.$options['inheritance']['keyField'].'\' => '.$options['inheritance']['keyValue'].'));'; - } - - if (!empty($ret)) { - return "\n\tpublic function setUp()\n\t{\n".implode("\n", $ret)."\n\t}"; - } - } - - public function buildDefinition(array $options, array $columns, array $relations = array()) - { - if ( ! isset($options['className'])) { - throw new Doctrine_Import_Builder_Exception('Missing class name.'); - } - - $abstract = isset($options['abstract']) ? 'abstract ':null; - $className = $options['className']; - $extends = isset($options['inheritance']['extends']) ? $options['inheritance']['extends']:'Doctrine_Record'; - $definition = !isset($options['no_definition']) ? $this->buildTableDefinition($options, $columns, $relations):null; - $setUp = !isset($options['no_definition']) ? $this->buildSetUp($options, $columns, $relations):null; - - $content = sprintf(self::$tpl, $abstract, - $className, - $extends, - $definition, - $setUp); - - return $content; - } - - public function buildRecord(array $options, array $columns, array $relations = array()) - { - if ( !isset($options['className'])) { - throw new Doctrine_Import_Builder_Exception('Missing class name.'); - } - - if ( !isset($options['fileName'])) { - if (empty($this->path)) { - throw new Doctrine_Import_Builder_Exception('No build target directory set.'); - } - - - if (is_writable($this->path) === false) { - throw new Doctrine_Import_Builder_Exception('Build target directory ' . $this->path . ' is not writable.'); - } - - $options['fileName'] = $this->path . DIRECTORY_SEPARATOR . $options['className'] . $this->suffix; - } - - if ($this->generateBaseClasses()) { - - // We only want to generate this one if it doesn't already exist - if (!file_exists($options['fileName'])) { - $optionsBak = $options; - - unset($options['tableName']); - $options['inheritance']['extends'] = 'Base' . $options['className']; - $options['requires'] = array($this->baseClassesDirectory . DIRECTORY_SEPARATOR . $options['inheritance']['extends'] . $this->suffix); - $options['no_definition'] = true; - - $this->writeDefinition($options, array(), array()); - - $options = $optionsBak; - } - - $generatedPath = $this->path . DIRECTORY_SEPARATOR . $this->baseClassesDirectory; - - if (!file_exists($generatedPath)) { - mkdir($generatedPath); - } - - $options['className'] = 'Base' . $options['className']; - $options['abstract'] = true; - $options['fileName'] = $generatedPath . DIRECTORY_SEPARATOR . $options['className'] . $this->suffix; - - $this->writeDefinition($options, $columns, $relations); - } else { - $this->writeDefinition($options, $columns, $relations); - } - } - - public function writeDefinition(array $options, array $columns, array $relations = array()) - { - $content = $this->buildDefinition($options, $columns, $relations); - - $code = ". + */ + +/** + * Doctrine_Import_Builder + * Import builder is responsible of building Doctrine ActiveRecord classes + * based on a database schema. + * + * @package Doctrine + * @subpackage Import + * @link www.phpdoctrine.com + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @since 1.0 + * @version $Revision$ + * @author Konsta Vesterinen + * @author Jukka Hassinen + * @author Nicolas Bérard-Nault + */ +class Doctrine_Import_Builder +{ + /** + * @var string $path the path where imported files are being generated + */ + private $path = ''; + + private $suffix = '.class.php'; + + private $generateBaseClasses = false; + + private $baseClassesDirectory = 'generated'; + + private static $tpl; + + public function __construct() + { + $this->loadTemplate(); + } + + /** + * setTargetPath + * + * @param string path the path where imported files are being generated + * @return + */ + public function setTargetPath($path) + { + if ( ! file_exists($path)) { + mkdir($path, 0777); + } + + $this->path = $path; + } + + /** + * generateBaseClasses + * + * Specify whether or not to generate classes which extend from generated base classes + * + * @param string $bool + * @return void + * @author Jonathan H. Wage + */ + public function generateBaseClasses($bool = null) + { + if ($bool !== null) { + $this->generateBaseClasses = $bool; + } + + return $this->generateBaseClasses; + } + + /** + * getTargetPath + * + * @return string the path where imported files are being generated + */ + public function getTargetPath() + { + return $this->path; + } + + /** + * This is a template that was previously in Builder/Record.tpl. Due to the fact + * that it was not bundled when compiling, it had to be moved here. + * + * @return void + */ + public function loadTemplate() + { + if (isset(self::$tpl)) { + return; + } + + self::$tpl =<<setTableName(\''. $options['tableName'].'\');'; + + $i++; + } + + foreach ($columns as $name => $column) { + $ret[$i] = ' $this->hasColumn(\'' . $name . '\', \'' . $column['type'] . '\''; + + if ($column['length']) { + $ret[$i] .= ', ' . $column['length']; + } else { + $ret[$i] .= ', null'; + } + + $a = array(); + + if (isset($column['default']) && $column['default']) { + $a[] = '\'default\' => ' . var_export($column['default'], true); + } + if (isset($column['notnull']) && $column['notnull']) { + $a[] = '\'notnull\' => true'; + } + if (isset($column['primary']) && $column['primary']) { + $a[] = '\'primary\' => true'; + } + if ((isset($column['autoinc']) && $column['autoinc']) || isset($column['autoincrement']) && $column['autoincrement']) { + $a[] = '\'autoincrement\' => true'; + } + if (isset($column['unique']) && $column['unique']) { + $a[] = '\'unique\' => true'; + } + if (isset($column['unsigned']) && $column['unsigned']) { + $a[] = '\'unsigned\' => true'; + } + if ($column['type'] == 'enum' && isset($column['values']) ) { + $a[] = '\'values\' => array(\'' . implode('\',\'', $column['values']) . '\')'; + } + + if ( ! empty($a)) { + $ret[$i] .= ', ' . 'array('; + $length = strlen($ret[$i]); + $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')'; + } + + $ret[$i] .= ');'; + + if ($i < (count($columns) - 1)) { + $ret[$i] .= PHP_EOL; + } + $i++; + } + + foreach ($indexes as $indexName => $definitions) { + $ret[$i] = "\n".' $this->index(\'' . $indexName . '\', array('; + + foreach ($definitions as $name => $value) { + + // parse fields + if ($name === 'fields') { + $ret[$i] .= '\'fields\' => array('; + + foreach ($value as $fieldName => $fieldValue) { + $ret[$i] .= '\'' . $fieldName . '\' => array( '; + + // parse options { sorting, length, primary } + if (isset($fieldValue) && $fieldValue) { + foreach ($fieldValue as $optionName => $optionValue) { + + $ret[$i] .= '\'' . $optionName . '\' => '; + + // check primary option, mark either as true or false + if ($optionName === 'primary') { + $ret[$i] .= (($optionValue == 'true') ? 'true' : 'false') . ', '; + continue; + } + + // convert sorting option to uppercase, for instance, asc -> ASC + if ($optionName === 'sorting') { + $ret[$i] .= '\'' . strtoupper($optionValue) . '\', '; + continue; + } + + // check the rest of the options + $ret[$i] .= '\'' . $optionValue . '\', '; + } + } + + $ret[$i] .= '), '; + } + } + + // parse index type option, 4 choices { unique, fulltext, gist, gin } + if ($name === 'type') { + $ret[$i] .= '), \'type\' => \'' . $value . '\''; + } + + // add extra ) if type definition is not declared + if (!isset($definitions['type'])) { + $ret[$i] .= ')'; + } + } + + $ret[$i] .= '));'; + $i++; + } + + if (!empty($ret)) { + return "\n\tpublic function setTableDefinition()"."\n\t{\n".implode("\n", $ret)."\n\t}"; + } + } + public function buildSetUp(array $options, array $columns, array $relations) + { + $ret = array(); + + $i = 0; + + if (isset($options['inheritance']['extends']) && !isset($options['override_parent'])) { + $ret[$i] = "\t\t\t\tparent::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 ( ! empty($a)) { + $ret[$i] .= ', ' . 'array('; + $length = strlen($ret[$i]); + $ret[$i] .= implode(',' . PHP_EOL . str_repeat(' ', $length), $a) . ')'; + } + + $ret[$i] .= ');'; + $i++; + } + + if (isset($options['inheritance']['keyField']) && isset($options['inheritance']['keyValue'])) { + $i++; + $ret[$i] = "\t\t".'$this->setInheritanceMap(array(\''.$options['inheritance']['keyField'].'\' => '.$options['inheritance']['keyValue'].'));'; + } + + if (!empty($ret)) { + return "\n\tpublic function setUp()\n\t{\n".implode("\n", $ret)."\n\t}"; + } + } + + public function buildDefinition(array $options, array $columns, array $relations = array(), array $indexes = array()) + { + if ( ! isset($options['className'])) { + throw new Doctrine_Import_Builder_Exception('Missing class name.'); + } + + $abstract = isset($options['abstract']) ? 'abstract ':null; + $className = $options['className']; + $extends = isset($options['inheritance']['extends']) ? $options['inheritance']['extends']:'Doctrine_Record'; + $definition = !isset($options['no_definition']) ? $this->buildTableDefinition($options, $columns, $relations, $indexes):null; + $setUp = !isset($options['no_definition']) ? $this->buildSetUp($options, $columns, $relations):null; + + $content = sprintf(self::$tpl, $abstract, + $className, + $extends, + $definition, + $setUp); + + return $content; + } + + public function buildRecord(array $options, array $columns, array $relations = array(), array $indexes = array()) + { + if ( !isset($options['className'])) { + throw new Doctrine_Import_Builder_Exception('Missing class name.'); + } + + if ( !isset($options['fileName'])) { + if (empty($this->path)) { + throw new Doctrine_Import_Builder_Exception('No build target directory set.'); + } + + + if (is_writable($this->path) === false) { + throw new Doctrine_Import_Builder_Exception('Build target directory ' . $this->path . ' is not writable.'); + } + + $options['fileName'] = $this->path . DIRECTORY_SEPARATOR . $options['className'] . $this->suffix; + } + + if ($this->generateBaseClasses()) { + + // We only want to generate this one if it doesn't already exist + if (!file_exists($options['fileName'])) { + $optionsBak = $options; + + unset($options['tableName']); + $options['inheritance']['extends'] = 'Base' . $options['className']; + $options['requires'] = array($this->baseClassesDirectory . DIRECTORY_SEPARATOR . $options['inheritance']['extends'] . $this->suffix); + $options['no_definition'] = true; + + $this->writeDefinition($options, array(), array()); + + $options = $optionsBak; + } + + $generatedPath = $this->path . DIRECTORY_SEPARATOR . $this->baseClassesDirectory; + + if (!file_exists($generatedPath)) { + mkdir($generatedPath); + } + + $options['className'] = 'Base' . $options['className']; + $options['abstract'] = true; + $options['fileName'] = $generatedPath . DIRECTORY_SEPARATOR . $options['className'] . $this->suffix; + + $this->writeDefinition($options, $columns, $relations, $indexes); + } else { + $this->writeDefinition($options, $columns, $relations, $indexes); + } + } + + public function writeDefinition(array $options, array $columns, array $relations = array(), array $indexes = array()) + { + $content = $this->buildDefinition($options, $columns, $relations, $indexes); + + $code = "buildRelationships($array); - return array('schema' => $array, 'relations' => $this->relations); + return array('schema' => $array, 'relations' => $this->relations, 'indexes' => $this->indexes); } /** * importSchema @@ -69,7 +70,7 @@ class Doctrine_Import_Schema * @param string $schema The file containing the XML schema * @param string $directory The directory where the Doctrine_Record class will be written * @param array $models Optional array of models to import - * + * * @access public */ public function importSchema($schema, $format = 'yml', $directory = null, $models = array()) @@ -89,7 +90,7 @@ class Doctrine_Import_Schema $options = $this->getOptions($properties, $directory); $columns = $this->getColumns($properties); - $relations = $this->getRelations($properties); + $relations = $this->getRelations($properties); $builder->buildRecord($options, $columns, $relations); } @@ -118,11 +119,16 @@ class Doctrine_Import_Schema { return isset($this->relations[$properties['className']]) ? $this->relations[$properties['className']]:array(); } + + public function getIndexes($properties) + { + return isset($properties['indexes']) ? $properties['indexes']:array();; + } /** * parseSchema * - * A method to parse a Yml Schema and translate it into a property array. + * A method to parse a Yml Schema and translate it into a property array. * The function returns that property array. * * @param string $schema Path to the file containing the XML schema @@ -164,6 +170,7 @@ class Doctrine_Import_Schema $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(); } if (isset($table['inheritance'])) { @@ -181,14 +188,14 @@ class Doctrine_Import_Schema continue; } - $className = $properties['className']; + $className = $properties['className']; $relations = $properties['relations']; foreach ($relations as $alias => $relation) { $class = isset($relation['class']) ? $relation['class']:$alias; - $relation['foreign'] = isset($relation['foreign'])?$relation['foreign']:'id'; + $relation['foreign'] = isset($relation['foreign'])?$relation['foreign']:'id'; $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias; $relation['class'] = $class; @@ -236,7 +243,7 @@ class Doctrine_Import_Schema if(isset($relation['foreignType'])) { $newRelation['type'] = $relation['foreignType']; } else { - $newRelation['type'] = $relation['type'] === Doctrine_Relation::ONE ? Doctrine_Relation::MANY:Doctrine_Relation::ONE; + $newRelation['type'] = $relation['type'] === Doctrine_Relation::ONE ? Doctrine_Relation::MANY:Doctrine_Relation::ONE; } if( isset($this->relations[$relation['class']]) && is_array($this->relations[$relation['class']]) ) {