sfForm field widget

From a recent post on the symfony mailing-list someone raises a lack of features in the sfForm framework. One of his point was :

Another thing is, it would be nice if one could reuse “logical field definition” (combination of widget and validator) for example for things like email, date, or names which are very common and used very often. I don’t know but maybe the sfFormField can be extended to also include a validator? or would it make sense to use mergeForm() for such things? (lack of documentation).

So yes, mergeForm() can be a good option to create standalone field : widget + validator

Let’s try to code that !

The trEmailFormField

This class defines a “field form”, which must be used only as a field, the first parameter is the name of the field. The name is used when trEmailFormField will be merge with the parent form.

<?php
class trEmailFormField extends sfForm
{
    protected $field_name;

    public function __construct($name, $defaults = array(), $options = array(), $CSRFSecret = null)
    {
        $this->field_name = $name;

        parent::__construct($defaults, $options , $CSRFSecret);
    }

    public function configure()
    {
        $this->widgetSchema[$this->field_name] = new sfWidgetFormInput;
        $this->validatorSchema[$this->field_name] = new sfValidatorEmail;
    }
}

Usage : FakeContactForm

Now we can use the “trEmailFormField” inside a form to automatically add an email field (widget+validator) into the FakeContactForm.

<?php
// FakeContactForm.class.php
class FakeContactForm extends sfForm
{
    public function configure()
    {
        $this->widgetSchema['object'] = new sfWidgetFormInput;
        $this->validatorSchema['object'] = new swValidatorText;

        $this->widgetSchema['message'] = new sfWidgetFormTextarea;
        $this->validatorSchema['message'] = new swValidatorText;

        // this will add widget and validator to the current form
        $this->mergeForm(new trEmailFormField('email'));

        $this->widgetSchema->setNameFormat('fake_contact_form[%s]');
    }
}

// actions.class.php
public function executeDemoEmailFormField(sfWebRequest $request)
{
    $this->form = new FakeContactForm(array(
        'object' => 'The subject',
        'message' => 'The message',
        'email' => 'And the email'
    ));

    if($request->isMethod('post'))
    {
        $this->form->bind($request->getParameter($this->form->getName()));
    }
}

sfForm::mergeForm

The mergeForm method copies validator and widget into the current form. Widget and validator are defined at the same nested level of other fields. That’s why trEmailFormField’s first argument is the name of the form.

Notes

When you do such thing you are coupling widget and validator, which might not always good to create bespoken solution.