Development

/branches/1.3/lib/plugins/sfDoctrinePlugin/lib/form/sfFormDoctrine.class.php

You must first sign up to be able to contribute.

root/branches/1.3/lib/plugins/sfDoctrinePlugin/lib/form/sfFormDoctrine.class.php

Revision 29643, 11.6 kB (checked in by Jonathan.Wage, 4 years ago)

[1.3, 1.4] Fixing issue with i18n forms when primary key and i18n field are not the defaults (fixes #8650)

  • 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  * (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  * sfFormDoctrine is the base class for forms based on Doctrine objects.
14  *
15  * This class extends BaseForm, a class generated automatically with each new project.
16  *
17  * @package    symfony
18  * @subpackage form
19  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
20  * @author     Jonathan H. Wage <jonwage@gmail.com>
21  * @version    SVN: $Id$
22  */
23 abstract class sfFormDoctrine extends sfFormObject
24 {
25   /**
26    * Constructor.
27    *
28    * @param mixed  A object used to initialize default values
29    * @param array  An array of options
30    * @param string A CSRF secret (false to disable CSRF protection, null to use the global CSRF secret)
31    *
32    * @see sfForm
33    */
34   public function __construct($object = null, $options = array(), $CSRFSecret = null)
35   {
36     $class = $this->getModelName();
37     if (!$object)
38     {
39       $this->object = new $class();
40     }
41     else
42     {
43       if (!$object instanceof $class)
44       {
45         throw new sfException(sprintf('The "%s" form only accepts a "%s" object.', get_class($this), $class));
46       }
47
48       $this->object = $object;
49       $this->isNew = !$this->getObject()->exists();
50     }
51
52     parent::__construct(array(), $options, $CSRFSecret);
53
54     $this->updateDefaultsFromObject();
55   }
56
57   /**
58    * @return Doctrine_Connection
59    * @see sfFormObject
60    */
61   public function getConnection()
62   {
63     return Doctrine_Manager::getInstance()->getConnectionForComponent($this->getModelName());
64   }
65
66   /**
67    * Embeds i18n objects into the current form.
68    *
69    * @param array   $cultures   An array of cultures
70    * @param string  $decorator  A HTML decorator for the embedded form
71    */
72   public function embedI18n($cultures, $decorator = null)
73   {
74     if (!$this->isI18n())
75     {
76       throw new sfException(sprintf('The model "%s" is not internationalized.', $this->getModelName()));
77     }
78
79     $class = $this->getI18nFormClass();
80     foreach ($cultures as $culture)
81     {
82       $i18nObject = $this->getObject()->Translation[$culture];
83       $i18n = new $class($i18nObject);
84
85       if (false === $i18nObject->exists())
86       {
87         unset($i18n[$this->getI18nModelPrimaryKeyName()], $i18n[$this->getI18nModelI18nField()]);
88       }
89
90       $this->embedForm($culture, $i18n, $decorator);
91     }
92   }
93
94   /**
95    * Embed a Doctrine_Collection relationship in to a form
96    *
97    *     [php]
98    *     $userForm = new UserForm($user);
99    *     $userForm->embedRelation('Groups AS groups');
100    *
101    * @param  string $relationName  The name of the relation and an optional alias
102    * @param  string $formClass     The name of the form class to use
103    * @param  array  $formArguments Arguments to pass to the constructor (related object will be shifted onto the front)
104    * @param string  $innerDecorator A HTML decorator for each embedded form
105    * @param string  $decorator      A HTML decorator for the main embedded form
106    *
107    * @throws InvalidArgumentException If the relationship is not a collection
108    */
109   public function embedRelation($relationName, $formClass = null, $formArgs = array(), $innerDecorator = null, $decorator = null)
110   {
111     if (false !== $pos = stripos($relationName, ' as '))
112     {
113       $fieldName = substr($relationName, $pos + 4);
114       $relationName = substr($relationName, 0, $pos);
115     }
116     else
117     {
118       $fieldName = $relationName;
119     }
120
121     $relation = $this->getObject()->getTable()->getRelation($relationName);
122
123     $r = new ReflectionClass(null === $formClass ? $relation->getClass().'Form' : $formClass);
124
125     if (Doctrine_Relation::ONE == $relation->getType())
126     {
127       $this->embedForm($fieldName, $r->newInstanceArgs(array_merge(array($this->getObject()->$relationName), $formArgs)), $decorator);
128     }
129     else
130     {
131       $subForm = new sfForm();
132
133       foreach ($this->getObject()->$relationName as $index => $childObject)
134       {
135         $form = $r->newInstanceArgs(array_merge(array($childObject), $formArgs));
136
137         $subForm->embedForm($index, $form, $innerDecorator);
138         $subForm->getWidgetSchema()->setLabel($index, (string) $childObject);
139       }
140
141       $this->embedForm($fieldName, $subForm, $decorator);
142     }
143   }
144
145   /**
146    * @see sfFormObject
147    */
148   protected function doUpdateObject($values)
149   {
150     $this->getObject()->fromArray($values);
151   }
152
153   /**
154    * Processes cleaned up values with user defined methods.
155    *
156    * To process a value before it is used by the updateObject() method,
157    * you need to define an updateXXXColumn() method where XXX is the PHP name
158    * of the column.
159    *
160    * The method must return the processed value or false to remove the value
161    * from the array of cleaned up values.
162    *
163    * @see sfFormObject
164    */
165   public function processValues($values)
166   {
167     // see if the user has overridden some column setter
168     $valuesToProcess = $values;
169     foreach ($valuesToProcess as $field => $value)
170     {
171       $method = sprintf('update%sColumn', $this->camelize($field));
172
173       if (method_exists($this, $method))
174       {
175         if (false === $ret = $this->$method($value))
176         {
177           unset($values[$field]);
178         }
179         else
180         {
181           $values[$field] = $ret;
182         }
183       }
184       else
185       {
186         // save files
187         if ($this->validatorSchema[$field] instanceof sfValidatorFile)
188         {
189           $values[$field] = $this->processUploadedFile($field, null, $valuesToProcess);
190         }         
191       }
192     }
193
194     return $values;
195   }
196
197   /**
198    * Returns true if the current form has some associated i18n objects.
199    *
200    * @return Boolean true if the current form has some associated i18n objects, false otherwise
201    */
202   public function isI18n()
203   {
204     return $this->getObject()->getTable()->hasTemplate('Doctrine_Template_I18n');
205   }
206
207   /**
208    * Returns the name of the i18n model.
209    *
210    * @return string The name of the i18n model
211    */
212   public function getI18nModelName()
213   {
214     return $this->getObject()->getTable()->getTemplate('Doctrine_Template_I18n')->getI18n()->getOption('className');
215   }
216
217   /**
218    * Returns the name of the i18n form class.
219    *
220    * @return string The name of the i18n form class
221    */
222   public function getI18nFormClass()
223   {
224     return $this->getI18nModelName().'Form';
225   }
226
227   /**
228    * Returns the primary key name of the i18n model.
229    *
230    * @return string The primary key name of the i18n model
231    */
232   public function getI18nModelPrimaryKeyName()
233   {
234     $primaryKey = $this->getObject()->getTable()->getIdentifier();
235
236     if (is_array($primaryKey))
237     {
238       throw new sfException(sprintf('The model "%s" has composite primary keys and cannot be used with i18n..', $this->getModelName()));
239     }
240
241     return $primaryKey;
242   }
243
244   /**
245    * Returns the i18nField name of the i18n model.
246    *
247    * @return string The i18nField name of the i18n model
248    */
249   public function getI18nModelI18nField()
250   {
251     return $this->getObject()->getTable()->getTemplate('Doctrine_Template_I18n')->getI18n()->getOption('i18nField');
252   }
253
254   /**
255    * Updates the default values of the form with the current values of the current object.
256    */
257   protected function updateDefaultsFromObject()
258   {
259     $defaults = $this->getDefaults();
260
261     // update defaults for the main object
262     if ($this->isNew())
263     {
264       $defaults = $defaults + $this->getObject()->toArray(false);
265     }
266     else
267     {
268       $defaults = $this->getObject()->toArray(false) + $defaults;
269     }
270
271     foreach ($this->embeddedForms as $name => $form)
272     {
273       if ($form instanceof sfFormDoctrine)
274       {
275         $form->updateDefaultsFromObject();
276         $defaults[$name] = $form->getDefaults();
277       }
278     }
279
280     $this->setDefaults($defaults);
281   }
282
283   /**
284    * Saves the uploaded file for the given field.
285    *
286    * @param  string $field The field name
287    * @param  string $filename The file name of the file to save
288    * @param  array  $values An array of values
289    *
290    * @return string The filename used to save the file
291    */
292   protected function processUploadedFile($field, $filename = null, $values = null)
293   {
294     if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
295     {
296       throw new LogicException(sprintf('You cannot save the current file for field "%s" as the field is not a file.', $field));
297     }
298
299     if (null === $values)
300     {
301       $values = $this->values;
302     }
303
304     if (isset($values[$field.'_delete']) && $values[$field.'_delete'])
305     {
306       $this->removeFile($field);
307
308       return '';
309     }
310
311     if (!$values[$field])
312     {
313       // this is needed if the form is embedded, in which case
314       // the parent form has already changed the value of the field
315       $oldValues = $this->getObject()->getModified(true, false);
316
317       return isset($oldValues[$field]) ? $oldValues[$field] : $this->object->$field;
318     }
319
320     // we need the base directory
321     if (!$this->validatorSchema[$field]->getOption('path'))
322     {
323       return $values[$field];
324     }
325
326     $this->removeFile($field);
327
328     return $this->saveFile($field, $filename, $values[$field]);
329   }
330
331   /**
332    * Removes the current file for the field.
333    *
334    * @param string $field The field name
335    */
336   protected function removeFile($field)
337   {
338     if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
339     {
340       throw new LogicException(sprintf('You cannot remove the current file for field "%s" as the field is not a file.', $field));
341     }
342
343     $directory = $this->validatorSchema[$field]->getOption('path');
344     if ($directory && is_file($file = $directory.'/'.$this->getObject()->$field))
345     {
346       unlink($file);
347     }
348   }
349
350   /**
351    * Saves the current file for the field.
352    *
353    * @param  string          $field    The field name
354    * @param  string          $filename The file name of the file to save
355    * @param  sfValidatedFile $file     The validated file to save
356    *
357    * @return string The filename used to save the file
358    */
359   protected function saveFile($field, $filename = null, sfValidatedFile $file = null)
360   {
361     if (!$this->validatorSchema[$field] instanceof sfValidatorFile)
362     {
363       throw new LogicException(sprintf('You cannot save the current file for field "%s" as the field is not a file.', $field));
364     }
365
366     if (null === $file)
367     {
368       $file = $this->getValue($field);
369     }
370
371     $method = sprintf('generate%sFilename', $this->camelize($field));
372
373     if (null !== $filename)
374     {
375       return $file->save($filename);
376     }
377     else if (method_exists($this, $method))
378     {
379       return $file->save($this->$method($file));
380     }
381     else if (method_exists($this->getObject(), $method))
382     {
383       return $file->save($this->getObject()->$method($file));
384     }
385     else if (method_exists($this->getObject(), $method = sprintf('generate%sFilename', $field)))
386     {
387       // this non-camelized method name has been deprecated
388       return $file->save($this->getObject()->$method($file));
389     }
390     else
391     {
392       return $file->save();
393     }
394   }
395
396   /**
397    * Used in generated forms when models use inheritance.
398    */
399   protected function setupInheritance()
400   {
401   }
402
403   /**
404    * Returns the name of the related model.
405    *
406    * @param string $alias A relation alias
407    *
408    * @return string
409    *
410    * @throws InvalidArgumentException If no relation with the supplied alias exists on the current model
411    */
412   protected function getRelatedModelName($alias)
413   {
414     $table = Doctrine_Core::getTable($this->getModelName());
415
416     if (!$table->hasRelation($alias))
417     {
418       throw new InvalidArgumentException(sprintf('The "%s" model has to "%s" relation.', $this->getModelName(), $alias));
419     }
420
421     $relation = $table->getRelation($alias);
422
423     return $relation['class'];
424   }
425 }
426
Note: See TracBrowser for help on using the browser.