Coverage for Doctrine_Import_Schema

Back to coverage report

1 <?php
2 /*
3  * $Id: Schema.php 1838 2007-06-26 00:58:21Z nicobn $
4  *
5  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16  *
17  * This software consists of voluntary contributions made by many individuals
18  * and is licensed under the LGPL. For more information, see
19  * <http://www.phpdoctrine.com>.
20  */
21
22 /**
23  * class Doctrine_Import_Schema
24  *
25  * Different methods to import a XML schema. The logic behind using two different
26  * methods is simple. Some people will like the idea of producing Doctrine_Record
27  * objects directly, which is totally fine. But in fast and growing application,
28  * table definitions tend to be a little bit more volatile. importArr() can be used
29  * to output a table definition in a PHP file. This file can then be stored
30  * independantly from the object itself.
31  *
32  * @package     Doctrine
33  * @subpackage  Import
34  * @link        www.phpdoctrine.com
35  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
36  * @version     $Revision: 1838 $
37  * @author      Nicolas BĂ©rard-Nault <nicobn@gmail.com>
38  * @author      Jonathan H. Wage <jonwage@gmail.com>
39  */
40 class Doctrine_Import_Schema
41 {
42     protected $_relations = array();
43     protected $_options = array('packagesPrefix'        =>  'Package',
44                                 'packagesPath'          =>  '',
45                                 'generateBaseClasses'   =>  true,
46                                 'generateTableClasses'  =>  true,
47                                 'baseClassesDirectory'  =>  'generated',
48                                 'baseClassName'         =>  'Doctrine_Record',
49                                 'suffix'                =>  '.php');
50     
51     /**
52      * getOption
53      *
54      * @param string $name 
55      * @return void
56      */
57     public function getOption($name)
58     {
59         if (isset($this->_options[$name]))   {
60             return $this->_options[$name];
61         }
62     }
63     
64     /**
65      * setOption
66      *
67      * @param string $name 
68      * @param string $value 
69      * @return void
70      */
71     public function setOption($name, $value)
72     {
73         if (isset($this->_options[$name])) {
74             $this->_options[$name] = $value;
75         }
76     }
77     
78     /**
79      * setOptions
80      *
81      * @param string $options 
82      * @return void
83      */
84     public function setOptions($options)
85     {
86         if (!empty($options)) {
87           $this->_options = $options;
88         }
89     }
90
91     /**
92      * buildSchema
93      *
94      * Loop throug directories of schema files and part them all in to one complete array of schema information
95      *
96      * @param  string   $schema Array of schema files or single schema file. Array of directories with schema files or single directory
97      * @param  string   $format Format of the files we are parsing and building from
98      * @return array    $array
99      */
100     public function buildSchema($schema, $format)
101     {
102         $array = array();
103
104         foreach ((array) $schema AS $s) {
105             if (is_file($s)) {
106                 $array = array_merge($this->parseSchema($s, $format), $array);
107             } else if (is_dir($s)) {
108                 $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($s),
109                                                       RecursiveIteratorIterator::LEAVES_ONLY);
110
111                 foreach ($it as $file) {
112                     $e = explode('.', $file->getFileName());
113                     if (end($e) === $format) {
114                         $array = array_merge($this->parseSchema($file->getPathName(), $format), $array);
115                     }
116                 }
117             }
118         }
119
120         $this->buildRelationships($array);
121
122         return array('schema' => $array, 'relations' => $this->_relations);
123     }
124
125     /**
126      * importSchema
127      *
128      * A method to import a Schema and translate it into a Doctrine_Record object
129      *
130      * @param  string $schema       The file containing the XML schema
131      * @param  string $directory    The directory where the Doctrine_Record class will be written
132      * @param  array $models        Optional array of models to import
133      *
134      * @return void
135      */
136     public function importSchema($schema, $format = 'yml', $directory = null, $models = array())
137     {
138         $builder = new Doctrine_Import_Builder();
139         $builder->setTargetPath($directory);
140         $builder->generateBaseClasses($this->getOption('generateBaseClasses'));
141         $builder->generateTableClasses($this->getOption('generateTableClasses'));
142         $builder->setBaseClassesDirectory($this->getOption('baseClassesDirectory'));
143         $builder->setBaseClassName($this->getOption('baseClassName'));
144         $builder->setPackagesPath($this->getOption('packagesPath'));
145         $builder->setPackagesPrefix($this->getOption('packagesPrefix'));
146         $builder->setSuffix($this->getOption('suffix'));
147         
148         $schema = $this->buildSchema($schema, $format);
149         
150         $array = $schema['schema'];
151         
152         foreach ($array as $name => $properties) {
153             if ( ! empty($models) && !in_array($properties['className'], $models)) {
154                 continue;
155             }
156             
157             $options = $this->getOptions($properties);
158             $columns = $this->getColumns($properties);
159             $relations = $this->getRelations($properties);
160             $indexes = $this->getIndexes($properties);
161             $attributes = $this->getAttributes($properties);
162             $templates = $this->getTemplates($properties);
163             $actAs = $this->getActAs($properties);
164             
165             $builder->buildRecord($options, $columns, $relations, $indexes, $attributes, $templates, $actAs);
166         }
167     }
168
169     /**
170      * getOptions
171      *
172      * @param string $properties Array of table properties
173      * @param string $directory  Directory we are writing the class to
174      * @return array $options    Array of options from a parse schemas properties
175      */
176     public function getOptions($properties)
177     {
178         $options = array();
179         $options['className'] = $properties['className'];
180         $options['tableName'] = isset($properties['tableName']) ? $properties['tableName']:null;
181         $options['connection'] = isset($properties['connection']) ? $properties['connection']:null;
182         $options['connectionClassName'] = isset($properties['connection']) ? $properties['className']:null;
183         $options['package'] = $properties['package'];
184         
185         if (isset($properties['inheritance'])) {
186             $options['inheritance'] = $properties['inheritance'];
187         }
188
189         return $options;
190     }
191
192     /**
193      * getColumns
194      *
195      * Get array of columns from table properties
196      *
197      * @param  string $properties Array of table properties
198      * @return array  $columns    Array of columns
199      */
200     public function getColumns($properties)
201     {
202         return isset($properties['columns']) ? $properties['columns']:array();
203     }
204
205     /**
206      * getRelations
207      * 
208      * Get array of relations from table properties
209      *
210      * @param  string $properties Array of tables properties
211      * @return array  $relations  Array of relations
212      */
213     public function getRelations($properties)
214     {
215         $all_relations = isset($this->_relations[$properties['className']]) ? $this->_relations[$properties['className']]:array();
216         
217         // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations
218         $exist_relations = array();
219         $unique_relations = array();
220         foreach ($all_relations as $relation) {
221             if (!in_array($relation['class'], $exist_relations)) {
222                 $exist_relations[] = $relation['class'];
223                 $unique_relations = array_merge($unique_relations, array($relation['alias'] => $relation));
224             } else {
225                 // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it
226                 if (!isset($relation['autogenerated']) || $relation['autogenerated'] != true) {
227                     $unique_relations = array_merge($unique_relations, array($relation['alias'] => $relation));
228                 }
229             }
230         }
231         
232         return $unique_relations;
233     }
234
235     /**
236      * getIndexes
237      *
238      * Get array of indexes from table properties
239      *
240      * @param  string $properties Array of table properties
241      * @return array  $index
242      */
243     public function getIndexes($properties)
244     {
245         return isset($properties['indexes']) ? $properties['indexes']:array();;
246     }
247
248     /**
249      * getAttributes
250      *
251      * Get array of attributes from table properties
252      *
253      * @param  string $properties Array of tables properties 
254      * @return array  $attributes
255      */
256     public function getAttributes($properties)
257     {
258         return isset($properties['attributes']) ? $properties['attributes']:array();
259     }
260
261     /**
262      * getTemplates
263      *
264      * Get array of templates from table properties
265      *
266      * @param  string $properties Array of table properties
267      * @return array  $templates  Array of table templates
268      */
269     public function getTemplates($properties)
270     {
271         return isset($properties['templates']) ? $properties['templates']:array();
272     }
273
274     /**
275      * getActAs
276      *
277      * Get array of actAs definitions from table properties
278      *
279      * @param  string $properties Array of table properties
280      * @return array  $actAs      Array of actAs definitions from table properties
281      */
282     public function getActAs($properties)
283     {
284         return isset($properties['actAs']) ? $properties['actAs']:array();
285     }
286
287     /**
288      * parseSchema
289      *
290      * A method to parse a Schema and translate it into a property array.
291      * The function returns that property array.
292      *
293      * @param  string $schema   Path to the file containing the schema
294      * @return array  $build    Built array of schema information
295      */
296     public function parseSchema($schema, $type)
297     {
298         $array = Doctrine_Parser::load($schema, $type);
299         
300         $build = array();
301         
302         foreach ($array as $className => $table) {
303             $columns = array();
304             
305             $className = isset($table['className']) ? (string) $table['className']:(string) $className;
306             
307             if (isset($table['tableName']) && $table['tableName']) {
308                 $tableName = $table['tableName'];
309             } else {
310                 if (isset($table['inheritance']['extends']) && isset($table['inheritance']['extends']['keyType']) && isset($table['inheritance']['extends']['keyValue'])) {
311                     $tableName = null;
312                 } else {
313                     $tableName = Doctrine::tableize($className);
314                 }
315             }
316             
317             $columns = isset($table['columns']) ? $table['columns']:array();
318             $columns = isset($table['fields']) ? $table['fields']:$columns;
319             
320             if ( ! empty($columns)) {
321                 foreach ($columns as $columnName => $field) {
322                     $colDesc = array();
323                     $colDesc['name'] = $columnName;
324                     
325                     $e = explode('(', $field['type']);
326                     if (isset($e[0]) && isset($e[1])) {
327                         $colDesc['type'] = $e[0];
328                         $colDesc['length'] = substr($e[1], 0, strlen($e[1]) - 1);
329                     } else {
330                         $colDesc['type'] = isset($field['type']) ? (string) $field['type']:null;
331                         $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null;
332                         $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length'];
333                     }
334                     
335                     $colDesc['ptype'] = isset($field['ptype']) ? (string) $field['ptype']:(string) $colDesc['type'];
336                     
337                     $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null;
338                     $colDesc['unsigned'] = isset($field['unsigned']) ? (bool) $field['unsigned']:null;
339                     $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null;
340                     $colDesc['default'] = isset($field['default']) ? $field['default']:null;
341                     $colDesc['notnull'] = isset($field['notnull']) ? (bool) (isset($field['notnull']) && $field['notnull']):null;
342                     $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null;
343                     $colDesc['autoincrement'] = isset($field['autoinc']) ? (bool) (isset($field['autoinc']) && $field['autoinc']):$colDesc['autoincrement'];
344                     $colDesc['unique'] = isset($field['unique']) ? (bool) (isset($field['unique']) && $field['unique']):null;
345                     $colDesc['values'] = isset($field['values']) ? (array) $field['values']: null;
346
347                     $columns[(string) $colDesc['name']] = $colDesc;
348                 }
349             }
350             
351             $build[$className]['connection'] = isset($table['connection']) ? $table['connection']:null;
352             $build[$className]['className'] = $className;
353             $build[$className]['tableName'] = $tableName;
354             $build[$className]['columns'] = $columns;
355             $build[$className]['relations'] = isset($table['relations']) ? $table['relations']:array();
356             $build[$className]['indexes'] = isset($table['indexes']) ? $table['indexes']:array();
357             $build[$className]['attributes'] = isset($table['attributes']) ? $table['attributes']:array();
358             $build[$className]['templates'] = isset($table['templates']) ? $table['templates']:array();
359             $build[$className]['actAs'] = isset($table['actAs']) ? $table['actAs']:array();
360             $build[$className]['package'] = isset($table['package']) ? $table['package']:null;
361         
362             if (isset($table['inheritance'])) {
363                 $build[$className]['inheritance'] = $table['inheritance'];
364             }
365         }
366         
367         return $build;
368     }
369
370     /**
371      * buildRelationships
372      *
373      * Loop through an array of schema information and build all the necessary relationship information
374      * Will attempt to auto complete relationships and simplify the amount of information required for defining a relationship
375      *
376      * @param  string $array 
377      * @return void
378      */
379     protected function buildRelationships(&$array)
380     {
381         foreach ($array as $name => $properties) {
382             if ( ! isset($properties['relations'])) {
383                 continue;
384             }
385             
386             $className = $properties['className'];
387             $relations = $properties['relations'];
388             
389             foreach ($relations as $alias => $relation) {
390                 $class = isset($relation['class']) ? $relation['class']:$alias;
391                 
392                 // Attempt to guess the local and foreign
393                 if (isset($relation['refClass'])) {
394                     $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine::tableize($name) . '_id';
395                     $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:Doctrine::tableize($class) . '_id';
396                 } else {
397                     $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine::tableize($class) . '_id';
398                     $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:'id';
399                 }
400             
401                 $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias;
402                 $relation['class'] = $class;
403                 
404                 if (isset($relation['refClass'])) {
405                     $relation['type'] = 'many';
406                 }
407                 
408                 if (isset($relation['type']) && $relation['type']) {
409                     $relation['type'] = $relation['type'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
410                 } else {
411                     $relation['type'] = Doctrine_Relation::ONE;
412                 }
413
414                 if (isset($relation['foreignType']) && $relation['foreignType']) {
415                     $relation['foreignType'] = $relation['foreignType'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
416                 }
417                 
418                 $this->_relations[$className][$alias] = $relation;
419             }
420         }
421         
422         // Now we fix all the relationships and auto-complete opposite ends of relationships
423         $this->fixRelationships();
424     }
425
426     /**
427      * fixRelationships
428      *
429      * Loop through all relationships building the opposite ends of each relationship
430      *
431      * @return void
432      */
433     protected function fixRelationships()
434     {
435         foreach($this->_relations as $className => $relations) {
436             foreach ($relations AS $alias => $relation) {
437                 $newRelation = array();
438                 $newRelation['foreign'] = $relation['local'];
439                 $newRelation['local'] = $relation['foreign'];
440                 $newRelation['class'] = isset($relation['foreignClass']) ? $relation['foreignClass']:$className;
441                 $newRelation['alias'] = isset($relation['foreignAlias']) ? $relation['foreignAlias']:$className;
442                 
443                 // this is so that we know that this relation was autogenerated and
444                 // that we do not need to include it if it is explicitly declared in the schema by the users.
445                 $newRelation['autogenerated'] = true; 
446                 
447                 if (isset($relation['refClass'])) {
448                     $newRelation['refClass'] = $relation['refClass'];
449                     $newRelation['type'] = isset($relation['foreignType']) ? $relation['foreignType']:$relation['type'];
450                 } else {                
451                     if(isset($relation['foreignType'])) {
452                         $newRelation['type'] = $relation['foreignType'];
453                     } else {
454                         $newRelation['type'] = $relation['type'] === Doctrine_Relation::ONE ? Doctrine_Relation::MANY:Doctrine_Relation::ONE;
455                     }
456                 }
457                 
458                 if (!isset($this->_relations[$relation['class']][$newRelation['alias']])) {
459                     $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation;
460                 }
461             }
462         }
463     }
464 }