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'                =>  '.class.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         $this->_options = $options;
87     }
88
89     /**
90      * buildSchema
91      *
92      * Loop throug directories of schema files and part them all in to one complete array of schema information
93      *
94      * @param  string   $schema Array of schema files or single schema file. Array of directories with schema files or single directory
95      * @param  string   $format Format of the files we are parsing and building from
96      * @return array    $array
97      */
98     public function buildSchema($schema, $format)
99     {
100         $array = array();
101         
102         foreach ((array) $schema AS $s) {
103             if (is_file($s)) {
104                 $array = array_merge($array, $this->parseSchema($s, $format));
105             } else if (is_dir($s)) {
106                 $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($s),
107                                                       RecursiveIteratorIterator::LEAVES_ONLY);
108             
109                 foreach ($it as $file) {
110                     $e = explode('.', $file->getFileName());
111                     if (end($e) === $format) {
112                         $array = array_merge($array, $this->parseSchema($file->getPathName(), $format));
113                     }
114                 }
115             }
116         }
117         
118         $this->buildRelationships($array);
119         
120         return array('schema' => $array, 'relations' => $this->_relations);
121     }
122
123     /**
124      * importSchema
125      *
126      * A method to import a Schema and translate it into a Doctrine_Record object
127      *
128      * @param  string $schema       The file containing the XML schema
129      * @param  string $directory    The directory where the Doctrine_Record class will be written
130      * @param  array $models        Optional array of models to import
131      *
132      * @return void
133      */
134     public function importSchema($schema, $format = 'yml', $directory = null, $models = array())
135     {
136         $builder = new Doctrine_Import_Builder();
137         $builder->setTargetPath($directory);
138         $builder->generateBaseClasses($this->getOption('generateBaseClasses'));
139         $builder->generateTableClasses($this->getOption('generateTableClasses'));
140         $builder->setBaseClassesDirectory($this->getOption('baseClassesDirectory'));
141         $builder->setBaseClassName($this->getOption('baseClassName'));
142         $builder->setPackagesPath($this->getOption('packagesPath'));
143         $builder->setPackagesPrefix($this->getOption('packagesPrefix'));
144         $builder->setSuffix($this->getOption('suffix'));
145         
146         $schema = $this->buildSchema($schema, $format);
147         
148         $array = $schema['schema'];
149         
150         foreach ($array as $name => $properties) {
151             if ( ! empty($models) && !in_array($properties['className'], $models)) {
152                 continue;
153             }
154             
155             $options = $this->getOptions($properties);
156             $columns = $this->getColumns($properties);
157             $relations = $this->getRelations($properties);
158             $indexes = $this->getIndexes($properties);
159             $attributes = $this->getAttributes($properties);
160             $templates = $this->getTemplates($properties);
161             $actAs = $this->getActAs($properties);
162             
163             $builder->buildRecord($options, $columns, $relations, $indexes, $attributes, $templates, $actAs);
164         }
165     }
166
167     /**
168      * getOptions
169      *
170      * @param string $properties Array of table properties
171      * @param string $directory  Directory we are writing the class to
172      * @return array $options    Array of options from a parse schemas properties
173      */
174     public function getOptions($properties)
175     {
176         $options = array();
177         $options['className'] = $properties['className'];
178         $options['tableName'] = isset($properties['tableName']) ? $properties['tableName']:null;
179         $options['connection'] = isset($properties['connection']) ? $properties['connection']:null;
180         $options['connectionClassName'] = isset($properties['connection']) ? $properties['className']:null;
181         $options['package'] = $properties['package'];
182         
183         if (isset($properties['inheritance'])) {
184             $options['inheritance'] = $properties['inheritance'];
185         }
186
187         return $options;
188     }
189
190     /**
191      * getColumns
192      *
193      * Get array of columns from table properties
194      *
195      * @param  string $properties Array of table properties
196      * @return array  $columns    Array of columns
197      */
198     public function getColumns($properties)
199     {
200         return isset($properties['columns']) ? $properties['columns']:array();
201     }
202
203     /**
204      * getRelations
205      * 
206      * Get array of relations from table properties
207      *
208      * @param  string $properties Array of tables properties
209      * @return array  $relations  Array of relations
210      */
211     public function getRelations($properties)
212     {
213         $all_relations = isset($this->_relations[$properties['className']]) ? $this->_relations[$properties['className']]:array();
214         
215         // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations
216         $exist_relations = array();
217         $unique_relations = array();
218         foreach ($all_relations as $relation) {
219           if (!in_array($relation['class'], $exist_relations)) {
220             $exist_relations[] = $relation['class'];
221             $unique_relations = array_merge($unique_relations, array($relation['alias'] => $relation));
222           } else {
223             // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it
224             if (!isset($relation['autogenerated']) || $relation['autogenerated'] != true) {
225               $unique_relations = array_merge($unique_relations, array($relation['alias'] => $relation));
226             }
227           }
228         }
229         
230         return $unique_relations;
231     }
232
233     /**
234      * getIndexes
235      *
236      * Get array of indexes from table properties
237      *
238      * @param  string $properties Array of table properties
239      * @return array  $index
240      */
241     public function getIndexes($properties)
242     {
243         return isset($properties['indexes']) ? $properties['indexes']:array();;
244     }
245
246     /**
247      * getAttributes
248      *
249      * Get array of attributes from table properties
250      *
251      * @param  string $properties Array of tables properties 
252      * @return array  $attributes
253      */
254     public function getAttributes($properties)
255     {
256         return isset($properties['attributes']) ? $properties['attributes']:array();
257     }
258
259     /**
260      * getTemplates
261      *
262      * Get array of templates from table properties
263      *
264      * @param  string $properties Array of table properties
265      * @return array  $templates  Array of table templates
266      */
267     public function getTemplates($properties)
268     {
269         return isset($properties['templates']) ? $properties['templates']:array();
270     }
271
272     /**
273      * getActAs
274      *
275      * Get array of actAs definitions from table properties
276      *
277      * @param  string $properties Array of table properties
278      * @return array  $actAs      Array of actAs definitions from table properties
279      */
280     public function getActAs($properties)
281     {
282         return isset($properties['actAs']) ? $properties['actAs']:array();
283     }
284
285     /**
286      * parseSchema
287      *
288      * A method to parse a Schema and translate it into a property array.
289      * The function returns that property array.
290      *
291      * @param  string $schema   Path to the file containing the schema
292      * @return array  $build    Built array of schema information
293      */
294     public function parseSchema($schema, $type)
295     {
296         $array = Doctrine_Parser::load($schema, $type);
297         
298         $build = array();
299         
300         foreach ($array as $className => $table) {
301             $columns = array();
302             
303             $className = isset($table['className']) ? (string) $table['className']:(string) $className;
304             
305             if (isset($table['tableName']) && $table['tableName']) {
306                 $tableName = $table['tableName'];
307             } else {
308                 if (isset($table['inheritance']['extends']) && isset($table['inheritance']['extends']['keyType']) && isset($table['inheritance']['extends']['keyValue'])) {
309                     $tableName = null;
310                 } else {
311                     $tableName = Doctrine::tableize($className);
312                 }
313             }
314             
315             $columns = isset($table['columns']) ? $table['columns']:array();
316             $columns = isset($table['fields']) ? $table['fields']:$columns;
317             
318             if ( ! empty($columns)) {
319                 foreach ($columns as $columnName => $field) {
320                     $colDesc = array();
321                     $colDesc['name'] = isset($field['name']) ? (string) $field['name']:$columnName;
322                     
323                     $e = explode('(', $field['type']);
324                     if (isset($e[0]) && isset($e[1])) {
325                         $colDesc['type'] = $e[0];
326                         $colDesc['length'] = substr($e[1], 0, strlen($e[1]) - 1);
327                     } else {
328                         $colDesc['type'] = isset($field['type']) ? (string) $field['type']:null;
329                         $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null;
330                         $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length'];
331                     }
332                     
333                     $colDesc['ptype'] = isset($field['ptype']) ? (string) $field['ptype']:(string) $colDesc['type'];
334                     
335                     $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null;
336                     $colDesc['unsigned'] = isset($field['unsigned']) ? (bool) $field['unsigned']:null;
337                     $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null;
338                     $colDesc['default'] = isset($field['default']) ? $field['default']:null;
339                     $colDesc['notnull'] = isset($field['notnull']) ? (bool) (isset($field['notnull']) && $field['notnull']):null;
340                     $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null;
341                     $colDesc['autoincrement'] = isset($field['autoinc']) ? (bool) (isset($field['autoinc']) && $field['autoinc']):$colDesc['autoincrement'];
342                     $colDesc['unique'] = isset($field['unique']) ? (bool) (isset($field['unique']) && $field['unique']):null;
343                     $colDesc['values'] = isset($field['values']) ? (array) $field['values']: null;
344
345                     $columns[(string) $colDesc['name']] = $colDesc;
346                 }
347             }
348             
349             $build[$className]['connection'] = isset($table['connection']) ? $table['connection']:null;
350             $build[$className]['className'] = $className;
351             $build[$className]['tableName'] = $tableName;
352             $build[$className]['columns'] = $columns;
353             $build[$className]['relations'] = isset($table['relations']) ? $table['relations']:array();
354             $build[$className]['indexes'] = isset($table['indexes']) ? $table['indexes']:array();
355             $build[$className]['attributes'] = isset($table['attributes']) ? $table['attributes']:array();
356             $build[$className]['templates'] = isset($table['templates']) ? $table['templates']:array();
357             $build[$className]['actAs'] = isset($table['actAs']) ? $table['actAs']:array();
358             $build[$className]['package'] = isset($table['package']) ? $table['package']:null;
359         
360             if (isset($table['inheritance'])) {
361                 $build[$className]['inheritance'] = $table['inheritance'];
362             }
363         }
364         
365         return $build;
366     }
367
368     /**
369      * buildRelationships
370      *
371      * Loop through an array of schema information and build all the necessary relationship information
372      * Will attempt to auto complete relationships and simplify the amount of information required for defining a relationship
373      *
374      * @param  string $array 
375      * @return void
376      */
377     protected function buildRelationships(&$array)
378     {
379         foreach ($array as $name => $properties) {
380             if ( ! isset($properties['relations'])) {
381                 continue;
382             }
383             
384             $className = $properties['className'];
385             $relations = $properties['relations'];
386             
387             foreach ($relations as $alias => $relation) {
388                 $class = isset($relation['class']) ? $relation['class']:$alias;
389                 
390                 // Attempt to guess the local and foreign
391                 if (isset($relation['refClass'])) {
392                     $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine::tableize($name) . '_id';
393                     $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:Doctrine::tableize($class) . '_id';
394                 } else {
395                     $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine::tableize($class) . '_id';
396                     $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:'id';
397                 }
398             
399                 $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias;
400                 $relation['class'] = $class;
401                 
402                 if (isset($relation['refClass'])) {
403                     $relation['type'] = 'many';
404                 }
405                 
406                 if (isset($relation['type']) && $relation['type']) {
407                     $relation['type'] = $relation['type'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
408                 } else {
409                     $relation['type'] = Doctrine_Relation::ONE;
410                 }
411
412                 if (isset($relation['foreignType']) && $relation['foreignType']) {
413                     $relation['foreignType'] = $relation['foreignType'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
414                 }
415                 
416                 $this->_relations[$className][$alias] = $relation;
417             }
418         }
419         
420         // Now we fix all the relationships and auto-complete opposite ends of relationships
421         $this->fixRelationships();
422     }
423
424     /**
425      * fixRelationships
426      *
427      * Loop through all relationships building the opposite ends of each relationship
428      *
429      * @return void
430      */
431     protected function fixRelationships()
432     {
433         foreach($this->_relations as $className => $relations) {
434             foreach ($relations AS $alias => $relation) {
435                 $newRelation = array();
436                 $newRelation['foreign'] = $relation['local'];
437                 $newRelation['local'] = $relation['foreign'];
438                 $newRelation['class'] = isset($relation['foreignClass']) ? $relation['foreignClass']:$className;
439                 $newRelation['alias'] = isset($relation['foreignAlias']) ? $relation['foreignAlias']:$className;
440                 
441                 // this is so that we know that this relation was autogenerated and
442                 // that we do not need to include it if it is explicitly declared in the schema by the users.
443                 $newRelation['autogenerated'] = true; 
444                 
445                 if (isset($relation['refClass'])) {
446                     $newRelation['refClass'] = $relation['refClass'];
447                     $newRelation['type'] = isset($relation['foreignType']) ? $relation['foreignType']:$relation['type'];
448                 } else {                
449                     if(isset($relation['foreignType'])) {
450                         $newRelation['type'] = $relation['foreignType'];
451                     } else {
452                         $newRelation['type'] = $relation['type'] === Doctrine_Relation::ONE ? Doctrine_Relation::MANY:Doctrine_Relation::ONE;
453                     }
454                 }
455                 
456                 if (!isset($this->_relations[$relation['class']][$newRelation['alias']])) {
457                     $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation;
458                 }
459             }
460         }
461     }
462 }