Development

/branches/1.2/lib/plugins/sfPropelPlugin/lib/form/sfFormPropel.class.php

You must first sign up to be able to contribute.

root/branches/1.2/lib/plugins/sfPropelPlugin/lib/form/sfFormPropel.class.php

Revision 16007, 13.3 kB (checked in by Kris.Wallsmith, 6 years ago)

[1.2, 1.3] ported r15242 to propel plugin

  • 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 /**
13  * sfFormPropel is the base class for forms based on Propel objects.
14  *
15  * @package    symfony
16  * @subpackage form
17  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18  * @version    SVN: $Id$
19  */
20 abstract class sfFormPropel extends sfForm
21 {
22   protected
23     $isNew  = true,
24     $object = null;
25
26   /**
27    * Constructor.
28    *
29    * @param BaseObject $object      A Propel object used to initialize default values
30    * @param array      $options     An array of options
31    * @param string     $CSRFSecret  A CSRF secret (false to disable CSRF protection, null to use the global CSRF secret)
32    *
33    * @see sfForm
34    */
35   public function __construct(BaseObject $object = null, $options = array(), $CSRFSecret = null)
36   {
37     $class = $this->getModelName();
38     if (is_null($object))
39     {
40       $this->object = new $class();
41     }
42     else
43     {
44       if (!$object instanceof $class)
45       {
46         throw new sfException(sprintf('The "%s" form only accepts a "%s" object.', get_class($this), $class));
47       }
48
49       $this->object = $object;
50       $this->isNew = $this->object->isNew();
51     }
52
53     parent::__construct(array(), $options, $CSRFSecret);
54
55     $this->updateDefaultsFromObject();
56   }
57
58   /**
59    * Returns the default connection for the current model.
60    *
61    * @return PropelPDO A database connection
62    */
63   public function getConnection()
64   {
65     return Propel::getConnection(constant(sprintf('%s::DATABASE_NAME', get_class($this->object->getPeer()))));
66   }
67
68   /**
69    * Returns the current model name.
70    */
71   abstract public function getModelName();
72
73   /**
74    * Returns true if the current form embeds a new object.
75    *
76    * @return Boolean true if the current form embeds a new object, false otherwise
77    */
78   public function isNew()
79   {
80     return $this->isNew;
81   }
82
83   /**
84    * Embeds i18n objects into the current form.
85    *
86    * @param array   $cultures   An array of cultures
87    * @param string  $decorator  A HTML decorator for the embedded form
88    */
89   public function embedI18n($cultures, $decorator = null)
90   {
91     if (!$this->isI18n())
92     {
93       throw new sfException(sprintf('The model "%s" is not internationalized.', $this->getModelName()));
94     }
95
96     $class = $this->getI18nFormClass();
97     foreach ($cultures as $culture)
98     {
99       $method = sprintf('getCurrent%s', $this->getI18nModelName($culture));
100       $i18nObject = $this->object->$method($culture);
101       $i18n = new $class($i18nObject);
102       unset($i18n['id'], $i18n['culture']);
103
104       $this->embedForm($culture, $i18n, $decorator);
105     }
106   }
107
108   /**
109    * Returns the current object for this form.
110    *
111    * @return BaseObject The current object.
112    */
113   public function getObject()
114   {
115     return $this->object;
116   }
117
118   /**
119    * Binds the current form and save the to the database in one step.
120    *
121    * @param  array      $taintedValues    An array of tainted values to use to bind the form
122    * @param  array      $taintedFiles     An array of uploaded files (in the $_FILES or $_GET format)
123    * @param  PropelPDO  $con              An optional PropelPDO object
124    *
125    * @return Boolean    true if the form is valid, false otherwise
126    */
127   public function bindAndSave($taintedValues, $taintedFiles = null, $con = null)
128   {
129     $this->bind($taintedValues, $taintedFiles);
130     if ($this->isValid())
131     {
132       $this->save($con);
133
134       return true;
135     }
136
137     return false;
138   }
139
140   /**
141    * Saves the current object to the database.
142    *
143    * The object saving is done in a transaction and handled by the doSave() method.
144    *
145    * If the form is not valid, it throws an sfValidatorError.
146    *
147    * @param PropelPDO $con An optional PropelPDO object
148    *
149    * @return BaseObject The current saved object
150    *
151    * @see doSave()
152    */
153   public function save($con = null)
154   {
155     if (!$this->isValid())
156     {
157       throw $this->getErrorSchema();
158     }
159
160     if (is_null($con))
161     {
162       $con = $this->getConnection();
163     }
164
165     try
166     {
167       $con->beginTransaction();
168
169       $this->doSave($con);
170
171       $con->commit();
172     }
173     catch (Exception $e)
174     {
175       $con->rollBack();
176
177       throw $e;
178     }
179
180     return $this->object;
181   }
182
183   /**
184    * Updates the values of the object with the cleaned up values.
185    *
186    * @param  array $values An array of values
187    *
188    * @return BaseObject The current updated object
189    */
190   public function updateObject($values = null)
191   {
192     if (is_null($values))
193     {
194       $values = $this->values;
195     }
196
197     $values = $this->processValues($values);
198
199     $this->object->fromArray($values, BasePeer::TYPE_FIELDNAME);
200
201     // embedded forms
202     $this->updateObjectEmbeddedForms($values);
203
204     return $this->object;
205   }
206
207   /**
208    * Updates the values of the objects in embedded forms.
209    *
210    * @param array $values An array of values
211    * @param array $forms  An array of forms
212    */
213   public function updateObjectEmbeddedForms($values, $forms = null)
214   {
215     if (is_null($forms))
216     {
217       $forms = $this->embeddedForms;
218     }
219
220     foreach ($forms as $name => $form)
221     {
222       if (!isset($values[$name]) || !is_array($values[$name]))
223       {
224         continue;
225       }
226
227       if ($form instanceof sfFormPropel)
228       {
229         $form->updateObject($values[$name]);
230       }
231       else
232       {
233         $this->updateObjectEmbeddedForms($values[$name], $form->getEmbeddedForms());
234       }
235     }
236   }
237
238   /**
239    * Processes cleaned up values with user defined methods.
240    *
241    * To process a value before it is used by the updateObject() method,
242    * you need to define an updateXXXColumn() method where XXX is the PHP name
243    * of the column.
244    *
245    * The method must return the processed value or false to remove the value
246    * from the array of cleaned up values.
247    *
248    * @param  array $values An array of values
249    *
250    * @return array An array of cleaned up values processed by the user defined methods
251    */
252   public function processValues($values)
253   {
254     // see if the user has overridden some column setter
255     $valuesToProcess = $values;
256     foreach ($valuesToProcess as $field => $value)
257     {
258       try
259       {
260         $method = sprintf('update%sColumn', call_user_func(array(constant(get_class($this->object).'::PEER'), 'translateFieldName'), $field, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_PHPNAME));
261       }
262       catch (Exception $e)
263       {
264         // not a "real" column of this object
265         if (!method_exists($this, $method = sprintf('update%sColumn', self::camelize($field))))
266         {
267           continue;
268         }
269       }
270
271       if (method_exists($this, $method))
272       {
273         if (false === $ret = $this->$method($value))
274         {
275           unset($values[$field]);
276         }
277         else
278         {
279           $values[$field] = $ret;
280         }
281       }
282       else
283       {
284         // save files
285         if ($this->validatorSchema[$field] instanceof sfValidatorFile)
286         {
287           $values[$field] = $this->processUploadedFile($field, null, $valuesToProcess);
288         }
289       }
290     }
291
292     return $values;
293   }
294
295   /**
296    * Returns true if the current form has some associated i18n objects.
297    *
298    * @return Boolean true if the current form has some associated i18n objects, false otherwise
299    */
300   public function isI18n()
301   {
302     return !is_null($this->getI18nFormClass());
303   }
304
305   /**
306    * Returns the name of the i18n model.
307    *
308    * @return string The name of the i18n model
309    */
310   public function getI18nModelName()
311   {
312     return null;
313   }
314
315   /**
316    * Returns the name of the i18n form class.
317    *
318    * @return string The name of the i18n form class
319    */
320   public function getI18nFormClass()
321   {
322     return null;
323   }
324
325   /**
326    * Renders a form tag suitable for the related Propel object.
327    *
328    * The method is automatically guessed based on the Propel object:
329    *
330    *  * if the object is new, the method is POST
331    *  * if the object already exists, the method is PUT
332    *
333    * @param  string $url         The URL for the action
334    * @param  array  $attributes  An array of HTML attributes
335    *
336    * @return string An HTML representation of the opening form tag
337    *
338    * @see sfForm
339    */
340   public function renderFormTag($url, array $attributes = array())
341   {
342     if (!isset($attributes['method']))
343     {
344       $attributes['method'] = $this->getObject()->isNew() ? 'post' : 'put';
345     }
346
347     return parent::renderFormTag($url, $attributes);
348   }
349
350   /**
351    * Updates and saves the current object.
352    *
353    * If you want to add some logic before saving or save other associated objects,
354    * this is the method to override.
355    *
356    * @param PropelPDO $con An optional PropelPDO object
357    */
358   protected function doSave($con = null)
359   {
360     if (is_null($con))
361     {
362       $con = $this->getConnection();
363     }
364
365     $this->updateObject();
366
367     $this->object->save($con);
368
369     // embedded forms
370     $this->saveEmbeddedForms($con);
371   }
372
373   /**
374    * Saves embedded form objects.
375    *
376    * @param PropelPDO $con   An optional PropelPDO object
377    * @param array     $forms An array of forms
378    */
379   public function saveEmbeddedForms($con = null, $forms = null)
380   {
381     if (is_null($con))
382     {
383       $con = $this->getConnection();
384     }
385
386     if (is_null($forms))
387     {
388       $forms = $this->embeddedForms;
389     }
390
391     foreach ($forms as $form)
392     {
393       if ($form instanceof sfFormPropel)
394       {
395         $form->saveEmbeddedForms($con);
396         $form->getObject()->save($con);
397       }
398       else
399       {
400         $this->saveEmbeddedForms($con, $form->getEmbeddedForms());
401       }
402     }
403   }
404
405   /**
406    * Updates the default values of the form with the current values of the current object.
407    */
408   protected function updateDefaultsFromObject()
409   {
410     // update defaults for the main object
411     if ($this->isNew)
412     {
413       $this->setDefaults(array_merge($this->object->toArray(BasePeer::TYPE_FIELDNAME), $this->getDefaults()));
414     }
415     else
416     {
417       $this->setDefaults(array_merge($this->getDefaults(), $this->object->toArray(BasePeer::TYPE_FIELDNAME)));
418     }
419   }
420
421   /**
422    * Saves the uploaded file for the given field.
423    *
424    * @param  string $field The field name
425    * @param  string $filename The file name of the file to save
426    * @param  array  $values An array of values
427    *
428    * @return string The filename used to save the file
429    */
430   protected function processUploadedFile($field, $filename = null, $values = null)
431   {
432     if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
433     {
434       throw new LogicException(sprintf('You cannot save the current file for field "%s" as the field is not a file.', $field));
435     }
436
437     if (is_null($values))
438     {
439       $values = $this->values;
440     }
441
442     if (isset($values[$field.'_delete']) && $values[$field.'_delete'])
443     {
444       $this->removeFile($field);
445
446       return '';
447     }
448
449     if (!$values[$field])
450     {
451       $column = call_user_func(array(constant(get_class($this->object).'::PEER'), 'translateFieldName'), $field, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_PHPNAME);
452       $getter = 'get'.$column;
453
454       return $this->object->$getter();
455     }
456
457     // we need the base directory
458     if (!$this->validatorSchema[$field]->getOption('path'))
459     {
460       return $values[$field];
461     }
462
463     $this->removeFile($field);
464
465     return $this->saveFile($field, $filename, $values[$field]);
466   }
467
468   /**
469    * Removes the current file for the field.
470    *
471    * @param string $field The field name
472    */
473   protected function removeFile($field)
474   {
475     if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
476     {
477       throw new LogicException(sprintf('You cannot remove the current file for field "%s" as the field is not a file.', $field));
478     }
479
480     $column = call_user_func(array(constant(get_class($this->object).'::PEER'), 'translateFieldName'), $field, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_PHPNAME);
481     $getter = 'get'.$column;
482
483     if (($directory = $this->validatorSchema[$field]->getOption('path')) && is_file($directory.DIRECTORY_SEPARATOR.$this->object->$getter()))
484     {
485       unlink($directory.DIRECTORY_SEPARATOR.$this->object->$getter());
486     }
487   }
488
489   /**
490    * Saves the current file for the field.
491    *
492    * @param  string          $field    The field name
493    * @param  string          $filename The file name of the file to save
494    * @param  sfValidatedFile $file     The validated file to save
495    *
496    * @return string The filename used to save the file
497    */
498   protected function saveFile($field, $filename = null, sfValidatedFile $file = null)
499   {
500     if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
501     {
502       throw new LogicException(sprintf('You cannot save the current file for field "%s" as the field is not a file.', $field));
503     }
504
505     if (is_null($file))
506     {
507       $file = $this->getValue($field);
508     }
509
510     $column = call_user_func(array(constant(get_class($this->object).'::PEER'), 'translateFieldName'), $field, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_PHPNAME);
511     $method = sprintf('generate%sFilename', $column);
512
513     if (!is_null($filename))
514     {
515       return $file->save($filename);
516     }
517     else if (method_exists($this->object, $method))
518     {
519       return $file->save($this->object->$method($file));
520     }
521     else
522     {
523       return $file->save();
524     }
525   }
526
527   protected function camelize($text)
528   {
529     return sfToolkit::pregtr($text, array('#/(.?)#e' => "'::'.strtoupper('\\1')", '/(^|_|-)+(.)/e' => "strtoupper('\\2')"));
530   }
531 }
532
Note: See TracBrowser for help on using the browser.