Development

/branches/1.1/lib/plugins/sfPropelPlugin/lib/propel/generator/sfPropelFormGenerator.class.php

You must first sign up to be able to contribute.

root/branches/1.1/lib/plugins/sfPropelPlugin/lib/propel/generator/sfPropelFormGenerator.class.php

Revision 14728, 13.0 kB (checked in by Kris.Wallsmith, 5 years ago)

[1.1, 1.2, 1.3] fixed foreign keys are assumed with column name "ID" in auto generation of forms (closes #5370)

  • 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'))
73       {
74         mkdir(sfConfig::get('sf_lib_dir').'/form', 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($table->getPhpName().'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($table->getPhpName().'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->getPhpName().'Form.class.php', $this->evalTemplate('sfPropelFormGeneratedTemplate.php'));
101       if (!file_exists($classFile = $baseDir.'/'.$table->getPhpName().'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->getPhpName() == $this->getForeignTable($column)->getPhpName())
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->getPhpName() != $this->getForeignTable($relatedColumn)->getPhpName())
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)->getPhpName(), $column->getPhpName(), $column->isNotNull(), false);
169       }
170     }
171
172     foreach ($this->getManyToManyTables() as $tables)
173     {
174       $names[] = array($tables['relatedTable']->getPhpName(), $tables['middleTable']->getPhpName(), 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->getCreoleType())
218     {
219       case CreoleTypes::BOOLEAN:
220         $name = 'InputCheckbox';
221         break;
222       case CreoleTypes::LONGVARCHAR:
223         $name = 'Textarea';
224         break;
225       case CreoleTypes::DATE:
226         $name = 'Date';
227         break;
228       case CreoleTypes::TIME:
229         $name = 'Time';
230         break;
231       case CreoleTypes::TIMESTAMP:
232         $name = 'DateTime';
233         break;
234       default:
235         $name = 'Input';
236     }
237
238     if ($column->isPrimaryKey())
239     {
240       $name = 'InputHidden';
241     }
242     else if ($column->isForeignKey())
243     {
244       $name = 'PropelSelect';
245     }
246
247     return sprintf('sfWidgetForm%s', $name);
248   }
249
250   /**
251    * Returns a PHP string representing options to pass to a widget for a given column.
252    *
253    * @param  ColumnMap $column  A ColumnMap object
254    *
255    * @return string    The options to pass to the widget as a PHP string
256    */
257   public function getWidgetOptionsForColumn(ColumnMap $column)
258   {
259     $options = array();
260
261     if (!$column->isPrimaryKey() && $column->isForeignKey())
262     {
263       $options[] = sprintf('\'model\' => \'%s\', \'add_empty\' => %s', $this->getForeignTable($column)->getPhpName(), $column->isNotNull() ? 'false' : 'true');
264     }
265
266     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
267   }
268
269   /**
270    * Returns a sfValidator class name for a given column.
271    *
272    * @param  ColumnMap $column  A ColumnMap object
273    *
274    * @return string    The name of a subclass of sfValidator
275    */
276   public function getValidatorClassForColumn(ColumnMap $column)
277   {
278     switch ($column->getCreoleType())
279     {
280       case CreoleTypes::BOOLEAN:
281         $name = 'Boolean';
282         break;
283       case CreoleTypes::CHAR:
284       case CreoleTypes::VARCHAR:
285       case CreoleTypes::LONGVARCHAR:
286         $name = 'String';
287         break;
288       case CreoleTypes::DOUBLE:
289       case CreoleTypes::FLOAT:
290       case CreoleTypes::NUMERIC:
291       case CreoleTypes::DECIMAL:
292       case CreoleTypes::REAL:
293         $name = 'Number';
294         break;
295       case CreoleTypes::INTEGER:
296       case CreoleTypes::SMALLINT:
297       case CreoleTypes::TINYINT:
298       case CreoleTypes::BIGINT:
299       case CreoleTypes::YEAR:
300         $name = 'Integer';
301         break;
302       case CreoleTypes::DATE:
303         $name = 'Date';
304         break;
305       case CreoleTypes::TIME:
306         $name = 'Time';
307         break;
308       case CreoleTypes::TIMESTAMP:
309         $name = 'DateTime';
310         break;
311       default:
312         $name = 'Pass';
313     }
314
315     if ($column->isPrimaryKey() || $column->isForeignKey())
316     {
317       $name = 'PropelChoice';
318     }
319
320     return sprintf('sfValidator%s', $name);
321   }
322
323   /**
324    * Returns a PHP string representing options to pass to a validator for a given column.
325    *
326    * @param  ColumnMap $column  A ColumnMap object
327    *
328    * @return string    The options to pass to the validator as a PHP string
329    */
330   public function getValidatorOptionsForColumn(ColumnMap $column)
331   {
332     $options = array();
333
334     if ($column->isForeignKey())
335     {
336       $options[] = sprintf('\'model\' => \'%s\', \'column\' => \'%s\'', $this->getForeignTable($column)->getPhpName(), strtolower($column->getRelatedColumnName()));
337     }
338     else if ($column->isPrimaryKey())
339     {
340       $options[] = sprintf('\'model\' => \'%s\', \'column\' => \'%s\'', $column->getTable()->getPhpName(), strtolower($column->getColumnName()));
341     }
342     else
343     {
344       switch ($column->getCreoleType())
345       {
346         case CreoleTypes::CHAR:
347         case CreoleTypes::VARCHAR:
348         case CreoleTypes::LONGVARCHAR:
349           if ($column->getSize())
350           {
351             $options[] = sprintf('\'max_length\' => %s', $column->getSize());
352           }
353           break;
354       }
355     }
356
357     if (!$column->isNotNull() || $column->isPrimaryKey())
358     {
359       $options[] = '\'required\' => false';
360     }
361
362     return count($options) ? sprintf('array(%s)', implode(', ', $options)) : '';
363   }
364
365   /**
366    * Returns the maximum length for a column name.
367    *
368    * @return integer The length of the longer column name
369    */
370   public function getColumnNameMaxLength()
371   {
372     $max = 0;
373     foreach ($this->table->getColumns() as $column)
374     {
375       if (($m = strlen($column->getColumnName())) > $max)
376       {
377         $max = $m;
378       }
379     }
380
381     foreach ($this->getManyToManyTables() as $tables)
382     {
383       if (($m = strlen($this->underscore($tables['middleTable']->getPhpName()).'_list')) > $max)
384       {
385         $max = $m;
386       }
387     }
388
389     return $max;
390   }
391
392   /**
393    * Returns an array of primary key column names.
394    *
395    * @return array An array of primary key column names
396    */
397   public function getPrimaryKeyColumNames()
398   {
399     $pks = array();
400     foreach ($this->table->getColumns() as $column)
401     {
402       if ($column->isPrimaryKey())
403       {
404         $pks[] = strtolower($column->getColumnName());
405       }
406     }
407
408     return $pks;
409   }
410
411   /**
412    * Returns a PHP string representation for the array of all primary key column names.
413    *
414    * @return string A PHP string representation for the array of all primary key column names
415    *
416    * @see getPrimaryKeyColumNames()
417    */
418   public function getPrimaryKeyColumNamesAsString()
419   {
420     return sprintf('array(\'%s\')', implode('\', \'', $this->getPrimaryKeyColumNames()));
421   }
422
423   /**
424    * Returns true if the current table is internationalized.
425    *
426    * @return Boolean true if the current table is internationalized, false otherwise
427    */
428   public function isI18n()
429   {
430     return method_exists($this->table->getPhpName().'Peer', 'getI18nModel');
431   }
432
433   /**
434    * Returns the i18n model name for the current table.
435    *
436    * @return string The model class name
437    */
438   public function getI18nModel()
439   {
440     return call_user_func(array($this->table->getPhpName().'Peer', 'getI18nModel'));
441   }
442
443   public function underscore($name)
444   {
445     return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), '\\1_\\2', $name));
446   }
447
448   public function getUniqueColumnNames()
449   {
450     $uniqueColumns = array();
451
452     foreach (call_user_func(array($this->table->getPhpName().'Peer', 'getUniqueColumnNames')) as $unique)
453     {
454       $uniqueColumn = array();
455       foreach ($unique as $column)
456       {
457         $uniqueColumn[] = strtolower($this->table->getColumn($column)->getColumnName());
458       }
459
460       $uniqueColumns[] = $uniqueColumn;
461     }
462
463     return $uniqueColumns;
464   }
465
466   /**
467    * Loads all Propel builders.
468    */
469   protected function loadBuilders()
470   {
471     $classes = sfFinder::type('file')->name('*MapBuilder.php')->in($this->generatorManager->getConfiguration()->getModelDirs());
472     foreach ($classes as $class)
473     {
474       $class = basename($class, '.php');
475       $map = new $class();
476       if (!$map->isBuilt())
477       {
478         $map->doBuild();
479       }
480     }
481   }
482 }
483
Note: See TracBrowser for help on using the browser.