Search code examples
phpsymfony1symfony-formssymfony-1.4

Symfony 1.4 Form Templating


I'm working on an installation of Symfony 1.4 that we've needed to re-skin to match new company branding. One of the requirements of the new skin is that radio buttons have the following syntax -

<label><input name="foo" type="radio" value="bar" id="foo_bar"><span></span></label>

However the default Symfony code for radio buttons is -

<input name="foo" type="radio" value="bar" id="foo_bar">

Where is the template that I can change to add the additional elements to surround the input tag? I've searched high and low through the code and I can't seem to find where this code is being generated.

Thanks in advance!


Solution

  • You'll need to create a custom widget class. I've extended the radio widget and changed the formatter functionality.

    You might need to customise this yourself a little. But the below myWidgetFormSelectRadio class will wrap each radio field in a label like you've specified.

    // \lib\widget\myWidgetFormSelectRadio.class.php
    class myWidgetFormSelectRadio extends sfWidgetFormSelectRadio
    {
      protected function formatChoices($name, $value, $choices, $attributes)
      {
        $inputs = array();
        foreach ($choices as $key => $option)
        {
          $baseAttributes = array(
            'name'  => substr($name, 0, -2),
            'type'  => 'radio',
            'value' => self::escapeOnce($key),
            'id'    => $id = $this->generateId($name, self::escapeOnce($key)),
          );
    
          if (strval($key) == strval($value === false ? 0 : $value))
          {
            $baseAttributes['checked'] = 'checked';
          }
    
          $inputs[] = array(
            'input' => $this->renderContentTag('label', $this->renderTag('input', array_merge($baseAttributes, $attributes)), array('for' => $id)),
          );
        }
    
        return call_user_func($this->getOption('formatter'), $this, $inputs);
      }
    
      public function formatter($widget, $inputs)
      {
        $rows = array();
        foreach ($inputs as $input)
        {
          $rows[] = $input['input'].$this->getOption('label_separator');
        }
    
        return implode($this->getOption('separator'), $rows);
      }
    }
    

    Then in your form classes

    // \lib\form\myForm.class.php
    public function configure()
    {
        $this->widgetSchema['my_field'] = new myWidgetFormSelectRadio(array('choices' => $choices));
    
        // ....
    }
    

    Are you using Twitter Bootstrap by chance? If so, then you'll want to extend the Symfony checkbox widget as well. If you do this then you might want to extend the sfWidgetFormChoice class and change the getRenderer() method so that it uses your myWidgetFormSelectRadio and myWidgetFormSelectCheckbox class when rendering radio/checkbox fields.

    // \lib\widget\myWidgetFormChoice.php
    class myWidgetFormChoice extends sfWidgetFormChoice
    {
      public function getRenderer()
      {
        if ($this->getOption('renderer'))
        {
          return $this->getOption('renderer');
        }
    
        $prefix = $this->getOption('use_my_custom_widget') ? 'my' : 'sf';
    
        if (!$class = $this->getOption('renderer_class'))
        {
          $type = !$this->getOption('expanded') ? '' : ($this->getOption('multiple') ? 'checkbox' : 'radio');
          $class = sprintf($prefix . 'WidgetFormSelect%s', ucfirst($type));
        }
    
        return new $class(array_merge(array('choices' => new sfCallable(array($this, 'getChoices'))), $this->options['renderer_options']), $this->getAttributes());
      }
    }
    

    Then in your form class

    public function configure()
    {
        $this->widgetSchema['my_field'] = new myWidgetFormChoice(array(
             'choices' => $choices,
             'expanded' => true,
             'use_my_custom_widget' => true,
        ));
    
        // ....
    }