PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

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