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
â?¨$inputs = array();
require('doctrine.php');
$table = Doctrine::getTable($recordName);
â?¨foreach ($table->getColumnNames() as $columnName) {
$fieldName = $table->getClassnameToReturn()."[$columnName]";
if ($table->isIdentifier($columnName)) {
$inputs[] = new FormInput('hidden', $fieldName);
} else {
$printableName = ucwords(str_replace('_',' ',$columnName));
$inputs[] = new FormInput($table->getTypeOf($columnName), â?¨$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:
<form action="" method="post">
<input type='hidden' name='Articles[id]' value='' />
<p><b>Name</b></p>
<input type='text' name='Articles[name]' value='' />
<p><b>Content</b></p>
<textarea name='Articles[content]'></textarea>
<input type="submit" name="action" value="Save" />
</form>
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
Interesting. Looking forward for your further posts
— Mytholog Mon, 25 Jan 2010
Very good post, I am honored to be the first comment on this grate article.
— Alejandro Sat, 7 Nov 2009