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($array, $this->parseSchema($s, $format));
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($array, $this->parseSchema($file->getPathName(), $format));
115                     }
116                 }
117             } else {
118               $array = array_merge($array, $this->parseSchema($s, $format));
119             }
120         }
121
122         $this->_buildRelationships($array);
123
124         return $array;
125     }
126
127     /**
128      * importSchema
129      *
130      * A method to import a Schema and translate it into a Doctrine_Record object
131      *
132      * @param  string $schema       The file containing the XML schema
133      * @param  string $directory    The directory where the Doctrine_Record class will be written
134      * @param  array $models        Optional array of models to import
135      *
136      * @return void
137      */
138     public function importSchema($schema, $format = 'yml', $directory = null, $models = array())
139     {
140         $builder = new Doctrine_Import_Builder();
141         $builder->setTargetPath($directory);
142         
143         foreach ($this->_options as $key => $value) {
144             if ($value) {
145                 $builder->setOption($key, $value);
146             }
147         }
148         
149         $array = $this->buildSchema($schema, $format);
150
151         foreach ($array as $name => $properties) {
152             if ( ! empty($models) && !in_array($properties['className'], $models)) {
153                 continue;
154             }
155             
156             $options = $this->getOptions($properties);
157             $columns = $this->getColumns($properties);
158             $relations = $this->getRelations($properties);
159             $indexes = $this->getIndexes($properties);
160             $attributes = $this->getAttributes($properties);
161             $templates = $this->getTemplates($properties);
162             $actAs = $this->getActAs($properties);
163             $tableOptions = $this->getTableOptions($properties);
164             
165             $builder->buildRecord($options, $columns, $relations, $indexes, $attributes, $templates, $actAs, $tableOptions);
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      * getTableOptions
289      *
290      * @param string $properties 
291      * @return void
292      */
293     public function getTableOptions($properties)
294     {
295         return isset($properties['options']) ? $properties['options']:array();
296     }
297
298     /**
299      * parseSchema
300      *
301      * A method to parse a Schema and translate it into a property array.
302      * The function returns that property array.
303      *
304      * @param  string $schema   Path to the file containing the schema
305      * @return array  $build    Built array of schema information
306      */
307     public function parseSchema($schema, $type)
308     {
309         $array = Doctrine_Parser::load($schema, $type);
310         
311         $build = array();
312         
313         foreach ($array as $className => $table) {
314             $columns = array();
315             
316             $className = isset($table['className']) ? (string) $table['className']:(string) $className;
317             
318             if (isset($table['tableName']) && $table['tableName']) {
319                 $tableName = $table['tableName'];
320             } else {
321                 if (isset($table['inheritance']['extends']) && isset($table['inheritance']['extends']['keyType']) && isset($table['inheritance']['extends']['keyValue'])) {
322                     $tableName = null;
323                 } else {
324                     $tableName = Doctrine::tableize($className);
325                 }
326             }
327             
328             $columns = isset($table['columns']) ? $table['columns']:array();
329             $columns = isset($table['fields']) ? $table['fields']:$columns;
330             
331             if ( ! empty($columns)) {
332                 foreach ($columns as $columnName => $field) {
333                     $colDesc = array();
334                     $colDesc['name'] = $columnName;
335                     
336                     $e = explode('(', $field['type']);
337                     if (isset($e[0]) && isset($e[1])) {
338                         $colDesc['type'] = $e[0];
339                         $colDesc['length'] = substr($e[1], 0, strlen($e[1]) - 1);
340                     } else {
341                         $colDesc['type'] = isset($field['type']) ? (string) $field['type']:null;
342                         $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null;
343                         $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length'];
344                     }
345                     
346                     $colDesc['ptype'] = isset($field['ptype']) ? (string) $field['ptype']:(string) $colDesc['type'];
347                     $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null;
348                     $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null;
349                     $colDesc['default'] = isset($field['default']) ? $field['default']:null;
350                     $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null;
351                     $colDesc['autoincrement'] = isset($field['autoinc']) ? (bool) (isset($field['autoinc']) && $field['autoinc']):$colDesc['autoincrement'];
352                     $colDesc['values'] = isset($field['values']) ? (array) $field['values']:null;
353                     
354                     $validators = Doctrine::getValidators();
355                     
356                     foreach ($validators as $validator) {
357                         if (isset($field[$validator])) {
358                             $colDesc[$validator] = $field[$validator];
359                         }
360                     }
361                     
362                     $columns[(string) $colDesc['name']] = $colDesc;
363                 }
364             }
365             
366             $build[$className]['connection'] = isset($table['connection']) ? $table['connection']:null;
367             $build[$className]['className'] = $className;
368             $build[$className]['tableName'] = $tableName;
369             $build[$className]['columns'] = $columns;
370             $build[$className]['relations'] = isset($table['relations']) ? $table['relations']:array();
371             $build[$className]['indexes'] = isset($table['indexes']) ? $table['indexes']:array();
372             $build[$className]['attributes'] = isset($table['attributes']) ? $table['attributes']:array();
373             $build[$className]['templates'] = isset($table['templates']) ? $table['templates']:array();
374             $build[$className]['actAs'] = isset($table['actAs']) ? $table['actAs']:array();
375             $build[$className]['options'] = isset($table['options']) ? $table['options']:array();
376             $build[$className]['package'] = isset($table['package']) ? $table['package']:null;
377             
378             if (isset($table['inheritance'])) {
379                 $build[$className]['inheritance'] = $table['inheritance'];
380             }
381         }
382         
383         return $build;
384     }
385
386     /**
387      * buildRelationships
388      *
389      * Loop through an array of schema information and build all the necessary relationship information
390      * Will attempt to auto complete relationships and simplify the amount of information required for defining a relationship
391      *
392      * @param  string $array 
393      * @return void
394      */
395     protected function _buildRelationships(&$array)
396     {
397         foreach ($array as $name => $properties) {
398             if ( ! isset($properties['relations'])) {
399                 continue;
400             }
401             
402             $className = $properties['className'];
403             $relations = $properties['relations'];
404             
405             foreach ($relations as $alias => $relation) {
406                 $class = isset($relation['class']) ? $relation['class']:$alias;
407                 
408                 // Attempt to guess the local and foreign
409                 if (isset($relation['refClass'])) {
410                     $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine::tableize($name) . '_id';
411                     $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:Doctrine::tableize($class) . '_id';
412                 } else {
413                     $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine::tableize($class) . '_id';
414                     $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:'id';
415                 }
416             
417                 $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias;
418                 $relation['class'] = $class;
419                 
420                 if (isset($relation['refClass'])) {
421                     $relation['type'] = 'many';
422                 }
423                 
424                 if (isset($relation['type']) && $relation['type']) {
425                     $relation['type'] = $relation['type'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
426                 } else {
427                     $relation['type'] = Doctrine_Relation::ONE;
428                 }
429
430                 if (isset($relation['foreignType']) && $relation['foreignType']) {
431                     $relation['foreignType'] = $relation['foreignType'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
432                 }
433                 
434                 $this->_relations[$className][$alias] = $relation;
435             }
436         }
437         
438         // Now we fix all the relationships and auto-complete opposite ends of relationships
439         $this->_fixRelationships();
440     }
441
442     /**
443      * fixRelationships
444      *
445      * Loop through all relationships building the opposite ends of each relationship
446      *
447      * @return void
448      */
449     protected function _fixRelationships()
450     {
451         foreach($this->_relations as $className => $relations) {
452             foreach ($relations AS $alias => $relation) {
453                 $newRelation = array();
454                 $newRelation['foreign'] = $relation['local'];
455                 $newRelation['local'] = $relation['foreign'];
456                 $newRelation['class'] = isset($relation['foreignClass']) ? $relation['foreignClass']:$className;
457                 $newRelation['alias'] = isset($relation['foreignAlias']) ? $relation['foreignAlias']:$className;
458                 
459                 // this is so that we know that this relation was autogenerated and
460                 // that we do not need to include it if it is explicitly declared in the schema by the users.
461                 $newRelation['autogenerated'] = true; 
462                 
463                 if (isset($relation['refClass'])) {
464                     $newRelation['refClass'] = $relation['refClass'];
465                     $newRelation['type'] = isset($relation['foreignType']) ? $relation['foreignType']:$relation['type'];
466                 } else {                
467                     if(isset($relation['foreignType'])) {
468                         $newRelation['type'] = $relation['foreignType'];
469                     } else {
470                         $newRelation['type'] = $relation['type'] === Doctrine_Relation::ONE ? Doctrine_Relation::MANY:Doctrine_Relation::ONE;
471                     }
472                 }
473                 
474                 if (!isset($this->_relations[$relation['class']][$newRelation['alias']])) {
475                     $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation;
476                 }
477             }
478         }
479     }
480 }