Drive Forms from Doctrine Entities

21 Mar 2012

FormFactory - Driving Doctrine 1.2 / 2.x Mappings into Zend_Form objects

On a few of my previous projects I found myself creating more form classes than I'd like. And after the 30th one I figured there had to be a better way. I quickly realised that most of the elements within these forms shared similarities to the data type I would use on my database definitions. As I was using Doctrine at the time I figured I could not only drive my database from my mapping definitions, but my forms too.

So the idea would be to take all of the possible data types allowed in a doctrine schematic and provide a default form element for them. So for example a string column could become a text field, or an enum could be a checkbox. Now obviously this would need to be configurable and be easily overridden with another element type when required.

The plan was never to solve all my form problems, but to reduce the workload on creating the simple ones. However I found that simply providing an override configuration wasn't enough, and that I needed slightly more control over my elements. So I did two things;

- created a bunch of quick configuration options such as a default value, whether its a required element or the ability to disable it all together. And,

-Allowed a fall back onto Zend_Form's ini configuration, giving me a greater flexibility when required.

I'm using this library in my current project but have only driven the forms using the doctrine 2.x adapter. There is some work to be done to have it drive off of doctrine 1.x mappings, and i'd also like to be able to parse a sql create file with it at some point too. The code is on github so feel free to fork and have a play. I would love to hear any feedback on any changes / issues / improvements.

https://github.com/leedavis81/FormFactory

Main Features;

Use doctrine mappings to create fast flexible zend forms

FormFactory will enable to drive your forms from your Entity annotations. It uses a default configuration for each attribute of your Entity and builds a Zend Form Element from it. For example a column of type 'string' will by default become a text input. These are completely configurable, columns can be ignored or customised to quickly build the form you need. Zend Form objects are returned for further customisations if required.

Example, generating a form from my User entity

$form = new FormFactory\Form('User', 'add');
echo $form->getZendForm();

or, you can use the quick static call, taking advantage of PHP 5.3's __callStatic method

echo FormFactory\Form::AdminUser('edit');

inherit / override config definitions from other forms

Any definitions provided for a form can be extended for other uses. for example if using ini configurations

[add]
;...some configurations...
[edit : add]
; removes the password element when editing
config.password.disabled = true;

Add a relationship of entities to a single form

You may want to use several Entities to generate your form. By passing a relationship to the form constructor additional attributes of those Entities will be added to your form. eg

$form = new FormFactory\Form(array('User' => array('UserPassword', 'UserAddress')), 'add');
echo $form->getZendForm();

Allows you to fall back to a native Zend_Form configuration

If the default configuration options aren't enough, you have the power of customising your form elements using typical zend_form configurations. This is very useful for adding validators / filters / decorators to your elements.

form.elements.username.type = text
form.elements.username.options.label = Username
form.elements.username.options.order = 0
form.elements.username.options.attribs.class = 'small usernameCheck'
form.elements.username.options.required = 1
form.elements.username.options.description = 'Must be greater than 3 characters'
form.elements.username.options.validators.user-exists.validator = "UserExists"

Creating elements using a native Zend_Form configuration will override any default FormFactory element with the same name.

Linking data to multi type element

Multi type form elements such as select boxes, radio buttons or check boxes require populating with a set of name / values. There are currently three ways to do this;

Add link data via a config file

You can set multiple field values directly via your form configuration by passing an array of names and values. Note the length of these arrays must be identical.

config.role_id.linkData.names = Administrator, Operator
config.role_id.linkData.values = 1, 2

Add link data by selecting from another entity

You may have a set of Data that you want to appear in a select element. Using the FormFactory adapters this data can be pulled in and injected into you form. For example, I want my Users Entity column 'role_id' to be a select field populated with the data currently persisted for that Entity.

config.role_id.linkEntity.entityName = Role
config.role_id.linkEntity.entityField = id
config.role_id.linkEntity.entityFieldValue = name

You can even filter on where status is equal to 1

config.role_id.linkEntity.filters.status = 1

Add Link data via a plugin hook

If you need to perform slightly more work to retrieve the multi field names and values you can use a plugin to do so. This class must extend the \FormFactory\Plugin\LinkData class and implement the getOptions() method like so;

class System_FormFactoryPlugin_ServiceHolidayBoard
    extends \FormFactory\Plugin\LinkData
{
  public function getOptions()
  {
    return \Entities\Repositories\ServiceHolidayRepository::$boardNames;
  }
}

and set in your config like;

config.{elementName}.linkDataPlugin = My_Plugin_Class

Has its own autoloader and bootstrapping mechanism

Library can be quickly bootstrapped by including a ClassLoader, and passing a path to the formfactory config file. for example..

require_once realpath(APPLICATION_PATH . '/../library/FormFactory/ClassLoader.php');
$formFactory = new FormFactory\Bootstrap(realpath(APPLICATION_PATH . '/configs/form/config.ini'));

Configure a caching mechanism for your config files

Its likely your configuration files are not going to change in a production environment, so you can pass in a caching mechanism to stop the library from having to re-read them every time a form is produced. This can be done like so;

ff.cache.frontend.name = Core;
ff.cache.frontend.options.lifetime = 30;
ff.cache.frontend.options.automatic_serialization = true;
ff.cache.backend.name = Apc;

Quick config options

Here are some of the common configurations I found myself needing when setting up simple forms.

config.{entityname}.value = {default value}
config.{entityname}.label = {label name}
config.{entityname}.required = true
config.{entityname}.disabled = {true}
config.disabled = {column1, column2, column3}

Its available now on github, have fun with it.

https://github.com/leedavis81/FormFactory

Requirements:

  1. PHP 5.3
  2. Doctrine 1.2 / 2
  3. Zend - Config, Form
comments powered by Disqus