Development

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

You must first sign up to be able to contribute.

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

Revision 29661, 14.9 kB (checked in by Kris.Wallsmith, 4 years ago)

[1.3, 1.4] changed generation of (non-foreign key) primary key form fields so validation will fail if the primary key is changed (closes #8639, refs #8704)

  • 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->dbMap = Propel::getDatabaseMap($this->params['connection']);
65
66     $this->loadBuilders();
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($directory = dirname($file)))
73       {
74         mkdir($directory, 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       $behaviors = $table->getBehaviors();
84       if (isset($behaviors['symfony']['form']) && 'false' === $behaviors['symfony']['form'])
85       {
86         continue;
87       }
88
89       $this->table = $table;
90
91       // find the package to store forms in the same directory as the model classes
92       $packages = explode('.', constant(constant($table->getClassname().'::PEER').'::CLASS_DEFAULT'));
93       array_pop($packages);
94       if (false === $pos = array_search($this->params['model_dir_name'], $packages))
95       {
96         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')));
97       }
98       $packages[$pos] = $this->params['form_dir_name'];
99       $baseDir = sfConfig::get('sf_root_dir').'/'.implode(DIRECTORY_SEPARATOR, $packages);
100
101       if (!is_dir($baseDir.'/base'))
102       {
103         mkdir($baseDir.'/base', 0777, true);
104       }
105
106       file_put_contents($baseDir.'/base/Base'.$table->getClassname().'Form.class.php', $this->evalTemplate('sfPropelFormGeneratedTemplate.php'));
107       if (!file_exists($classFile = $baseDir.'/'.$table->getClassname().'Form.class.php'))
108       {
109         file_put_contents($classFile, $this->evalTemplate('sfPropelFormTemplate.php'));
110       }
111     }
112   }
113
114   /**
115    * Returns an array of tables that represents a many to many relationship.
116    *
117    * A table is considered to be a m2m table if it has 2 foreign keys that are also primary keys.
118    *
119    * @return array An array of tables.
120    */
121   public function getManyToManyTables()
122   {
123     $tables = array();
124
125     // go through all tables to find m2m relationships
126     foreach ($this->dbMap->getTables() as $tableName => $table)
127     {
128       foreach ($table->getColumns() as $column)
129       {
130         if ($column->isForeignKey() && $column->isPrimaryKey() && $this->table->getClassname() == $this->getForeignTable($column)->getClassname())
131         {
132           // we have a m2m relationship
133           // find the other primary key
134           foreach ($table->getColumns() as $relatedColumn)
135           {
136             if ($relatedColumn->isForeignKey() && $relatedColumn->isPrimaryKey() && $this->table->getClassname() != $this->getForeignTable($relatedColumn)->getClassname())
137             {
138               // we have the related table
139               $tables[] = array(
140                 'middleTable'   => $table,
141                 'relatedTable'  => $this->getForeignTable($relatedColumn),
142                 'column'        => $column,
143                 'relatedColumn' => $relatedColumn,
144               );
145
146               break 2;
147             }
148           }
149         }
150       }
151     }
152
153     return $tables;
154   }
155
156   /**
157    * Returns PHP names for all foreign keys of the current table.
158    *
159    * This method does not returns foreign keys that are also primary keys.
160    *
161    * @return array An array composed of:
162    *                 * The foreign table PHP name
163    *                 * The foreign key PHP name
164    *                 * A Boolean to indicate whether the column is required or not
165    *                 * A Boolean to indicate whether the column is a many to many relationship or not
166    */
167   public function getForeignKeyNames()
168   {
169     $names = array();
170     foreach ($this->table->getColumns() as $column)
171     {
172       if (!$column->isPrimaryKey() && $column->isForeignKey())
173       {
174         $names[] = array($this->getForeignTable($column)->getClassname(), $column->getPhpName(), $column->isNotNull(), false);
175       }
176     }
177
178     foreach ($this->getManyToManyTables() as $tables)
179     {
180       $names[] = array($tables['relatedTable']->getClassname(), $tables['middleTable']->getClassname(), false, true);
181     }
182
183     return $names;
184   }
185
186   /**
187    * Returns the first primary key column of the current table.
188    *
189    * @return ColumnMap A ColumnMap object
190    */
191   public function getPrimaryKey()
192   {
193     foreach ($this->table->getColumns() as $column)
194     {
195       if ($column->isPrimaryKey())
196       {
197         return $column;
198       }
199     }
200   }
201
202   /**
203    * Returns the foreign table associated with a column.
204    *
205    * @param  ColumnMap $column A ColumnMap object
206    *
207    * @return TableMap  A TableMap object
208    */
209   public function getForeignTable(ColumnMap $column)
210   {
211     return $this->dbMap->getTable($column->getRelatedTableName());
212   }
213
214   /**
215    * Returns a sfWidgetForm class name for a given column.
216    *
217    * @param  ColumnMap  $column A ColumnMap object
218    *
219    * @return string    The name of a subclass of sfWidgetForm
220    */
221   public function getWidgetClassForColumn(ColumnMap $column)
222   {
223     switch ($column->getType())
224     {
225       case PropelColumnTypes::BOOLEAN:
226         $name = 'InputCheckbox';
227         break;
228       case PropelColumnTypes::CLOB:
229       case PropelColumnTypes::LONGVARCHAR:
230         $name = 'Textarea';
231         break;
232       case PropelColumnTypes::DATE:
233         $name = 'Date';
234         break;
235       case PropelColumnTypes::TIME:
236         $name = 'Time';
237         break;
238       case PropelColumnTypes::TIMESTAMP:
239         $name = 'DateTime';
240         break;
241       default:
242         $name = 'InputText';
243     }
244
245     if ($column->isPrimaryKey())
246     {
247       $name = 'InputHidden';
248     }
249     else if ($column->isForeignKey())
250     {
251       $name = 'PropelChoice';
252     }
253
254     return sprintf('sfWidgetForm%s', $name);
255   }
256
257   /**
258    * Returns a PHP string representing options to pass to a widget for a given column.
259    *
260    * @param  ColumnMap $column  A ColumnMap object
261    *
262    * @return string    The options to pass to the widget as a PHP string
263    */
264   public function getWidgetOptionsForColumn(ColumnMap $column)
265   {
266     $options = array();
267
268     if (!$column->isPrimaryKey() && $column->isForeignKey())
269     {
270       $options[] = sprintf('\'model\' => \'%s\', \'add_empty\' => %s', $this->getForeignTable($column)->getClassname(), $column->isNotNull() ? 'false' : 'true');
271
272       $refColumn = $this->getForeignTable($column)->getColumn($column->getRelatedColumnName());
273       if (!$refColumn->isPrimaryKey())
274       {
275         $options[] = sprintf('\'key_method\' => \'get%s\'', $refColumn->getPhpName());
276       }
277     }
278
279     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
280   }
281
282   /**
283    * Returns a sfValidator class name for a given column.
284    *
285    * @param  ColumnMap $column  A ColumnMap object
286    *
287    * @return string    The name of a subclass of sfValidator
288    */
289   public function getValidatorClassForColumn(ColumnMap $column)
290   {
291     switch ($column->getType())
292     {
293       case PropelColumnTypes::BOOLEAN:
294         $name = 'Boolean';
295         break;
296       case PropelColumnTypes::CLOB:
297       case PropelColumnTypes::CHAR:
298       case PropelColumnTypes::VARCHAR:
299       case PropelColumnTypes::LONGVARCHAR:
300         $name = 'String';
301         break;
302       case PropelColumnTypes::DOUBLE:
303       case PropelColumnTypes::FLOAT:
304       case PropelColumnTypes::NUMERIC:
305       case PropelColumnTypes::DECIMAL:
306       case PropelColumnTypes::REAL:
307         $name = 'Number';
308         break;
309       case PropelColumnTypes::INTEGER:
310       case PropelColumnTypes::SMALLINT:
311       case PropelColumnTypes::TINYINT:
312       case PropelColumnTypes::BIGINT:
313         $name = 'Integer';
314         break;
315       case PropelColumnTypes::DATE:
316         $name = 'Date';
317         break;
318       case PropelColumnTypes::TIME:
319         $name = 'Time';
320         break;
321       case PropelColumnTypes::TIMESTAMP:
322         $name = 'DateTime';
323         break;
324       default:
325         $name = 'Pass';
326     }
327
328     if ($column->isForeignKey())
329     {
330       $name = 'PropelChoice';
331     }
332     else if ($column->isPrimaryKey())
333     {
334       $name = 'Choice';
335     }
336
337     return sprintf('sfValidator%s', $name);
338   }
339
340   /**
341    * Returns a PHP string representing options to pass to a validator for a given column.
342    *
343    * @param  ColumnMap $column  A ColumnMap object
344    *
345    * @return string    The options to pass to the validator as a PHP string
346    */
347   public function getValidatorOptionsForColumn(ColumnMap $column)
348   {
349     $options = array();
350
351     if ($column->isForeignKey())
352     {
353       $options[] = sprintf('\'model\' => \'%s\', \'column\' => \'%s\'', $this->getForeignTable($column)->getClassname(), $this->translateColumnName($column, true));
354     }
355     else if ($column->isPrimaryKey())
356     {
357       $options[] = sprintf('\'choices\' => array($this->getObject()->get%s()), \'empty_value\' => $this->getObject()->get%1$s()', $this->translateColumnName($column, false, BasePeer::TYPE_PHPNAME));
358     }
359     else
360     {
361       switch ($column->getType())
362       {
363         case PropelColumnTypes::CLOB:
364         case PropelColumnTypes::CHAR:
365         case PropelColumnTypes::VARCHAR:
366         case PropelColumnTypes::LONGVARCHAR:
367           if ($column->getSize())
368           {
369             $options[] = sprintf('\'max_length\' => %s', $column->getSize());
370           }
371           break;
372
373        case PropelColumnTypes::TINYINT:
374          $options[] = sprintf('\'min\' => %s, \'max\' => %s', -128, 127);
375          break;
376
377        case PropelColumnTypes::SMALLINT:
378          $options[] = sprintf('\'min\' => %s, \'max\' => %s', -32768, 32767);
379          break;
380
381        case PropelColumnTypes::INTEGER:
382          $options[] = sprintf('\'min\' => %s, \'max\' => %s', -2147483648, 2147483647);
383          break;
384
385        case PropelColumnTypes::BIGINT:
386          $options[] = sprintf('\'min\' => %s, \'max\' => %s', -9223372036854775808, 9223372036854775807);
387          break;
388       }
389     }
390
391     if (!$column->isNotNull() || $column->isPrimaryKey())
392     {
393       $options[] = '\'required\' => false';
394     }
395
396     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
397   }
398
399   /**
400    * Returns the maximum length for a column name.
401    *
402    * @return integer The length of the longer column name
403    */
404   public function getColumnNameMaxLength()
405   {
406     $max = 0;
407     foreach ($this->table->getColumns() as $column)
408     {
409       if (($m = strlen($column->getName())) > $max)
410       {
411         $max = $m;
412       }
413     }
414
415     foreach ($this->getManyToManyTables() as $tables)
416     {
417       if (($m = strlen($this->underscore($tables['middleTable']->getClassname()).'_list')) > $max)
418       {
419         $max = $m;
420       }
421     }
422
423     return $max;
424   }
425
426   /**
427    * Returns an array of primary key column names.
428    *
429    * @return array An array of primary key column names
430    */
431   public function getPrimaryKeyColumNames()
432   {
433     $pks = array();
434     foreach ($this->table->getColumns() as $column)
435     {
436       if ($column->isPrimaryKey())
437       {
438         $pks[] = $this->translateColumnName($column);
439       }
440     }
441
442     return $pks;
443   }
444
445   /**
446    * Returns a PHP string representation for the array of all primary key column names.
447    *
448    * @return string A PHP string representation for the array of all primary key column names
449    *
450    * @see getPrimaryKeyColumNames()
451    */
452   public function getPrimaryKeyColumNamesAsString()
453   {
454     return sprintf('array(\'%s\')', implode('\', \'', $this->getPrimaryKeyColumNames()));
455   }
456
457   /**
458    * Returns true if the current table is internationalized.
459    *
460    * @return Boolean true if the current table is internationalized, false otherwise
461    */
462   public function isI18n()
463   {
464     return method_exists(constant($this->table->getClassname().'::PEER'), 'getI18nModel');
465   }
466
467   /**
468    * Returns the i18n model name for the current table.
469    *
470    * @return string The model class name
471    */
472   public function getI18nModel()
473   {
474     return call_user_func(array(constant($this->table->getClassname().'::PEER'), 'getI18nModel'));
475   }
476
477   public function underscore($name)
478   {
479     return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), '\\1_\\2', $name));
480   }
481
482   public function getUniqueColumnNames()
483   {
484     $uniqueColumns = array();
485
486     foreach (call_user_func(array(constant($this->table->getClassname().'::PEER'), 'getUniqueColumnNames')) as $unique)
487     {
488       $uniqueColumn = array();
489       foreach ($unique as $column)
490       {
491         $uniqueColumn[] = $this->translateColumnName($this->table->getColumn($column));
492       }
493
494       $uniqueColumns[] = $uniqueColumn;
495     }
496
497     return $uniqueColumns;
498   }
499
500   public function translateColumnName($column, $related = false, $to = BasePeer::TYPE_FIELDNAME)
501   {
502     $peer = $related ? constant($column->getTable()->getDatabaseMap()->getTable($column->getRelatedTableName())->getPhpName().'::PEER') : constant($column->getTable()->getPhpName().'::PEER');
503     $field = $related ? $column->getRelatedName() : $column->getFullyQualifiedName();
504
505     return call_user_func(array($peer, 'translateFieldName'), $field, BasePeer::TYPE_COLNAME, $to);
506   }
507
508   /**
509    * Loads all Propel builders.
510    */
511   protected function loadBuilders()
512   {
513     $this->dbMap = Propel::getDatabaseMap($this->params['connection']);
514     $classes = sfFinder::type('file')->name('*TableMap.php')->in($this->generatorManager->getConfiguration()->getModelDirs());
515     foreach ($classes as $class)
516     {
517       $omClass = basename($class, 'TableMap.php');
518       if (class_exists($omClass) && is_subclass_of($omClass, 'BaseObject'))
519       {
520         $tableMapClass = basename($class, '.php');
521         $this->dbMap->addTableFromMapClass($tableMapClass);
522       }
523     }
524   }
525 }
526
527
Note: See TracBrowser for help on using the browser.