* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Twig\Extension; use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; use Symfony\Component\Form\FormView; use Symfony\Component\Form\Exception\FormException; /** * FormExtension extends Twig with form capabilities. * * @author Fabien Potencier * @author Bernhard Schussek */ class FormExtension extends \Twig_Extension { protected $resources; protected $templates; protected $environment; protected $themes; protected $varStack; public function __construct(array $resources = array()) { $this->themes = new \SplObjectStorage(); $this->varStack = new \SplObjectStorage(); $this->templates = new \SplObjectStorage(); $this->resources = $resources; } /** * {@inheritdoc} */ public function initRuntime(\Twig_Environment $environment) { $this->environment = $environment; } /** * Sets a theme for a given view. * * @param FormView $view A FormView instance * @param array $resources An array of resources */ public function setTheme(FormView $view, array $resources) { $this->themes->attach($view, $resources); $this->templates->detach($view); } /** * Returns the token parser instance to add to the existing list. * * @return array An array of Twig_TokenParser instances */ public function getTokenParsers() { return array( // {% form_theme form "SomeBungle::widgets.twig" %} new FormThemeTokenParser(), ); } public function getFunctions() { return array( 'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))), 'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))), 'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))), 'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))), 'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))), 'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))), ); } /** * Renders the HTML enctype in the form tag, if necessary * * Example usage in Twig templates: * *
* * @param FormView $view The view for which to render the encoding type */ public function renderEnctype(FormView $view) { return $this->render($view, 'enctype'); } /** * Renders a row for the view. * * @param FormView $view The view to render as a row * @param array $variables An array of variables */ public function renderRow(FormView $view, array $variables = array()) { return $this->render($view, 'row', $variables); } public function renderRest(FormView $view, array $variables = array()) { return $this->render($view, 'rest', $variables); } /** * Renders the HTML for a given view * * Example usage in Twig: * * {{ form_widget(view) }} * * You can pass attributes element during the call: * * {{ form_widget(view, {'class': 'foo'}) }} * * Some fields also accept additional variables as parameters: * * {{ form_widget(view, {}, {'separator': '+++++'}) }} * * @param FormView $view The view to render * @param array $variables Additional variables passed to the template */ public function renderWidget(FormView $view, array $variables = array()) { return $this->render($view, 'widget', $variables); } /** * Renders the errors of the given view * * @param FormView $view The view to render the errors for */ public function renderErrors(FormView $view) { return $this->render($view, 'errors'); } /** * Renders the label of the given view * * @param FormView $view The view to render the label for * @param string $label Label name * @param array $variables Additional variables passed to the template */ public function renderLabel(FormView $view, $label = null, array $variables = array()) { if ($label !== null) { $variables += array('label' => $label); } return $this->render($view, 'label', $variables); } protected function render(FormView $view, $section, array $variables = array()) { $templates = $this->getTemplates($view); $blocks = $view->get('types'); array_unshift($blocks, '_'.$view->get('id')); foreach ($blocks as &$block) { $block = $block.'_'.$section; if (isset($templates[$block])) { if ('widget' === $section || 'row' === $section) { $view->setRendered(); } $this->varStack[$view] = array_replace( $view->all(), isset($this->varStack[$view]) ? $this->varStack[$view] : array(), $variables ); $html = $templates[$block]->renderBlock($block, $this->varStack[$view]); return $html; } } throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks))); } /** * Returns the templates used by the view. * * templates are looked for in the following resources: * * resources from the themes (and its parents) * * default resources * * @param FormView $view The view * * @return array An array of Twig_TemplateInterface instances */ protected function getTemplates(FormView $view) { if (!$this->templates->contains($view)) { // defaults $all = $this->resources; // themes $parent = $view; do { if (isset($this->themes[$parent])) { $all = array_merge($all, $this->themes[$parent]); } } while ($parent = $parent->getParent()); $templates = array(); foreach ($all as $resource) { if (!$resource instanceof \Twig_Template) { $resource = $this->environment->loadTemplate($resource); } $blocks = array(); foreach ($this->getBlockNames($resource) as $name) { $blocks[$name] = $resource; } $templates = array_replace($templates, $blocks); } $this->templates->attach($view, $templates); } else { $templates = $this->templates[$view]; } return $templates; } protected function getBlockNames($resource) { $names = $resource->getBlockNames(); $parent = $resource; while (false !== $parent = $parent->getParent(array())) { $names = array_merge($names, $parent->getBlockNames()); } return array_unique($names); } /** * Returns the name of the extension. * * @return string The extension name */ public function getName() { return 'form'; } }