The current version of Zend Framework doesn’t include a way to use scaffolding in a script-in-development. Because I wanted to play with the framework, I’ve extended the Zend_Controller_Action class to make scaffolding possible. In this article I shall explain the code and give some feedback for the developers of the framework.
The main purpose for scaffolding is creating very basic pages for viewing and editing content in the database. In an early stage of implementation, scaffolding gives the developer a way to test certain functionality, without the need to work directly in the database. Besides that, scaffolding is used as a basis for the final implementation.
In this example, I’m using the Action and Router class from Akra’s DevNotes to create a simple application. In fact, de Scaffold class will extend the Akrabat_Action class, which hopefully in the future will be part of the framework itself.
h3. Creating the model
First of all, we need to create a new data object to apply our Scaffold class to. Create a new table in your favorite database and create a new model class for the database:
<?php
class tableName extends Zend_Db_Table { }
?>
‘tableName’ matches the name of the table you just created, in this case ‘table_name’. Make sure the column name which contains the primary id of the table is ‘id’ or define another primary column.
Creating the controller class
To manage the data inside the table, we want to create a controller class like this:
class tableNameController extends Fly_Scaffolding { }
The controller ‘tableName’ should give us the possibility to:
* Show a list of rows in the table
* Show a single row in the table
* Edit a row
* Delete a row
* Add a row
The Scaffolding class
The code for the Fly_Scaffolding class is:
<?php
/**
* This file contains the scaffolding class
*
*
* @copyright 2006 Edwin V.
* @license http://www.zend.com/license/3_0.txt PHP License 3.0
* @version CVS: $Id:$
* @link
*/
require_once 'Zend/Db/Inflector.php';
/**
* Scaffolding class, extends the Akrabat_Action class
* Provides the developer a simple CRUD interface for a table
*
* @copyright 2006 Edwin V.
* @license http://www.zend.com/license/3_0.txt PHP License 3.0
* version Release: @package_version
* @link
*/
class Fly_Scaffolding extends Akrabat_Controller_Action {
/**
* For name inflections.
*
* @var Zend_Db_Inflector
*/
static protected $_inflector;
/**
* The name of this controller
*
* @var string
*/
protected $_name;
/**
* The name of this entity, this is the table name without the given prefix
*
* @var string
*/
protected $_entityName;
/**
* The table to scaffold
*
* @var Zend_Db_Table
*/
private $_table;
/**
* The information about the table
*
* @var array
*/
private $_tableInfo;
/**
* Construct a new scaffolding object
*/
public function __construct($tableName = ""){
parent::__construct();
// Use the db inflector
self::$_inflector = new Zend_Db_Inflector();
if(empty($tableName)){
$this->_name = str_replace("Controller", "", get_class($this));
} else {
$this->_name = self::$_inflector->camelize($tableName);
}
// Replace fly with some kind of table prefix
$this->_entityName = $this->_name;
require_once("../application/models/".ucfirst($this->_name).".php");
$this->_table = new $this->_name();
$this->_tableInfo = $this->_table->info();
// Define the singular and plural name for use in the views
$this->nameSingular = self::$_inflector->singular($this->_entityName);
$this->namePlural = self::$_inflector->plural($this->_entityName);
$info = $this->_table->info();
$this->primary = strtolower($this->_tableInfo['primary']);
}
public function indexAction(){
$this->_redirect('list');
}
/**
* The list action shows a list of all rows in the table
*/
public function listAction(){
$info = $this->_table->info();
$this->columns = $info['cols'];
$this->items = $this->_table->fetchAll();
}
/**
* The show action shows a single row in the table
*/
public function showAction(){
$this->item = $this->_table->find($this->_getParam('id'));
// Create array because of bug
$data = $this->item->toArray();
$this->id = $data[$this->primary];
}
/**
* The edit action shows a form for editing a row in the table
*/
public function editAction(){
$this->item = $this->_table->find($this->_getParam('id'));
}
/**
* The update action updates the table with information from the edit form
*/
public function updateAction(){
// Add some kind of post method check
// check if post variables are available
$id = $_POST[$this->primary];
// We don't want to update the primary column
unset($_POST[$this->primary], $_POST['submit']);
$where = $this->_table->getAdapter()->quoteInto($this->primary." = ?", $id);
$this->_table->update($_POST, $where);
$this->_redirect('show/id/'.$id);
}
/**
* The add action displays a form for adding a new row to the table
*/
public function newAction(){
$this->columns = $this->_tableInfo['cols'];
}
/**
* The create action adds a new row to the table
*/
public function createAction(){
unset($_POST['submit']);
$id = $this->_table->insert($_POST);
$this->_redirect('show/id/'.$id);
}
/**
* The delete action deletes a row from the table
*/
public function destroyAction(){
$where = $this->_table->getAdapter()->quoteInto($this->primary." = ?", $this->_getParam('id'));
$this->_table->delete($where);
$this->_redirect('../../list');
}
}
This class extends the action class and defines eight new actions for the controller. For the list, show, edit and new action we create views to display the information delivered by this controller. The view files can be found in the zip file, together with an application to test the scaffolding class.
Suggestions for improvement of the Scaffold class
The given implementation of the Scaffold class is just an example of how it can be implemented. There are a lot of improvement points and possible security problems.
Some suggestions for improvement:
- Move the view files to the Zend Framework path, so users don’t have to copy it to their application path.
- Check if a view is available, if not, use the framework default view for the action (but therefor the above point should be solved first)
- Display form elements according to the column type
- Check if the request method of the update and create actions is post
Feedback
Things I’m missing in the current version of the framework and bugs I’ve found:
- There is no possibility to get the current url of the page. If you want to create a link to a different action in the same controller, you should always read the current url and do some tricky actions. Maybe a function like this can be interesting:
// Returns the value of the controller part and the action part of the url,
// in case of requesting 'tableNameController/edit/id/1' this would return 'tableNameController/edit'
$this->getUrl("controller/action");
- In the form helper functions, it is possible to define html tag arguments in the array. In case of the ‘disabled’ argument of a form element, giving a boolean always puts the disabled argument in the tag. Actually, you only want to add the disabled argument when the argument is true. In case of false, the output would be ‘disabled=”"‘, which still means that the element is disabled.
- A new function in the inflector class for creating human readable column names: ‘site_id’ should become ‘Site id’. Maybe make the inflector class available as a helper in a view?
- A way to detect if a request is post or get and if there are certain post variables available. For example in the update method in my class this can be useful. The update method should only be parsed succesful when the method is post and the post variables that existed in the edit form are available.
- With the Zend_Db_Table::update method, it is possible to update some rows in the table. The method doesn’t check if the given keys are valid columns in the table.
- “Notice: Undefined index: ID in e:public_htmlZFlibraryZendDbTableRow.php on line 95″ When using ID as a primary column, ZF sometimes makes the column lowercase. In this case, the column exists, but the value is stored in ‘id’, so this error is thrown.
- Create a special Zend_Db_Row property which contains the value of the primary key:
echo $this->item->_primary;
// instead of
$data = $this->item->toArray();
$info = $this->_table->info();
echo $data[$info['primary']];
- Make it possible to have table prefixes that do not need to be in the classname of the class that extends Zend_Db_Table or Fly_Scaffolding
- Improve the table classes to:
- Define validations for table contents which can easily be read to use in add/edit forms.
- Define relations between tables
- Make is possible to read the column types of the table to use appropriate form elements
I really hope that there will be some kind of scaffolding in the stable version of the Zend Framework. With one small class and a few views, the work of many developers can be made a lot easier.
Comments 6 Comments
“Check if a view is available, if not, use the framework default view for the action”
Is this not already possible with function noRoute() in the Controller_Action (sub)class?
The reason why I didn’t implement this feature, is because the view files are only placed in the application path. So the system doesn’t have any choice at this moment. Only when the first improvement point is implemented, the second one should be available too.
Why not just use CakePHP?
It’s a much better framework (especially since ZF is not a real framework), and it already supports much more advanced scaffolding.
http://www.cakephp.org/
Nice work!
I had to add
$view->setHelperPath(‘../library/Zend/View/Helper’);
to just after line 51 in index.php in order to get it work though.
Rob…
@Rob
Thanks, I updated the zip file with the new index.php file.
@nate
Maybe that’s just the reason why I’m using ZF and not CakePHP. In ZF it is easy to create your own application structure, while CakePHP does it all for you. Nothing is stopping me from creating ‘much more advanced scaffolding’ in ZF.
[...] There is no official zend framework scaffolding implementation out there there are a few pieces of code here and there, have a look at these links # Edwin V. Zend Framework: Scaffolding (written 2years ago!!) # whitewashing.de :: Introducing: Zend Controller Scaffolding :: Error # Avesta – Zend-based PHP5 web framework a few proposals are on the way at Zend: Zend_Build – Wil Sinclair – Zend Framework Wiki Zend_Controller_Front_Scaffold – Zend Framework Wiki zf-tool : ZFTool __________________ =E.PIA= == WebmasterBulletin.net : website design, development and optimization [...]