Palo Verde

Web Application Developer

Automatic form gernation with Doctrine PHP (Part 1)

In an effort fill the need for a stand alone form generator I’m going to post a little mulit-part guide on how to make one, any input or revisions are appreciated.

Firstly, my need is for a command line tool that I can tell it what 
record to use and it will output an html form that I can then funnel 
into a file where I need it.  I can then edit the form to suite. At 
worst this will saves me the hassle of some redundant typing.
For part one I will show you how to generate a very basic form 
without relationships, you will have to edit or tweak the form to fit 
your needs.
Usage: 

#> ./doctrine_form_generator Articles > ../templates/article/form.html

The Doctrine_Table gives us what we need to know to make a form. 
Doctrine_Table::getColumnNames() will return an array of the column 
names for the current table. 
Doctrine_Table::getTypeOf(string $columnName) will return a string, 
the type of the column. 
Doctrine_Table::isIdentifier(string $columnName) will return bool if 
this is a primary key. 
Doctrine_Table::getRelations() returns an associative array of 
Doctrine_Relation_ForeignKey objects where the key is the alias name.
So for now we are just going to grab the columns of the specified 
table, and map their type to an html form input type, then spit out a 
form.
Here is the code sample 1

if (!$argv1) {
 
        throw new Exception(‘No Record Argument’); 
}

$recordName = $argv1;

We just made sure that they passed an argument, which would be the 
record name
Code sample 2

class FormInput {
       public $type;
       public $name;
       public $display; 

protected $typeMap = array( ‘integer’=>‘text’, ‘string’=>‘text’, ‘text’=>‘textarea’ ); public function __construct($type, $name, $displayName=null) { $this->name = $name; if ($displayName) { $this->display = $displayName; $this->type = $this->typeMap[$type]; return; } $this->type = $type; } public function render() { switch ($this->type) { case ‘text’: print "<p><b>{$this->display}</b></p>\n\t<input type=‘text’ ?¨name=’{$this->name}’ value=’‘ />\n\n"; â?¨ â?¨ â?¨ break; case ‘textarea’: ?¨ print "<p><b>{$this->display}</b></p>\n\t<textarea name=’{$this->name}’></textarea>\n\n"; break; case ‘hidden’: print "<input type=‘hidden’ name=’{$this->name}’ value=’‘ />\n\n"; break; } } }

Not pretty, but this will allow us to iterate through the columns and
map them to form input.  You can see that at this point we are only
supporting types of ‘integer’,‘string’ and ‘text’.  The default name
of the input is in this format “recordname[column_name]”
Code sample 3


&acirc;?&uml;$inputs = array(); 
require('doctrine.php'); 
$table = Doctrine::getTable($recordName); 
&acirc;?&uml;foreach ($table-&gt;getColumnNames() as $columnName) {
        $fieldName = $table-&gt;getClassnameToReturn().&quot;[$columnName]&quot;;
        if ($table-&gt;isIdentifier($columnName)) {
                $inputs[] = new FormInput('hidden', $fieldName);
        } else {
                $printableName = ucwords(str_replace('_',' ',$columnName));
                $inputs[] = new FormInput($table-&gt;getTypeOf($columnName), &acirc;?&uml;$fieldName, $printableName);
        } 
}

$inputs stores the input objects (from sample 2).   We are just 
iterating through the columns, and creating a form input based on the 
column type.
require('doctrine.php') is the file in which I include doctrine and 
load the models.
There is still one last piece we need for this to work 
Code sample 4

<form action="" method="post">
	<?php foreach ($inputs as $input): ?>â?¨		<?php $input->render(); ?> 
	<?php endforeach; ?>

<input type="submit" name="action" value="Save" />â?¨</form>

Now if you assemble that into one file you can run it from the command 
line as described above.. output looks like this:

&lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
       &lt;input type='hidden' name='Articles[id]' value='' /&gt; 
       &lt;p&gt;&lt;b&gt;Name&lt;/b&gt;&lt;/p&gt;
       &lt;input type='text' name='Articles[name]' value='' /&gt; 
       &lt;p&gt;&lt;b&gt;Content&lt;/b&gt;&lt;/p&gt;
       &lt;textarea name='Articles[content]'&gt;&lt;/textarea&gt; 
       &lt;input type=&quot;submit&quot; name=&quot;action&quot; value=&quot;Save&quot; /&gt; 
&lt;/form&gt;

The output is a little handy but less than ideal, so next part we will 
look at organizing the output as a set of templates so that we can 
also specify from the command line what template set we want to use, 
and code our own templates.
Also go over breaking out the form input so instead of calling a 
switch, there are specific input element objects that it returns, and 
the output of those is taken from the template set.  Then we can make 
special input items for dates and other fields.
Finally we will try and figure out relationships.  I currently use a 
separate object to build the select elements for hasOne relationships, 
but if we want the generator to do it, we will probably have to move 
towards having a small config file that tells us what field to use as 
the text in the relationship select, if there should be a blank value, 
etc.

2 comments

  1. Interesting. Looking forward for your further posts

    — Mytholog Mon, 25 Jan 2010

  2. Very good post, I am honored to be the first comment on this grate article.

    Alejandro Sat, 7 Nov 2009

Please insert the result of the arithmetical operation from the following image:

Please insert the result of the arithmetical operation from this image. =