Development

/branches/1.2/lib/plugins/sfDoctrinePlugin/lib/generator/sfDoctrineFormGenerator.class.php

You must first sign up to be able to contribute.

root/branches/1.2/lib/plugins/sfDoctrinePlugin/lib/generator/sfDoctrineFormGenerator.class.php

Revision 20681, 16.4 kB (checked in by Kris.Wallsmith, 5 years ago)

[1.2, 1.3] fixed widget class selection to work with latest doctrine development which added the possibility of a string column with a length of null (closes #6873, thanks co3k)

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) Jonathan H. Wage <jonwage@gmail.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  * Doctrine form generator.
14  *
15  * This class generates a Doctrine forms.
16  *
17  * @package    symfony
18  * @subpackage generator
19  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
20  * @author     Jonathan H. Wage <jonwage@gmail.com>
21  * @version    SVN: $Id: sfDoctrineFormGenerator.class.php 8512 2008-04-17 18:06:12Z fabien $
22  */
23 class sfDoctrineFormGenerator extends sfGenerator
24 {
25   /**
26    * Array of all the loaded models
27    *
28    * @var array
29    */
30   public $models = array();
31
32   /**
33    * Array of all plugin models
34    *
35    * @var array
36    */
37   public $pluginModels = array();
38
39   /**
40    * Initializes the current sfGenerator instance.
41    *
42    * @param sfGeneratorManager A sfGeneratorManager instance
43    */
44   public function initialize(sfGeneratorManager $generatorManager)
45   {
46     parent::initialize($generatorManager);
47
48     $this->getPluginModels();
49     $this->setGeneratorClass('sfDoctrineForm');
50   }
51
52   /**
53    * Generates classes and templates in cache.
54    *
55    * @param array The parameters
56    *
57    * @return string The data to put in configuration cache
58    */
59   public function generate($params = array())
60   {
61     $this->params = $params;
62
63     if (!isset($this->params['connection']))
64     {
65       throw new sfParseException('You must specify a "connection" parameter.');
66     }
67
68     if (!isset($this->params['model_dir_name']))
69     {
70       $this->params['model_dir_name'] = 'model';
71     }
72
73     if (!isset($this->params['form_dir_name']))
74     {
75       $this->params['form_dir_name'] = 'form';
76     }
77
78     $models = $this->loadModels();
79
80     // create the project base class for all forms
81     $file = sfConfig::get('sf_lib_dir').'/form/doctrine/BaseFormDoctrine.class.php';
82     if (!file_exists($file))
83     {
84       if (!is_dir(sfConfig::get('sf_lib_dir').'/form/doctrine/base'))
85       {
86         mkdir(sfConfig::get('sf_lib_dir').'/form/doctrine/base', 0777, true);
87       }
88
89       file_put_contents($file, $this->evalTemplate('sfDoctrineFormBaseTemplate.php'));
90     }
91
92     $pluginPaths = $this->generatorManager->getConfiguration()->getAllPluginPaths();
93
94     // create a form class for every Doctrine class
95     foreach ($models as $model)
96     {
97       $this->table = Doctrine::getTable($model);
98       $this->modelName = $model;
99
100       $baseDir = sfConfig::get('sf_lib_dir') . '/form/doctrine';
101
102       $isPluginModel = $this->isPluginModel($model);
103       if ($isPluginModel)
104       {
105         $pluginName = $this->getPluginNameForModel($model);
106         $baseDir .= '/' . $pluginName;
107       }
108
109       if (!is_dir($baseDir.'/base'))
110       {
111         mkdir($baseDir.'/base', 0777, true);
112       }
113
114       file_put_contents($baseDir.'/base/Base'.$model.'Form.class.php', $this->evalTemplate('sfDoctrineFormGeneratedTemplate.php'));
115       if ($isPluginModel)
116       {
117         $pluginBaseDir = $pluginPaths[$pluginName].'/lib/form/doctrine';
118         if (!file_exists($classFile = $pluginBaseDir.'/Plugin'.$model.'Form.class.php'))
119         {
120             if (!is_dir($pluginBaseDir))
121             {
122               mkdir($pluginBaseDir, 0777, true);
123             }
124             file_put_contents($classFile, $this->evalTemplate('sfDoctrineFormPluginTemplate.php'));
125         }
126       }
127       if (!file_exists($classFile = $baseDir.'/'.$model.'Form.class.php'))
128       {
129         if ($isPluginModel)
130         {
131            file_put_contents($classFile, $this->evalTemplate('sfDoctrinePluginFormTemplate.php'));
132         } else {
133            file_put_contents($classFile, $this->evalTemplate('sfDoctrineFormTemplate.php'));
134         }
135       }
136     }
137   }
138
139   /**
140    * Get all the models which are a part of a plugin and the name of the plugin.
141    * The array format is modelName => pluginName
142    *
143    * @todo This method is ugly and is a very weird way of finding the models which
144    *       belong to plugins. If we could come up with a better way that'd be great
145    * @return array $pluginModels
146    */
147   public function getPluginModels()
148   {
149     if (!$this->pluginModels)
150     {
151       $plugins     = $this->generatorManager->getConfiguration()->getPlugins();
152       $pluginPaths = $this->generatorManager->getConfiguration()->getAllPluginPaths();
153
154       foreach ($pluginPaths as $pluginName => $path)
155       {
156         if (!in_array($pluginName, $plugins))
157         {
158           continue;
159         }
160
161         foreach (sfFinder::type('file')->name('*.php')->in($path.'/lib/model/doctrine') as $path)
162         {
163           $info = pathinfo($path);
164           $e = explode('.', $info['filename']);
165           $modelName = substr($e[0], 6, strlen($e[0]));
166
167           if (class_exists($e[0]) && class_exists($modelName))
168           {
169             $parent = new ReflectionClass('Doctrine_Record');
170             $reflection = new ReflectionClass($modelName);
171             if ($reflection->isSubClassOf($parent))
172             {
173               $this->pluginModels[$modelName] = $pluginName;
174               $generators = Doctrine::getTable($modelName)->getGenerators();
175               foreach ($generators as $generator)
176               {
177                 $this->pluginModels[$generator->getOption('className')] = $pluginName;
178               }
179             }
180           }
181         }
182       }
183     }
184
185     return $this->pluginModels;
186   }
187
188   /**
189    * Check to see if a model is part of a plugin
190    *
191    * @param string $modelName
192    * @return boolean $bool
193    */
194   public function isPluginModel($modelName)
195   {
196     return isset($this->pluginModels[$modelName]) ? true:false;
197   }
198
199   /**
200    * Get the name of the plugin a model belongs to
201    *
202    * @param string $modelName
203    * @return string $pluginName
204    */
205   public function getPluginNameForModel($modelName)
206   {
207     if ($this->isPluginModel($modelName))
208     {
209       return $this->pluginModels[$modelName];
210     } else {
211       return false;
212     }
213   }
214
215   /**
216    * Returns an array of relations that represents a many to many relationship.
217    *
218    * A table is considered to be a m2m table if it has 2 foreign keys that are also primary keys.
219    *
220    * @return array An array of relations.
221    */
222   public function getManyToManyRelations()
223   {
224     $relations = array();
225     foreach ($this->table->getRelations() as $relation)
226     {
227       if ($relation->getType() === Doctrine_Relation::MANY && isset($relation['refTable']))
228       {
229         $relations[] = $relation;
230       }
231     }
232     return $relations;
233   }
234
235   /**
236    * Returns PHP names for all foreign keys of the current table.
237    *
238    * This method does not returns foreign keys that are also primary keys.
239    *
240    * @return array An array composed of:
241    *                 * The foreign table PHP name
242    *                 * The foreign key PHP name
243    *                 * A Boolean to indicate whether the column is required or not
244    *                 * A Boolean to indicate whether the column is a many to many relationship or not
245    */
246   public function getForeignKeyNames()
247   {
248     $names = array();
249     foreach ($this->table->getRelations() as $relation)
250     {
251       if ($relation->getType() === Doctrine_Relation::ONE)
252       {
253         $foreignDef = $relation->getTable()->getDefinitionOf($relation->getForeignFieldName());
254         $names[] = array($relation['table']->getOption('name'), $relation->getForeignFieldName(), $this->isColumnNotNull($relation->getForeignFieldName(), $foreignDef), false);
255       }
256     }
257
258     foreach ($this->getManyToManyRelations() as $relation)
259     {
260       $names[] = array($relation['table']->getOption('name'), $relation['alias'], false, true);
261     }
262
263     return $names;
264   }
265
266   /**
267    * Returns the first primary key column of the current table.
268    *
269    * @param ColumnMap A ColumnMap object
270    */
271   public function getPrimaryKey()
272   {
273     foreach ($this->getColumns() as $column)
274     {
275       if ($column->isPrimaryKey())
276       {
277         return $column;
278       }
279     }
280   }
281
282   /**
283    * Returns a sfWidgetForm class name for a given column.
284    *
285    * @param  sfDoctrineColumn $column
286    * @return string    The name of a subclass of sfWidgetForm
287    */
288   public function getWidgetClassForColumn($column)
289   {
290     switch ($column->getDoctrineType())
291     {
292       case 'string':
293         $widgetSubclass = is_null($column->getLength()) || $column->getLength() > 255 ? 'Textarea' : 'Input';
294         break;
295       case 'boolean':
296         $widgetSubclass = 'InputCheckbox';
297         break;
298       case 'blob':
299       case 'clob':
300         $widgetSubclass = 'Textarea';
301         break;
302       case 'date':
303         $widgetSubclass = 'Date';
304         break;
305       case 'time':
306         $widgetSubclass = 'Time';
307         break;
308       case 'timestamp':
309         $widgetSubclass = 'DateTime';
310         break;
311       case 'enum':
312         $widgetSubclass = 'Choice';
313         break;
314       default:
315         $widgetSubclass = 'Input';
316     }
317
318     if ($column->isPrimaryKey())
319     {
320       $widgetSubclass = 'InputHidden';
321     }
322     else if ($column->isForeignKey())
323     {
324       $widgetSubclass = 'DoctrineChoice';
325     }
326
327     return sprintf('sfWidgetForm%s', $widgetSubclass);
328   }
329
330   /**
331    * Returns a PHP string representing options to pass to a widget for a given column.
332    *
333    * @param  sfDoctrineColumn $column
334    * @return string    The options to pass to the widget as a PHP string
335    */
336   public function getWidgetOptionsForColumn($column)
337   {
338     $options = array();
339
340     if ($column->isForeignKey())
341     {
342       $options[] = sprintf('\'model\' => \'%s\', \'add_empty\' => %s', $column->getForeignTable()->getOption('name'), $column->isNotNull() ? 'false' : 'true');
343     }
344     else
345     {
346       switch ($column->getDoctrineType())
347       {
348         case 'enum':
349           $values = $column->getDefinitionKey('values');
350           $values = array_combine($values, $values);
351           $options[] = "'choices' => " . str_replace("\n", '', $this->arrayExport($values));
352           break;
353       }
354     }
355
356     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
357   }
358
359   /**
360    * Returns a sfValidator class name for a given column.
361    *
362    * @param sfDoctrineColumn $column
363    * @return string    The name of a subclass of sfValidator
364    */
365   public function getValidatorClassForColumn($column)
366   {
367     switch ($column->getDoctrineType())
368     {
369       case 'boolean':
370         $validatorSubclass = 'Boolean';
371         break;
372       case 'string':
373             if ($column->getDefinitionKey('email'))
374             {
375               $validatorSubclass = 'Email';
376             }
377             else if ($column->getDefinitionKey('regexp'))
378             {
379               $validatorSubclass = 'Regex';
380             }
381             else
382             {
383               $validatorSubclass = 'String';
384             }
385         break;
386       case 'clob':
387       case 'blob':
388         $validatorSubclass = 'String';
389         break;
390       case 'float':
391       case 'decimal':
392         $validatorSubclass = 'Number';
393         break;
394       case 'integer':
395         $validatorSubclass = 'Integer';
396         break;
397       case 'date':
398         $validatorSubclass = 'Date';
399         break;
400       case 'time':
401         $validatorSubclass = 'Time';
402         break;
403       case 'timestamp':
404         $validatorSubclass = 'DateTime';
405         break;
406       case 'enum':
407         $validatorSubclass = 'Choice';
408         break;
409       default:
410         $validatorSubclass = 'Pass';
411     }
412
413     if ($column->isPrimaryKey() || $column->isForeignKey())
414     {
415       $validatorSubclass = 'DoctrineChoice';
416     }
417
418     return sprintf('sfValidator%s', $validatorSubclass);
419   }
420
421   /**
422    * Returns a PHP string representing options to pass to a validator for a given column.
423    *
424    * @param sfDoctrineColumn $column
425    * @return string    The options to pass to the validator as a PHP string
426    */
427   public function getValidatorOptionsForColumn($column)
428   {
429     $options = array();
430
431     if ($column->isForeignKey())
432     {
433       $options[] = sprintf('\'model\' => \'%s\'', $column->getForeignTable()->getOption('name'));
434     }
435     else if ($column->isPrimaryKey())
436     {
437       $options[] = sprintf('\'model\' => \'%s\', \'column\' => \'%s\'', $this->modelName, $column->getName());
438     }
439     else
440     {
441       switch ($column->getDoctrineType())
442       {
443         case 'string':
444           if ($column['length'])
445           {
446             $options[] = sprintf('\'max_length\' => %s', $column['length']);
447           }
448           if (isset($column['minlength']))
449           {
450             $options[] = sprintf('\'min_length\' => %s', $column['minlength']);
451           }
452           if (isset($column['regexp']))
453           {
454             $options[] = sprintf('\'pattern\' => \'%s\'', $column['regexp']);
455           }
456           break;
457         case 'enum':
458           $values = array_combine($column['values'], $column['values']);
459           $options[] = "'choices' => " . str_replace("\n", '', $this->arrayExport($values));
460           break;
461       }
462     }
463
464     if (!$column->isNotNull() || $column->isPrimaryKey())
465     {
466       $options[] = '\'required\' => false';
467     }
468
469     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
470   }
471
472   /**
473    * Returns the maximum length for a column name.
474    *
475    * @return integer The length of the longer column name
476    */
477   public function getColumnNameMaxLength()
478   {
479     $max = 0;
480     foreach ($this->getColumns() as $column)
481     {
482       if (($m = strlen($column->getFieldName())) > $max)
483       {
484         $max = $m;
485       }
486     }
487
488     foreach ($this->getManyToManyRelations() as $relation)
489     {
490       if (($m = strlen($this->underscore($relation['alias']).'_list')) > $max)
491       {
492         $max = $m;
493       }
494     }
495
496     return $max;
497   }
498
499   /**
500    * Returns an array of primary key column names.
501    *
502    * @return array An array of primary key column names
503    */
504   public function getPrimaryKeyColumNames()
505   {
506     return $this->table->getIdentifierColumnNames();
507   }
508
509   /**
510    * Returns a PHP string representation for the array of all primary key column names.
511    *
512    * @return string A PHP string representation for the array of all primary key column names
513    *
514    * @see getPrimaryKeyColumNames()
515    */
516   public function getPrimaryKeyColumNamesAsString()
517   {
518     return sprintf('array(\'%s\')', implode('\', \'', $this->getPrimaryKeyColumNames()));
519   }
520
521   /**
522    * Returns true if the current table is internationalized.
523    *
524    * @return Boolean true if the current table is internationalized, false otherwise
525    */
526   public function isI18n()
527   {
528     return $this->table->hasRelation('Translation');
529   }
530
531   /**
532    * Returns the i18n model name for the current table.
533    *
534    * @return string The model class name
535    */
536   public function getI18nModel()
537   {
538     return $this->table->getRelation('Translation')->getTable()->create();
539   }
540
541   public function underscore($name)
542   {
543     return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), '\\1_\\2', $name));
544   }
545
546   /**
547    * Get array of sfDoctrineColumn objects
548    *
549    * @return array $columns
550    */
551   public function getColumns()
552   {
553     foreach (array_keys($this->table->getColumns()) as $name)
554     {
555       $columns[] = new sfDoctrineColumn($name, $this->table);
556     }
557
558     return $columns;
559   }
560
561   public function getUniqueColumnNames()
562   {
563     $uniqueColumns = array();
564
565     foreach ($this->getColumns() as $column)
566     {
567       if ($column->getDefinitionKey('unique'))
568       {
569         $uniqueColumns[] = array($column->getName());
570       }
571     }
572
573     $indexes = $this->table->getOption('indexes');
574     foreach ($indexes as $name => $index)
575     {
576       if (isset($index['type']) && $index['type'] == 'unique')
577       {
578         $tmp = $index['fields'];
579         if (is_array(array_shift($tmp)))
580         {
581           $uniqueColumns[] = array_keys($index['fields']);
582         } else {
583           $uniqueColumns[] = $index['fields'];
584         }
585       }
586     }
587
588     return $uniqueColumns;
589   }
590
591   /**
592    * Loads all Doctrine builders.
593    */
594   protected function loadModels()
595   {
596     Doctrine::loadModels($this->generatorManager->getConfiguration()->getModelDirs(),
597                                    Doctrine::MODEL_LOADING_CONSERVATIVE);
598     $models = Doctrine::getLoadedModels();
599     $models Doctrine::initializeModels($models);
600     $this->models = Doctrine::filterInvalidModels($models);
601     return $this->models;
602   }
603
604   /**
605    * Array export. Export array to formatted php code
606    *
607    * @param array $values
608    * @return string $php
609    */
610   protected function arrayExport($values)
611   {
612     $php = var_export($values, true);
613     $php = str_replace("\n", '', $php);
614     $php = str_replace('array (  ', 'array(', $php);
615     $php = str_replace(',)', ')', $php);
616     $php = str_replace('  ', ' ', $php);
617     return $php;
618   }
619 }
Note: See TracBrowser for help on using the browser.