Development

/branches/1.1/lib/plugins/sfPropelPlugin/lib/propel/sfPropelDatabaseSchema.class.php

You must first sign up to be able to contribute.

root/branches/1.1/lib/plugins/sfPropelPlugin/lib/propel/sfPropelDatabaseSchema.class.php

Revision 15765, 29.3 kB (checked in by fabien, 5 years ago)

[1.0, 1.1, 1.2, 1.3] fixed propel:schema-to-yml composite foreign-Keys wrongly converted (closes #5483)

Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
6  * (c) Francois Zaninotto <francois.zaninotto@symfony-project.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 /**
13  *
14  * @package    symfony
15  * @subpackage propel
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @author     Fran├žois Zaninotto <francois.zaninotto@symfony-project.com>
18  * @version    SVN: $Id$
19  */
20 class sfPropelDatabaseSchema
21 {
22   protected $connection_name = '';
23   protected $database        = array();
24
25   public function asArray()
26   {
27     return array($this->connection_name => $this->database);
28   }
29
30   public function loadArray($schema_array)
31   {
32     $database = array();
33     $connection_name = '';
34
35     if (isset($schema_array['classes']))
36     {
37       // New schema syntax
38       $schema_array = $this->convertNewToOldYaml($schema_array);
39     }
40
41     if (count($schema_array) > 1)
42     {
43       throw new sfException('A schema.yml must only contain 1 database entry.');
44     }
45
46     $tmp = array_keys($schema_array);
47     $connection_name = array_shift($tmp);
48
49     if ($connection_name)
50     {
51       $database = $schema_array[$connection_name];
52     }
53
54     $this->connection_name = $connection_name;
55     $this->database = $database;
56     
57     $this->fixYAMLDatabase();
58     $this->fixYAMLI18n();
59     $this->fixYAMLColumns();
60   }
61  
62   public function loadYAML($file)
63   {
64     $schema_array = sfYaml::load($file);
65
66     if (!is_array($schema_array))
67     {
68       return; // No defined schema here, skipping
69     }
70
71     if (!isset($schema_array['classes']))
72     {
73       // Old schema syntax: we convert it
74       $schema_array = $this->convertOldToNewYaml($schema_array);
75     }
76
77     $this->loadArray($schema_array);
78   }
79  
80   public function convertOldToNewYaml($schema)
81   {
82     $new_schema = array();
83
84     $tmp = array_keys($schema);
85     $connection_name = array_shift($tmp);
86     $new_schema['connection'] = $connection_name;
87     
88     $classes = array();
89     foreach($schema[$connection_name] as $table => $table_params)
90     {
91       if ($table == '_attributes')
92       {
93         // Database attributes
94         $new_schema = array_merge($new_schema, $table_params);
95       }
96       else
97       {
98         // Table
99         $phpName = sfInflector::camelize($table);
100         if (isset($table_params['_attributes']))
101         {
102           $table_attributes = $table_params['_attributes'];
103           unset($table_params['_attributes']);
104           if (isset($table_attributes['phpName']))
105           {
106             $phpName = $table_attributes['phpName'];
107             unset($table_attributes['phpName']);
108           }
109         }
110         else
111         {
112           $table_attributes = array();
113         }
114         $classes[$phpName] = $table_attributes;
115         $classes[$phpName]['tableName'] = $table;
116         $classes[$phpName]['columns'] = array();
117         foreach($table_params as $column => $column_params)
118         {
119           switch($column)
120           {
121             case '_behaviors':
122               $classes[$phpName]['behaviors'] = $column_params;
123               break;
124             case '_inheritance':
125               $classes[$phpName]['inheritance'] = $column_params;
126               break;
127             case '_foreignKeys':
128               $classes[$phpName]['foreignKeys'] = $column_params;
129               break;
130             case '_indexes':
131               $classes[$phpName]['indexes'] = $column_params;
132               break;
133             case '_uniques':
134               $classes[$phpName]['uniques'] = $column_params;
135               break;
136             default:
137               $classes[$phpName]['columns'][$column] = $column_params;
138           }
139         }
140       }
141     }
142     
143     $new_schema['classes'] = $classes;
144     
145     return $new_schema;
146   }
147  
148   public function convertNewToOldYaml($schema)
149   {
150     
151     if (isset($schema['connection']))
152     {
153       $connection_name = $schema['connection'];
154       unset($schema['connection']);
155     }
156     else
157     {
158       $connection_name = 'propel';
159     }
160     
161     $database = array();
162     
163     // Tables
164     if (isset($schema['classes']))
165     {
166       $tables = array();
167       foreach ($schema['classes'] as $className => $classParams)
168       {
169         $tableParams = array();
170         
171         // Columns
172         if (isset($classParams['columns']))
173         {
174           $tableParams = array_merge($classParams['columns'], $tableParams);
175           unset($classParams['columns']);
176         }
177
178         // Indexes and foreign keys
179         if (isset($classParams['indexes']))
180         {
181           $tableParams['_indexes'] = $classParams['indexes'];
182           unset($classParams['indexes']);
183         }
184         if (isset($classParams['uniques']))
185         {
186           $tableParams['_uniques'] = $classParams['uniques'];
187           unset($classParams['uniques']);
188         }
189         if (isset($classParams['foreignKeys']))
190         {
191           $tableParams['_foreignKeys'] = $classParams['foreignKeys'];
192           unset($classParams['foreignKeys']);
193         }
194         
195         // Behaviors
196         if (isset($classParams['behaviors']))
197         {
198           $tableParams['_behaviors'] = $classParams['behaviors'];
199           unset($classParams['behaviors']);
200         }
201         
202         // Inheritance
203         if (isset($classParams['inheritance']))
204         {
205           $tableParams['_inheritance'] = $classParams['inheritance'];
206           unset($classParams['inheritance']);
207         }
208         
209         // Table attributes
210         $tableAttributes = array();
211         if (isset($classParams['tableName']))
212         {
213           $tableName = $classParams['tableName'];
214           unset($classParams['tableName']);
215         }
216         else
217         {
218           $tableName = sfInflector::underscore($className);
219         }
220         
221         if (sfInflector::camelize($tableName) != $className)
222         {
223           $tableAttributes['phpName'] = $className;
224         }
225         
226         if ($tableAttributes || $classParams)
227         {
228           $tableParams['_attributes'] = array_merge($tableAttributes, $classParams);
229         }
230         
231         $tables[$tableName] = $tableParams;
232       }
233       $database = array_merge($database, $tables);
234       unset($schema['classes']);
235     }
236     
237     // Database attributes
238     if ($schema)
239     {
240       $database['_attributes'] = $schema;
241     }
242     
243     return array($connection_name => $database);
244   }
245
246   public function asXML()
247   {
248     $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
249
250     $xml .= "<database name=\"$this->connection_name\"".$this->getAttributesFor($this->database).">\n";
251
252     // tables
253     foreach ($this->getChildren($this->database) as $tb_name => $table)
254     {
255       $xml .= "\n  <table name=\"$tb_name\"".$this->getAttributesFor($table);
256       if (isset($table['_behaviors']))
257       {
258         $xml .= sprintf(" behaviors=\"%s\"", htmlspecialchars(serialize($table['_behaviors']), ENT_QUOTES, sfConfig::get('sf_charset')));
259       }
260       $xml .= ">\n";
261
262       // columns
263       foreach ($this->getChildren($table) as $col_name => $column)
264       {
265         // inheritance
266         if (isset($table['_inheritance']) &&
267             isset($table['_inheritance']['column']) &&
268             $col_name == $table['_inheritance']['column'] &&
269             isset($table['_inheritance']['classes']) &&
270             is_array($table['_inheritance']['classes']))
271         {
272           $column['inheritance'] = $table['_inheritance']['classes'];
273           unset($table['_inheritance']);
274         }
275         
276         $xml .= "    <column name=\"$col_name\"".$this->getAttributesForColumn($tb_name, $col_name, $column);
277       }
278
279       // indexes
280       if (isset($table['_indexes']))
281       {
282         foreach ($table['_indexes'] as $index_name => $index)
283         {
284           $xml .= "    <index name=\"$index_name\">\n";
285           foreach ($index as $index_column)
286           {
287             preg_match('/^(.+?)\(([\d]+)\)$/', $index_column, $matches);
288             if (isset($matches[2]))
289             {
290               $xml .= "      <index-column name=\"{$matches[1]}\" size=\"{$matches[2]}\" />\n";
291             }
292             else
293             {
294               $xml .= "      <index-column name=\"$index_column\" />\n";
295             }
296           }
297           $xml .= "    </index>\n";
298         }
299       }
300
301       // uniques
302       if (isset($table['_uniques']))
303       {
304         foreach ($table['_uniques'] as $unique_name => $index)
305         {
306           $xml .= "    <unique name=\"$unique_name\">\n";
307           foreach ($index as $unique_column)
308           {
309             preg_match('/^(.+?)\(([\d]+)\)$/', $unique_column, $matches);
310             if (isset($matches[2]))
311             {
312               $xml .= "      <unique-column name=\"{$matches[1]}\" size=\"{$matches[2]}\" />\n";
313             }
314             else
315             {
316               $xml .= "      <unique-column name=\"$unique_column\" />\n";
317             }
318           }
319           $xml .= "    </unique>\n";
320         }
321       }
322
323       // foreign-keys
324       if (isset($table['_foreignKeys']))
325       {
326         foreach ($table['_foreignKeys'] as $fkey_name => $fkey)
327         {
328           $xml .= "    <foreign-key foreignTable=\"$fkey[foreignTable]\"";
329
330           // foreign key name
331           if (!is_numeric($fkey_name))
332           {
333             $xml .= " name=\"$fkey_name\"";
334           }
335
336           // onDelete
337           if (isset($fkey['onDelete']))
338           {
339             $xml .= " onDelete=\"$fkey[onDelete]\"";
340           }
341
342           // onUpdate
343           if (isset($fkey['onUpdate']))
344           {
345             $xml .= " onUpdate=\"$fkey[onUpdate]\"";
346           }
347           $xml .= ">\n";
348
349           // references
350           if (isset($fkey['references']))
351           {
352             foreach ($fkey['references'] as $reference)
353             {
354               $xml .= "      <reference local=\"$reference[local]\" foreign=\"$reference[foreign]\" />\n";
355             }
356           }
357           $xml .= "    </foreign-key>\n";
358         }
359       }
360
361       $xml .= "  </table>\n";
362     }
363     $xml .= "\n</database>\n";
364
365     return $xml;
366   }
367
368   protected function fixYAMLDatabase()
369   {
370     if (!isset($this->database['_attributes']))
371     {
372       $this->database['_attributes'] = array();
373     }
374
375     // conventions for database attributes
376     $this->setIfNotSet($this->database['_attributes'], 'defaultIdMethod', 'native');
377     $this->setIfNotSet($this->database['_attributes'], 'package', 'lib.model');
378   }
379
380   protected function fixYAMLI18n()
381   {
382     foreach ($this->getTables() as $i18n_table => $columns)
383     {
384       $pos = strpos($i18n_table, '_i18n');
385
386       $has_primary_key = false;
387       foreach ($columns as $column => $attributes)
388       {
389         if (is_array($attributes) && array_key_exists('primaryKey', $attributes))
390         {
391            $has_primary_key = true;
392         }
393       }
394
395       if ($pos > 0 && $pos == strlen($i18n_table) - 5 && !$has_primary_key)
396       {
397         // i18n table without primary key
398         $main_table = $this->findTable(substr($i18n_table, 0, $pos));
399
400         if ($main_table)
401         {
402           // set i18n attributes for main table
403           $this->setIfNotSet($this->database[$main_table]['_attributes'], 'isI18N', 1);
404           $this->setIfNotSet($this->database[$main_table]['_attributes'], 'i18nTable', $i18n_table);
405
406           // set id and culture columns for i18n table
407           $this->setIfNotSet($this->database[$i18n_table], 'id', array(
408             'type'             => 'integer',
409             'required'         => true,
410             'primaryKey'       => true,
411             'foreignTable'     => $main_table,
412             'foreignReference' => 'id',
413             'onDelete'         => 'cascade'
414           ));
415           $this->setIfNotSet($this->database[$i18n_table], 'culture', array(
416             'isCulture'  => true,
417             'type'       => 'varchar',
418             'size'       => '7',
419             'required'   => true,
420             'primaryKey' => true
421           ));
422         }
423         else
424         {
425           throw new sfException(sprintf('Missing main table for internationalized table "%s".', $i18n_table));
426         }
427       }
428     }
429   }
430
431   protected function fixYAMLColumns()
432   {
433     foreach ($this->getTables() as $table => $columns)
434     {
435       $has_primary_key = false;
436       
437       foreach ($columns as $column => $attributes)
438       {
439         if ($attributes == null)
440         {
441           // conventions for null attributes
442           if ($column == 'created_at' || $column == 'updated_at')
443           {
444             // timestamp convention
445             $this->database[$table][$column]['type']= 'timestamp';
446           }
447
448           if ($column == 'id')
449           {
450             // primary key convention
451             $this->database[$table]['id'] = array(
452               'type'          => 'integer',
453               'required'      => true,
454               'primaryKey'    => true,
455               'autoIncrement' => true
456             );
457             $has_primary_key = true;
458           }
459           
460           $pos = strpos($column, '_id');
461           if ($pos > 0 && $pos == strlen($column) - 3)
462           {
463             // foreign key convention
464             $foreign_table = $this->findTable(substr($column, 0, $pos));
465             if ($foreign_table)
466             {
467               $this->database[$table][$column] = array(
468                 'type'             => 'integer',
469                 'foreignTable'     => $foreign_table,
470                 'foreignReference' => 'id'
471               );
472             }
473             else
474             {
475               throw new sfException(sprintf('Unable to resolve foreign table for column "%s".', $column));
476             }
477           }
478           
479         }
480         else
481         {
482           if (!is_array($attributes))
483           {
484             // compact type given as single attribute
485             $this->database[$table][$column] = $this->getAttributesFromCompactType($attributes);
486           }
487           else
488           {
489             if (isset($attributes['type']))
490             {
491               // compact type given as value of the type attribute
492               $this->database[$table][$column] = array_merge($this->database[$table][$column], $this->getAttributesFromCompactType($attributes['type']));
493             }
494             if (isset($attributes['primaryKey']))
495             {
496               $has_primary_key = true;
497             }
498           }
499         }
500       }
501
502       if (!$has_primary_key)
503       {
504         // convention for tables without primary key
505         $this->database[$table]['id'] = array(
506           'type'          => 'integer',
507           'required'      => true,
508           'primaryKey'    => true,
509           'autoIncrement' => true
510         );
511       }
512     }
513   }
514
515   protected function getAttributesFromCompactType($type)
516   {
517     preg_match('/varchar\(([\d]+)\)/', $type, $matches);
518     if (isset($matches[1]))
519     {
520       return array('type' => 'varchar', 'size' => $matches[1]);
521     }
522     else
523     {
524       return array('type' => $type);
525     }
526   }
527
528   protected function setIfNotSet(&$entry, $key, $value)
529   {
530     if (!isset($entry[$key]))
531     {
532       $entry[$key] = $value;
533     }
534   }
535
536   protected function findTable($table_name)
537   {
538     // find a table from a phpName or a name
539     $table_match = false;
540     foreach ($this->getTables() as $tb_name => $table)
541     {
542       if (
543            ($tb_name == $table_name)
544            || (isset($table['_attributes']['phpName']) &&
545              (
546                $table['_attributes']['phpName'] == sfInflector::camelize($table_name)
547                || $table['_attributes']['phpName'] == $table_name
548              )
549            || (sfInflector::underscore($table_name) == $tb_name))
550          )
551       {
552         $table_match = $tb_name;
553         break;
554       }
555     }
556
557     return $table_match;
558   }
559
560   protected function getAttributesForColumn($tb_name, $col_name, $column)
561   {
562     $attributes_string = '';
563     if (is_array($column))
564     {
565       foreach ($column as $key => $value)
566       {
567         if (!in_array($key, array('foreignClass', 'foreignTable', 'foreignReference', 'onDelete', 'onUpdate', 'index', 'unique', 'sequence', 'inheritance')))
568         {
569           $attributes_string .= " $key=\"".htmlspecialchars($this->getCorrectValueFor($key, $value), ENT_QUOTES, sfConfig::get('sf_charset'))."\"";
570         }
571       }
572       if (isset($column['inheritance']))
573       {
574         $attributes_string .= ' inheritance="single">'."\n";
575         
576         $extended_package = isset($this->database[$tb_name]['_attributes']['package']) ?
577           $this->database[$tb_name]['_attributes']['package'] :
578           $this->database['_attributes']['package'];
579         
580         $extended_class = isset($this->database[$tb_name]['_attributes']['phpName']) ?
581           $this->database[$tb_name]['_attributes']['phpName'] :
582           sfInflector::camelize($tb_name);
583
584         foreach ($column['inheritance'] as $key => $class)
585         {
586           $attributes_string .= sprintf('      <inheritance extends="%s.%s" key="%s" class="%s" />%s',
587             $extended_package, $extended_class, $key, $class, "\n");
588         }
589         $attributes_string .= '    </column>'."\n";
590       }
591       else
592       {
593         $attributes_string .= " />\n";
594       }
595     }
596     else
597     {
598       throw new sfException(sprintf('Incorrect settings for column "%s".', $col_name));
599     }
600
601     // conventions for foreign key attributes
602     if (is_array($column) && (isset($column['foreignTable']) || isset($column['foreignClass'])))
603     {
604       if (isset($column['foreignTable']))
605       {
606         $attributes_string .= "    <foreign-key foreignTable=\"$column[foreignTable]\"";
607       }
608       else
609       {
610         $foreignTable = $this->findTable($column['foreignClass']);
611         if (!$foreignTable)
612         {
613           // Let's assume that the class given is from another schema
614           // We have no access to the other schema's phpNames
615           // So our last guess is to try to underscore the class name
616           $foreignTable = sfInflector::underscore($column['foreignClass']);
617         }
618         $attributes_string .= "    <foreign-key foreignTable=\"".$foreignTable."\"";
619       }
620       
621       if (isset($column['onDelete']))
622       {
623         $attributes_string .= " onDelete=\"$column[onDelete]\"";
624       }
625       if (isset($column['onUpdate']))
626       {
627         $attributes_string .= " onUpdate=\"$column[onUpdate]\"";
628       }
629       $attributes_string .= ">\n";
630       $attributes_string .= "      <reference local=\"$col_name\" foreign=\"$column[foreignReference]\" />\n";
631       $attributes_string .= "    </foreign-key>\n";
632     }
633
634     // conventions for index and unique index attributes
635     if (is_array($column) && isset($column['index']))
636     {
637       if ($column['index'] === 'unique')
638       {
639         $attributes_string .= "    <unique>\n";
640         $attributes_string .= "      <unique-column name=\"$col_name\" />\n";
641         $attributes_string .= "    </unique>\n";
642       }
643       else
644       {
645         $attributes_string .= "    <index>\n";
646         $attributes_string .= "      <index-column name=\"$col_name\" />\n";
647         $attributes_string .= "    </index>\n";
648       }
649     }
650
651     // conventions for sequence name attributes
652     // required for databases using sequences for auto-increment columns (e.g. PostgreSQL or Oracle)
653     if (is_array($column) && isset($column['sequence']))
654     {
655       $attributes_string .= "    <id-method-parameter value=\"$column[sequence]\" />\n";
656     }
657
658     return $attributes_string;
659   }
660
661   protected function getAttributesFor($tag)
662   {
663     if (!isset($tag['_attributes']))
664     {
665       return '';
666     }
667     $attributes = $tag['_attributes'];
668     $attributes_string = '';
669     foreach ($attributes as $key => $value)
670     {
671       $attributes_string .= ' '.$key.'="'.htmlspecialchars($this->getCorrectValueFor($key, $value), ENT_QUOTES, sfConfig::get('sf_charset')).'"';
672     }
673
674     return $attributes_string;
675   }
676
677   protected function getCorrectValueFor($key, $value)
678   {
679     $booleans = array('required', 'primaryKey', 'autoincrement', 'autoIncrement', 'isI18N', 'isCulture');
680     if (in_array($key, $booleans))
681     {
682       return $value == 1 ? 'true' : 'false';
683     }
684     else
685     {
686       return is_null($value) ? 'null' : $value;
687     }
688   }
689
690   public function getTables()
691   {
692     return $this->getChildren($this->database);
693   }
694
695   public function getChildren($hash)
696   {
697     foreach ($hash as $key => $value)
698     {
699       // ignore special children (starting with _)
700       if ($key[0] == '_')
701       {
702         unset($hash[$key]);
703       }
704     }
705
706     return $hash;
707   }
708
709   public function loadXML($file)
710   {
711     $schema = simplexml_load_file($file);
712     $database = array();
713
714     // database
715     list($database_name, $database_attributes) = $this->getNameAndAttributes($schema->attributes());
716     if ($database_name)
717     {
718       $this->connection_name = $database_name;
719     }
720     else
721     {
722       throw new sfException('The database tag misses a name attribute.');
723     }
724     if ($database_attributes)
725     {
726       $database['_attributes'] = $database_attributes;
727     }
728
729     // tables
730     foreach ($schema as $table)
731     {
732       list($table_name, $table_attributes) = $this->getNameAndAttributes($table->attributes());
733       if ($table_name)
734       {
735         $database[$table_name] = array();
736       }
737       else
738       {
739         throw new sfException('A table tag misses the name attribute.');
740       }
741       if ($table_attributes)
742       {
743         $database[$table_name]['_attributes'] = $table_attributes;
744       }
745
746       // columns
747       foreach ($table->xpath('column') as $column)
748       {
749         list($column_name, $column_attributes) = $this->getNameAndAttributes($column->attributes());
750         if ($column_name)
751         {
752           $database[$table_name][$column_name] = $column_attributes;
753         }
754         else
755         {
756           throw new sfException('A column tag misses the name attribute.');
757         }
758       }
759
760       // foreign-keys
761       $database[$table_name]['_foreignKeys'] = array();
762       foreach ($table->xpath('foreign-key') as $foreign_key)
763       {
764         $foreign_key_table = array();
765
766         // foreign key attributes
767         if (isset($foreign_key['foreignTable']))
768         {
769           $foreign_key_table['foreignTable'] = (string) $foreign_key['foreignTable'];
770         }
771         else
772         {
773           throw new sfException('A foreign key misses the foreignTable attribute.');
774         }
775         if (isset($foreign_key['onDelete']))
776         {
777           $foreign_key_table['onDelete'] = (string) $foreign_key['onDelete'];
778         }
779         if (isset($foreign_key['onUpdate']))
780         {
781           $foreign_key_table['onUpdate'] = (string) $foreign_key['onUpdate'];
782         }
783
784         // foreign key references
785         $foreign_key_table['references'] = array();
786         foreach ($foreign_key->xpath('reference') as $reference)
787         {
788           $reference_attributes = array();
789           foreach ($reference->attributes() as $reference_attribute_name => $reference_attribute_value)
790           {
791             $reference_attributes[$reference_attribute_name] = strval($reference_attribute_value);
792           }
793           $foreign_key_table['references'][] = $reference_attributes;
794         }
795
796         if (isset($foreign_key['name']))
797         {
798           $database[$table_name]['_foreignKeys'][(string)$foreign_key['name']] = $foreign_key_table;
799         }
800         else
801         {
802           $database[$table_name]['_foreignKeys'][] = $foreign_key_table;
803         }
804
805       }
806       $this->removeEmptyKey($database[$table_name], '_foreignKeys');
807
808       // indexes
809       $database[$table_name]['_indexes'] = array();
810       foreach ($table->xpath('index') as $index)
811       {
812         $index_keys = array();
813         foreach ($index->xpath('index-column') as $index_key)
814         {
815           $index_keys[] = strval($index_key['name']);
816         }
817         $database[$table_name]['_indexes'][strval($index['name'])] = $index_keys;
818       }
819       $this->removeEmptyKey($database[$table_name], '_indexes');
820
821       // unique indexes
822       $database[$table_name]['_uniques'] = array();
823       foreach ($table->xpath('unique') as $index)
824       {
825         $unique_keys = array();
826         foreach ($index->xpath('unique-column') as $unique_key)
827         {
828           $unique_keys[] = strval($unique_key['name']);
829         }
830         $database[$table_name]['_uniques'][strval($index['name'])] = $unique_keys;
831       }
832       $this->removeEmptyKey($database[$table_name], '_uniques');
833     }
834     $this->database = $database;
835     
836     $this->fixXML();
837   }
838
839   public function fixXML()
840   {
841     $this->fixXMLForeignKeys();
842     $this->fixXMLIndexes();
843     // $this->fixXMLColumns();
844   }
845
846   protected function fixXMLForeignKeys()
847   {
848     foreach ($this->getTables() as $table => $columns)
849     {
850       if (isset($this->database[$table]['_foreignKeys']))
851       {
852         $foreign_keys = $this->database[$table]['_foreignKeys'];
853         foreach ($foreign_keys as $foreign_key_name => $foreign_key_attributes)
854         {
855           // Only single foreign keys can be simplified
856           if (count($foreign_key_attributes['references']) == 1)
857           {
858             $reference = $foreign_key_attributes['references'][0];
859
860             // set simple foreign key
861             $this->database[$table][$reference['local']]['foreignTable'] = $foreign_key_attributes['foreignTable'];
862             $this->database[$table][$reference['local']]['foreignReference'] = $reference['foreign'];
863             if (isset($foreign_key_attributes['onDelete']))
864             {
865               $this->database[$table][$reference['local']]['onDelete'] = $foreign_key_attributes['onDelete'];
866             }
867             if (isset($foreign_key_attributes['onUpdate']))
868             {
869               $this->database[$table][$reference['local']]['onUpdate'] = $foreign_key_attributes['onUpdate'];
870             }
871
872             // remove complex foreign key
873             unset($this->database[$table]['_foreignKeys'][$foreign_key_name]);
874           }
875
876           $this->removeEmptyKey($this->database[$table], '_foreignKeys');
877         }
878       }
879     }
880   }
881
882   protected function fixXMLIndexes()
883   {
884     foreach ($this->getTables() as $table => $columns)
885     {
886       if (isset($this->database[$table]['_indexes']))
887       {
888         $indexes = $this->database[$table]['_indexes'];
889         foreach ($indexes as $index => $references)
890         {
891           // Only single indexes can be simplified
892           if (count($references) == 1 && false !== substr($index, 0, strlen($index) - 6) && array_key_exists(substr($index, 0, strlen($index) - 6), $columns))
893           {
894             $reference = $references[0];
895
896             // set simple index
897             $this->database[$table][$reference]['index'] = 'true';
898
899             // remove complex index
900             unset($this->database[$table]['_indexes'][$index]);
901           }
902           
903           $this->removeEmptyKey($this->database[$table], '_indexes');
904         }
905       }
906       if (isset($this->database[$table]['_uniques']))
907       {
908         $uniques = $this->database[$table]['_uniques'];
909         foreach ($uniques as $index => $references)
910         {
911           // Only single unique indexes can be simplified
912           if (count($references) == 1 && false !== substr($index, 0, strlen($index) - 7) && array_key_exists(substr($index, 0, strlen($index) - 7), $columns))
913           {
914             $reference = $references[0];
915
916             // set simple index
917             $this->database[$table][$reference]['index'] = 'unique';
918
919             // remove complex unique index
920             unset($this->database[$table]['_uniques'][$index]);
921           }
922
923           $this->removeEmptyKey($this->database[$table], '_uniques');
924         }
925       }
926     }
927   }
928
929   protected function fixXMLColumns()
930   {
931     foreach ($this->getTables() as $table => $columns)
932     {
933       foreach ($columns as $column => $attributes)
934       {
935         if ($column == 'id' && !array_diff($attributes, array('type' => 'integer', 'required' => 'true', 'primaryKey' => 'true', 'autoIncrement' => 'true')))
936         {
937           // simplify primary keys
938           $this->database[$table]['id'] = null;
939         }
940
941         if (($column == 'created_at') || ($column == 'updated_at') && !array_diff($attributes, array('type' => 'timestamp')))
942         {
943           // simplify timestamps
944           $this->database[$table][$column] = null;
945         }
946
947         $pos                 = strpos($column, '_id');
948         $has_fk_name         = $pos > 0 && $pos == strlen($column) - 3;
949         $is_foreign_key      = isset($attributes['type']) && $attributes['type'] == 'integer' && isset($attributes['foreignReference']) && $attributes['foreignReference'] == 'id';
950         $has_foreign_table   = isset($attributes['foreignTable']) && array_key_exists($attributes['foreignTable'], $this->getTables());
951         $has_other_attribute = isset($attributes['onDelete']);
952         if ($has_fk_name && $has_foreign_table && $is_foreign_key && !$has_other_attribute)
953         {
954           // simplify foreign key
955           $this->database[$table][$column] = null;
956         }
957       }
958     }
959   }
960
961   public function asYAML()
962   {
963     return sfYaml::dump(array($this->connection_name => $this->database), 3);
964   }
965
966   protected function getNameAndAttributes($hash, $name_attribute = 'name')
967   {
968     // tag name
969     $name = '';
970     if (isset($hash[$name_attribute]))
971     {
972       $name = strval($hash[$name_attribute]);
973       unset($hash[$name_attribute]);
974     }
975
976     // tag attributes
977     $attributes = array();
978     foreach ($hash as $attribute => $value)
979     {
980       $value = (string) $value;
981       if (in_array($value, array('true', 'on')))
982       {
983         $value = true;
984       }
985       elseif (in_array($value, array('false', 'off')))
986       {
987         $value = false;
988       }
989       $attributes[$attribute] = $value;
990     }
991
992     return array($name, $attributes);
993   }
994
995   protected function removeEmptyKey(&$hash, $key)
996   {
997     if (isset($hash[$key]) && !$hash[$key])
998     {
999       unset($hash[$key]);
1000     }
1001   }
1002 }
1003
Note: See TracBrowser for help on using the browser.