Development

/branches/1.2/lib/plugins/sfPropelPlugin/lib/generator/sfPropelFormGenerator.class.php

You must first sign up to be able to contribute.

root/branches/1.2/lib/plugins/sfPropelPlugin/lib/generator/sfPropelFormGenerator.class.php

Revision 16976, 14.1 kB (checked in by fabien, 6 years ago)

fixed Propel forms, filters, and admin generator when column names contain uppercase letters (closes #6151, #6187, #5030, #5610, #2855)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * Propel form generator.
13  *
14  * This class generates a Propel forms.
15  *
16  * @package    symfony
17  * @subpackage generator
18  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
19  * @version    SVN: $Id$
20  */
21 class sfPropelFormGenerator extends sfGenerator
22 {
23   protected
24     $dbMap = null;
25
26   /**
27    * Initializes the current sfGenerator instance.
28    *
29    * @param sfGeneratorManager $generatorManager A sfGeneratorManager instance
30    */
31   public function initialize(sfGeneratorManager $generatorManager)
32   {
33     parent::initialize($generatorManager);
34
35     $this->setGeneratorClass('sfPropelForm');
36   }
37
38   /**
39    * Generates classes and templates in cache.
40    *
41    * @param array $params The parameters
42    *
43    * @return string The data to put in configuration cache
44    */
45   public function generate($params = array())
46   {
47     $this->params = $params;
48
49     if (!isset($this->params['connection']))
50     {
51       throw new sfParseException('You must specify a "connection" parameter.');
52     }
53
54     if (!isset($this->params['model_dir_name']))
55     {
56       $this->params['model_dir_name'] = 'model';
57     }
58
59     if (!isset($this->params['form_dir_name']))
60     {
61       $this->params['form_dir_name'] = 'form';
62     }
63
64     $this->loadBuilders();
65
66     $this->dbMap = Propel::getDatabaseMap($this->params['connection']);
67
68     // create the project base class for all forms
69     $file = sfConfig::get('sf_lib_dir').'/form/BaseFormPropel.class.php';
70     if (!file_exists($file))
71     {
72       if (!is_dir(sfConfig::get('sf_lib_dir').'/form/base'))
73       {
74         mkdir(sfConfig::get('sf_lib_dir').'/form/base', 0777, true);
75       }
76
77       file_put_contents($file, $this->evalTemplate('sfPropelFormBaseTemplate.php'));
78     }
79
80     // create a form class for every Propel class
81     foreach ($this->dbMap->getTables() as $tableName => $table)
82     {
83       $this->table = $table;
84
85       // find the package to store forms in the same directory as the model classes
86       $packages = explode('.', constant(constant($table->getClassname().'::PEER').'::CLASS_DEFAULT'));
87       array_pop($packages);
88       if (false === $pos = array_search($this->params['model_dir_name'], $packages))
89       {
90         throw new InvalidArgumentException(sprintf('Unable to find the model dir name (%s) in the package %s.', $this->params['model_dir_name'], constant(constant($table->getClassname().'::PEER').'::CLASS_DEFAULT')));
91       }
92       $packages[$pos] = $this->params['form_dir_name'];
93       $baseDir = sfConfig::get('sf_root_dir').'/'.implode(DIRECTORY_SEPARATOR, $packages);
94
95       if (!is_dir($baseDir.'/base'))
96       {
97         mkdir($baseDir.'/base', 0777, true);
98       }
99
100       file_put_contents($baseDir.'/base/Base'.$table->getClassname().'Form.class.php', $this->evalTemplate('sfPropelFormGeneratedTemplate.php'));
101       if (!file_exists($classFile = $baseDir.'/'.$table->getClassname().'Form.class.php'))
102       {
103         file_put_contents($classFile, $this->evalTemplate('sfPropelFormTemplate.php'));
104       }
105     }
106   }
107
108   /**
109    * Returns an array of tables that represents a many to many relationship.
110    *
111    * A table is considered to be a m2m table if it has 2 foreign keys that are also primary keys.
112    *
113    * @return array An array of tables.
114    */
115   public function getManyToManyTables()
116   {
117     $tables = array();
118
119     // go through all tables to find m2m relationships
120     foreach ($this->dbMap->getTables() as $tableName => $table)
121     {
122       foreach ($table->getColumns() as $column)
123       {
124         if ($column->isForeignKey() && $column->isPrimaryKey() && $this->table->getClassname() == $this->getForeignTable($column)->getClassname())
125         {
126           // we have a m2m relationship
127           // find the other primary key
128           foreach ($table->getColumns() as $relatedColumn)
129           {
130             if ($relatedColumn->isForeignKey() && $relatedColumn->isPrimaryKey() && $this->table->getClassname() != $this->getForeignTable($relatedColumn)->getClassname())
131             {
132               // we have the related table
133               $tables[] = array(
134                 'middleTable'   => $table,
135                 'relatedTable'  => $this->getForeignTable($relatedColumn),
136                 'column'        => $column,
137                 'relatedColumn' => $relatedColumn,
138               );
139
140               break 2;
141             }
142           }
143         }
144       }
145     }
146
147     return $tables;
148   }
149
150   /**
151    * Returns PHP names for all foreign keys of the current table.
152    *
153    * This method does not returns foreign keys that are also primary keys.
154    *
155    * @return array An array composed of:
156    *                 * The foreign table PHP name
157    *                 * The foreign key PHP name
158    *                 * A Boolean to indicate whether the column is required or not
159    *                 * A Boolean to indicate whether the column is a many to many relationship or not
160    */
161   public function getForeignKeyNames()
162   {
163     $names = array();
164     foreach ($this->table->getColumns() as $column)
165     {
166       if (!$column->isPrimaryKey() && $column->isForeignKey())
167       {
168         $names[] = array($this->getForeignTable($column)->getClassname(), $column->getPhpName(), $column->isNotNull(), false);
169       }
170     }
171
172     foreach ($this->getManyToManyTables() as $tables)
173     {
174       $names[] = array($tables['relatedTable']->getClassname(), $tables['middleTable']->getClassname(), false, true);
175     }
176
177     return $names;
178   }
179
180   /**
181    * Returns the first primary key column of the current table.
182    *
183    * @return ColumnMap A ColumnMap object
184    */
185   public function getPrimaryKey()
186   {
187     foreach ($this->table->getColumns() as $column)
188     {
189       if ($column->isPrimaryKey())
190       {
191         return $column;
192       }
193     }
194   }
195
196   /**
197    * Returns the foreign table associated with a column.
198    *
199    * @param  ColumnMap $column A ColumnMap object
200    *
201    * @return TableMap  A TableMap object
202    */
203   public function getForeignTable(ColumnMap $column)
204   {
205     return $this->dbMap->getTable($column->getRelatedTableName());
206   }
207
208   /**
209    * Returns a sfWidgetForm class name for a given column.
210    *
211    * @param  ColumnMap  $column A ColumnMap object
212    *
213    * @return string    The name of a subclass of sfWidgetForm
214    */
215   public function getWidgetClassForColumn(ColumnMap $column)
216   {
217     switch ($column->getType())
218     {
219       case PropelColumnTypes::BOOLEAN:
220         $name = 'InputCheckbox';
221         break;
222       case PropelColumnTypes::CLOB:
223       case PropelColumnTypes::LONGVARCHAR:
224         $name = 'Textarea';
225         break;
226       case PropelColumnTypes::DATE:
227         $name = 'Date';
228         break;
229       case PropelColumnTypes::TIME:
230         $name = 'Time';
231         break;
232       case PropelColumnTypes::TIMESTAMP:
233         $name = 'DateTime';
234         break;
235       default:
236         $name = 'Input';
237     }
238
239     if ($column->isPrimaryKey())
240     {
241       $name = 'InputHidden';
242     }
243     else if ($column->isForeignKey())
244     {
245       $name = 'PropelChoice';
246     }
247
248     return sprintf('sfWidgetForm%s', $name);
249   }
250
251   /**
252    * Returns a PHP string representing options to pass to a widget for a given column.
253    *
254    * @param  ColumnMap $column  A ColumnMap object
255    *
256    * @return string    The options to pass to the widget as a PHP string
257    */
258   public function getWidgetOptionsForColumn(ColumnMap $column)
259   {
260     $options = array();
261
262     if (!$column->isPrimaryKey() && $column->isForeignKey())
263     {
264       $options[] = sprintf('\'model\' => \'%s\', \'add_empty\' => %s', $this->getForeignTable($column)->getClassname(), $column->isNotNull() ? 'false' : 'true');
265
266       $refColumn = $this->getForeignTable($column)->getColumn($column->getRelatedColumnName());
267       if (!$refColumn->isPrimaryKey())
268       {
269         $options[] = sprintf('\'key_method\' => \'get%s\'', $refColumn->getPhpName());
270       }
271     }
272
273     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
274   }
275
276   /**
277    * Returns a sfValidator class name for a given column.
278    *
279    * @param  ColumnMap $column  A ColumnMap object
280    *
281    * @return string    The name of a subclass of sfValidator
282    */
283   public function getValidatorClassForColumn(ColumnMap $column)
284   {
285     switch ($column->getType())
286     {
287       case PropelColumnTypes::BOOLEAN:
288         $name = 'Boolean';
289         break;
290       case PropelColumnTypes::CLOB:
291       case PropelColumnTypes::CHAR:
292       case PropelColumnTypes::VARCHAR:
293       case PropelColumnTypes::LONGVARCHAR:
294         $name = 'String';
295         break;
296       case PropelColumnTypes::DOUBLE:
297       case PropelColumnTypes::FLOAT:
298       case PropelColumnTypes::NUMERIC:
299       case PropelColumnTypes::DECIMAL:
300       case PropelColumnTypes::REAL:
301         $name = 'Number';
302         break;
303       case PropelColumnTypes::INTEGER:
304       case PropelColumnTypes::SMALLINT:
305       case PropelColumnTypes::TINYINT:
306       case PropelColumnTypes::BIGINT:
307         $name = 'Integer';
308         break;
309       case PropelColumnTypes::DATE:
310         $name = 'Date';
311         break;
312       case PropelColumnTypes::TIME:
313         $name = 'Time';
314         break;
315       case PropelColumnTypes::TIMESTAMP:
316         $name = 'DateTime';
317         break;
318       default:
319         $name = 'Pass';
320     }
321
322     if ($column->isPrimaryKey() || $column->isForeignKey())
323     {
324       $name = 'PropelChoice';
325     }
326
327     return sprintf('sfValidator%s', $name);
328   }
329
330   /**
331    * Returns a PHP string representing options to pass to a validator for a given column.
332    *
333    * @param  ColumnMap $column  A ColumnMap object
334    *
335    * @return string    The options to pass to the validator as a PHP string
336    */
337   public function getValidatorOptionsForColumn(ColumnMap $column)
338   {
339     $options = array();
340
341     if ($column->isForeignKey())
342     {
343       $options[] = sprintf('\'model\' => \'%s\', \'column\' => \'%s\'', $this->getForeignTable($column)->getClassname(), $this->translateColumnName($column, true));
344     }
345     else if ($column->isPrimaryKey())
346     {
347       $options[] = sprintf('\'model\' => \'%s\', \'column\' => \'%s\'', $column->getTable()->getClassname(), $this->translateColumnName($column));
348     }
349     else
350     {
351       switch ($column->getType())
352       {
353         case PropelColumnTypes::CLOB:
354         case PropelColumnTypes::CHAR:
355         case PropelColumnTypes::VARCHAR:
356         case PropelColumnTypes::LONGVARCHAR:
357           if ($column->getSize())
358           {
359             $options[] = sprintf('\'max_length\' => %s', $column->getSize());
360           }
361           break;
362       }
363     }
364
365     if (!$column->isNotNull() || $column->isPrimaryKey())
366     {
367       $options[] = '\'required\' => false';
368     }
369
370     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
371   }
372
373   /**
374    * Returns the maximum length for a column name.
375    *
376    * @return integer The length of the longer column name
377    */
378   public function getColumnNameMaxLength()
379   {
380     $max = 0;
381     foreach ($this->table->getColumns() as $column)
382     {
383       if (($m = strlen($column->getName())) > $max)
384       {
385         $max = $m;
386       }
387     }
388
389     foreach ($this->getManyToManyTables() as $tables)
390     {
391       if (($m = strlen($this->underscore($tables['middleTable']->getClassname()).'_list')) > $max)
392       {
393         $max = $m;
394       }
395     }
396
397     return $max;
398   }
399
400   /**
401    * Returns an array of primary key column names.
402    *
403    * @return array An array of primary key column names
404    */
405   public function getPrimaryKeyColumNames()
406   {
407     $pks = array();
408     foreach ($this->table->getColumns() as $column)
409     {
410       if ($column->isPrimaryKey())
411       {
412         $pks[] = $this->translateColumnName($column);
413       }
414     }
415
416     return $pks;
417   }
418
419   /**
420    * Returns a PHP string representation for the array of all primary key column names.
421    *
422    * @return string A PHP string representation for the array of all primary key column names
423    *
424    * @see getPrimaryKeyColumNames()
425    */
426   public function getPrimaryKeyColumNamesAsString()
427   {
428     return sprintf('array(\'%s\')', implode('\', \'', $this->getPrimaryKeyColumNames()));
429   }
430
431   /**
432    * Returns true if the current table is internationalized.
433    *
434    * @return Boolean true if the current table is internationalized, false otherwise
435    */
436   public function isI18n()
437   {
438     return method_exists(constant($this->table->getClassname().'::PEER'), 'getI18nModel');
439   }
440
441   /**
442    * Returns the i18n model name for the current table.
443    *
444    * @return string The model class name
445    */
446   public function getI18nModel()
447   {
448     return call_user_func(array(constant($this->table->getClassname().'::PEER'), 'getI18nModel'));
449   }
450
451   public function underscore($name)
452   {
453     return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), '\\1_\\2', $name));
454   }
455
456   public function getUniqueColumnNames()
457   {
458     $uniqueColumns = array();
459
460     foreach (call_user_func(array(constant($this->table->getClassname().'::PEER'), 'getUniqueColumnNames')) as $unique)
461     {
462       $uniqueColumn = array();
463       foreach ($unique as $column)
464       {
465         $uniqueColumn[] = $this->translateColumnName($this->table->getColumn($column));
466       }
467
468       $uniqueColumns[] = $uniqueColumn;
469     }
470
471     return $uniqueColumns;
472   }
473
474   public function translateColumnName($column, $related = false, $to = BasePeer::TYPE_FIELDNAME)
475   {
476     $peer = $related ? constant($column->getTable()->getDatabaseMap()->getTable($column->getRelatedTableName())->getPhpName().'::PEER') : constant($column->getTable()->getPhpName().'::PEER');
477     $field = $related ? $column->getRelatedName() : $column->getFullyQualifiedName();
478
479     return call_user_func(array($peer, 'translateFieldName'), $field, BasePeer::TYPE_COLNAME, $to);
480   }
481
482   /**
483    * Loads all Propel builders.
484    */
485   protected function loadBuilders()
486   {
487     $classes = sfFinder::type('file')->name('*MapBuilder.php')->in($this->generatorManager->getConfiguration()->getModelDirs());
488     foreach ($classes as $class)
489     {
490       $omClass = basename($class, 'MapBuilder.php');
491       if (class_exists($omClass) && is_subclass_of($omClass, 'BaseObject'))
492       {
493         $class = basename($class, '.php');
494         $map = new $class();
495         if (!$map->isBuilt())
496         {
497           $map->doBuild();
498         }
499       }
500     }
501   }
502 }
503
504
Note: See TracBrowser for help on using the browser.