Deprecated/0.11.0/FormPopulationFilter

Warning! This page has the following tags:

  • This page is deprecated as of Agavi 0.11.0, but still contains useful information.
  • This page needs to be fixed and then merged with docbook documentation.

The FormPopulationFilter?

The Usual Approach

The basic idea is that you don't have to do insert request parameters everywhere in your forms like

<input type="text" name="first_name" value="<?=$this->getContext()->getRequest()->getParameter('first_name')?>" />

or, worse

<input type="checkbox" name="newsletter"<?php if($this->getContext()->getRequest()->hasParameter('newsletter')): ?> checked="checked"<?php endif; ?> />

or, even worse

<select name="country">
<?php foreach($template['countries‘] as $code => $country): ?>
  <option value="<?=$code?>"<?php if($this->getContext()->getRequest()->getParameter('country') == $code): ?> selected="selected"<?php endif; ?>><?=htmlentities($country)?></option>
<?php endforeach; ?>
</select>

The Cure Is Here

Instead, all you have to do is lean back and let the FormPopulationFilter? do the job. It will automatically re-populate your forms on POST requests. This might happen when you'd usually return to the overview page of the element you're editing, but a validation error occured, so you're showing the input form again. Or if you always re-display the input form after an edit, no matter if it was successful or not.

This is really a matter of taste. I, for instance, always forward() to the overview page after an edit, because opening the edit page creates a lock entry for that item in the database. This could be one reason why you'd want to return to somewhere else if the edit went through.

So... you'd just change the code examples above to:

<input type="text" name="first_name" />

and

<input type="checkbox" name="newsletter" />

and

<select name="country">
<?php foreach($template['countries‘] as $code => $country): ?>
  <option value="<?=$code?>"><?=htmlentities($country)?></option>
<?php endforeach; ?>
</select>

But There Is More: Validation Support!

You'll likely show the form again because there was an error. Or not. Anyways, if there was an error, due to validation failure or whatever, you obviously want the corresponding fields highlighted. Red border for the input element, red color for the form label. And the filter does that! For every form element that has an error in the Request (it assumes the error name equals the form element's name), it will set a class on the input element, error by default. Also, it will look for all implicit and explicit <label>s that belong to the element and assign the error class to them, too (implicit labels are those that contain the form element; explicit labels are attached to the form element via the for attribute).

This means a lot of additional convenience. Using some very simple CSS, you can have your form fields and their labels highlighted automatically:

input.error, textarea.error, select.error {
  border-color: #F00;
}

label.error {
  color: #F00;
}

Now, here's another trick: as you may know, you can have more than one label per element. One idea would be to place error messages into your form by putting them into a label that has a class assigned which hides it by default right next to the input field, for instance. Then, you use some CSS to make the magic happen:

label.hidden {
  display: none;
}

label.hidden.error {
  display: inline;
}

Et voila, for invalid form fields, the error message appears.

Note that this might not work in IE because there are bugs with multiple classes. Instead, use only the error class on the label and add the !important declaration after the display:inline

You can customize the class name used by setting the param.error_class option.

XHTML and HTML

It is important to know that the filter will detect the document type you are using. If you use XHTML, it will return XML output. Otherwise, it'll all be HTML. That means if you're pretending to be cool by using the XHTML doctype, but you actually aren't producing valid markup, you're in for some seriously foobar'd output.

However, you can force the output mode by setting param.force_output_mode to xhtml or html to force either of these modes. We recommend to rely on the auto-detection though.

Also, if you're using XHTML, the filter will remove any XML prolog inserted during processing if it wasn't there before. This has to be done because Internet Explorer 6 would jump to Quirks Mode if they encounter an XML prolog, even though the doctype would force the browser into standards compliant mode normally. If you don't want the inserted xml prolog to be stripped from the output (there's not really any reason why you would want that, mind you!), you can set param.remove_xml_prolog to false.

Also, if you're using inline <style> and <script> blocks (which you better shouldn't in XHTML), the DOM processor will wrap their contents into CDATA blocks. The filter automatically applies fixes to these blocks so the style sheets and scripts still work in all browsers. If you don't want this compatibility fix to be applied (again, there's no reason why you'd want to do that!), you can set param.cdata_fix to false.

How It Detects Forms

It looks at the action parameter of the form and compares it to the URL requested. Of course, it can also deal with <base href="whatever" /> elements in the <head> portion of your HTML document, don't worry about that.

It Will Not Insert Everything

By default, the filter will not re-populate password and hidden fields. You can change that by setting param.include_password_inputs and/or param.include_hidden_inputs to true.

Default Values

Of course, you can still set default values for stuff. Do that in templates, for instance, like this:

<input type="checkbox" name="newsletter" checked="checked" />

Easy, eh?

Power To The People

As aforementioned, the filter will automagically jump in and save the world each time a POST request happens. However, there might be situations where you want to override this behavior. Either because you did have a POST, but you don't want re-population to happen, or because you have a GET request and now you need the filter's magic superpowers to do the job for you. In both cases, you can set a flag so the filter can recognize that you're thinking you know better and act accordingly:

// enforce population even if the request method is GET
$request->setAttribute('populate', true, 'org.agavi.filter.FormPopulationFilter');

or

// yes, yes, I know, it's a POST request, but I don't want teh filter to run in this special case
$request->setAttribute('populate', false, 'org.agavi.filter.FormPopulationFilter');

If this attribute is not set, the filter will fall back to the default behavior explained in the sections above.

How Does It Do All That?

Witchcraft. With magic spells from the planet of XPath.

Anything Else?

Yes. The <select> example is still somewhat ugly. You should consider giving PHPTAL a try, now that we have a PHPTALView?:

<select name="country">
  <option tal:repeat="country countries" tal:content="country" tal:attributes="value repeat/country/key" />
</select>

And How Do I Install It?

Just make sure you have these two lines in filters.ini:

[FormPopulationFilter]
  class = "FormPopulationFilter"