FormExtension.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.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. namespace Symfony\Bridge\Twig\Extension;
  11. use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
  12. use Symfony\Component\Form\FormView;
  13. use Symfony\Component\Form\Exception\FormException;
  14. /**
  15. * FormExtension extends Twig with form capabilities.
  16. *
  17. * @author Fabien Potencier <fabien@symfony.com>
  18. * @author Bernhard Schussek <bernhard.schussek@symfony.com>
  19. */
  20. class FormExtension extends \Twig_Extension
  21. {
  22. protected $resources;
  23. protected $templates;
  24. protected $environment;
  25. protected $themes;
  26. protected $varStack;
  27. public function __construct(array $resources = array())
  28. {
  29. $this->themes = new \SplObjectStorage();
  30. $this->varStack = new \SplObjectStorage();
  31. $this->templates = new \SplObjectStorage();
  32. $this->resources = $resources;
  33. }
  34. /**
  35. * {@inheritdoc}
  36. */
  37. public function initRuntime(\Twig_Environment $environment)
  38. {
  39. $this->environment = $environment;
  40. }
  41. /**
  42. * Sets a theme for a given view.
  43. *
  44. * @param FormView $view A FormView instance
  45. * @param array $resources An array of resources
  46. */
  47. public function setTheme(FormView $view, array $resources)
  48. {
  49. $this->themes->attach($view, $resources);
  50. $this->templates->detach($view);
  51. }
  52. /**
  53. * Returns the token parser instance to add to the existing list.
  54. *
  55. * @return array An array of Twig_TokenParser instances
  56. */
  57. public function getTokenParsers()
  58. {
  59. return array(
  60. // {% form_theme form "SomeBungle::widgets.twig" %}
  61. new FormThemeTokenParser(),
  62. );
  63. }
  64. public function getFunctions()
  65. {
  66. return array(
  67. 'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
  68. 'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
  69. 'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
  70. 'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
  71. 'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
  72. 'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
  73. );
  74. }
  75. /**
  76. * Renders the HTML enctype in the form tag, if necessary
  77. *
  78. * Example usage in Twig templates:
  79. *
  80. * <form action="..." method="post" {{ form_enctype(form) }}>
  81. *
  82. * @param FormView $view The view for which to render the encoding type
  83. */
  84. public function renderEnctype(FormView $view)
  85. {
  86. return $this->render($view, 'enctype');
  87. }
  88. /**
  89. * Renders a row for the view.
  90. *
  91. * @param FormView $view The view to render as a row
  92. * @param array $variables An array of variables
  93. */
  94. public function renderRow(FormView $view, array $variables = array())
  95. {
  96. return $this->render($view, 'row', $variables);
  97. }
  98. public function renderRest(FormView $view, array $variables = array())
  99. {
  100. return $this->render($view, 'rest', $variables);
  101. }
  102. /**
  103. * Renders the HTML for a given view
  104. *
  105. * Example usage in Twig:
  106. *
  107. * {{ form_widget(view) }}
  108. *
  109. * You can pass attributes element during the call:
  110. *
  111. * {{ form_widget(view, {'class': 'foo'}) }}
  112. *
  113. * Some fields also accept additional variables as parameters:
  114. *
  115. * {{ form_widget(view, {}, {'separator': '+++++'}) }}
  116. *
  117. * @param FormView $view The view to render
  118. * @param array $variables Additional variables passed to the template
  119. */
  120. public function renderWidget(FormView $view, array $variables = array())
  121. {
  122. return $this->render($view, 'widget', $variables);
  123. }
  124. /**
  125. * Renders the errors of the given view
  126. *
  127. * @param FormView $view The view to render the errors for
  128. */
  129. public function renderErrors(FormView $view)
  130. {
  131. return $this->render($view, 'errors');
  132. }
  133. /**
  134. * Renders the label of the given view
  135. *
  136. * @param FormView $view The view to render the label for
  137. * @param string $label Label name
  138. * @param array $variables Additional variables passed to the template
  139. */
  140. public function renderLabel(FormView $view, $label = null, array $variables = array())
  141. {
  142. if ($label !== null) {
  143. $variables += array('label' => $label);
  144. }
  145. return $this->render($view, 'label', $variables);
  146. }
  147. protected function render(FormView $view, $section, array $variables = array())
  148. {
  149. $templates = $this->getTemplates($view);
  150. $blocks = $view->get('types');
  151. array_unshift($blocks, '_'.$view->get('id'));
  152. foreach ($blocks as &$block) {
  153. $block = $block.'_'.$section;
  154. if (isset($templates[$block])) {
  155. if ('widget' === $section || 'row' === $section) {
  156. $view->setRendered();
  157. }
  158. $this->varStack[$view] = array_replace(
  159. $view->all(),
  160. isset($this->varStack[$view]) ? $this->varStack[$view] : array(),
  161. $variables
  162. );
  163. $html = $templates[$block]->renderBlock($block, $this->varStack[$view]);
  164. return $html;
  165. }
  166. }
  167. throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
  168. }
  169. /**
  170. * Returns the templates used by the view.
  171. *
  172. * templates are looked for in the following resources:
  173. * * resources from the themes (and its parents)
  174. * * default resources
  175. *
  176. * @param FormView $view The view
  177. *
  178. * @return array An array of Twig_TemplateInterface instances
  179. */
  180. protected function getTemplates(FormView $view)
  181. {
  182. if (!$this->templates->contains($view)) {
  183. // defaults
  184. $all = $this->resources;
  185. // themes
  186. $parent = $view;
  187. do {
  188. if (isset($this->themes[$parent])) {
  189. $all = array_merge($all, $this->themes[$parent]);
  190. }
  191. } while ($parent = $parent->getParent());
  192. $templates = array();
  193. foreach ($all as $resource) {
  194. if (!$resource instanceof \Twig_Template) {
  195. $resource = $this->environment->loadTemplate($resource);
  196. }
  197. $blocks = array();
  198. foreach ($this->getBlockNames($resource) as $name) {
  199. $blocks[$name] = $resource;
  200. }
  201. $templates = array_replace($templates, $blocks);
  202. }
  203. $this->templates->attach($view, $templates);
  204. } else {
  205. $templates = $this->templates[$view];
  206. }
  207. return $templates;
  208. }
  209. protected function getBlockNames($resource)
  210. {
  211. $names = $resource->getBlockNames();
  212. $parent = $resource;
  213. while (false !== $parent = $parent->getParent(array())) {
  214. $names = array_merge($names, $parent->getBlockNames());
  215. }
  216. return array_unique($names);
  217. }
  218. /**
  219. * Returns the name of the extension.
  220. *
  221. * @return string The extension name
  222. */
  223. public function getName()
  224. {
  225. return 'form';
  226. }
  227. }