Skip to main content

Creating simple, extendible CRUD, using Zend Framework


The Form

Creating a nice, easy to maintain form, starts with a form class. Creating your forms procedurally in your controller/actions is horrid. please don’t do it.
To start with creating your form classes, you need your own namespace in your library. If you don’t have this, register one. This can be done by adding an _initAutoloading method to your Bootstrap. below is a short example. its not comprehensive (you can also do this in your ini i believe, but I use php configuration files similar to DASPRiD‘s, and i’m not trying to show how to set up autoloading here.)
class Bootstrap extends Zend_Application_Bootstrap
{
    //...
    /**
    * Initilise autoloader and library namespaces
    */
    public function _initAutoloading()
    {
        $loader = Zend_Loader_Autoloader::getInstance();
        $loader->registerNamespace('My_');
    }
    //...
}
Your next task, create a form. This is pretty simple. I will demonstrate using just a few fields. Create a file in your library called My/Form.php
class My_Form extends Zend_Form
{
    /**
    * Set up form fields, filtering and validation
    */
    public function init()
    {
        $this->setMethod(Zend_Form::METHOD_POST);
        //Username
        $this->addElement($uname = new Zend_Form_Element_Text('username'));
        $uname->setLabel('Username')
              ->addValidator('Db_NoRecordExists', false, array('table' => 'users',
                                                               'field' => 'username'))
              ->addValidator('Alnum', false, array('allowWhiteSpace' => true));
        //Email
        $this->addElement($email = new Zend_Form_Element_Text('email'));
        $email->setLabel('Email')
              ->addValidator('Db_NoRecordExists', false, array('table' => 'users',
                                                               'field' => 'email'))
              ->addValidator('EmailAddress', false);
        //First name
        $this->addElement($firstname = new Zend_Form_Element_Text('firstname'));
        $firstname->setLabel('First name');
        //Last name
        $this->addElement($lastname = new Zend_Form_Element_Text('lastname'));
        $lastname->setLabel('Last name');
    }
}
Now that your basic form is created, you need to add a method to perform your CRUD. I’m not actually going to cover the D (delete) as thats realy simple, and doesn’t require the form, which is the focus of this post.
This method should take an array for the post data, and a Zend_Db_Table_Row to provide the save functionality. In this example the DB columns have the same names as the form fields, this means we can set values with less code. As we are using Zend_Db, there should be no injection problems with this method, as everything is automagically quoted.
    //...
    public function process (array $post, Zend_Db_Table_Row  $row)
    {
        $this->setDefaults($row->toArray());
        // If the id (primary key) is null then this is a new row, else it is an existing record
        if (null !== $row->id) {
            // Record already exists, exclude it from db record validation.
            $this->getElement('username')
            ->addValidator('Db_NoRecordExists',
                                 false,
                                 array('table'    => 'users',
                                          'field'     => 'username',
                                          'exclude' => array ('field' => 'id',
                                                                      'value' => $row->id)));
            $this->getElement('email')
            ->addValidator('Db_NoRecordExists',
                                 false,
                                 array('table'    => 'users',
                                         'field'     => 'email',
                                         'exclude' => array ('field' => 'id',
                                                                    'value' => $row->id)));
        }
        if (sizeof($post) && $this->isValid($post)) {
            try {
                $row->setFromArray($this->getValues());
                $row->save();
                return true;
            } catch (Exception $e) {
                $this->addDescription('There was an error saving your details');
                return $this;
            }
        }
        return $this;
    }

The Controller / Action

Now that we have created our nice form (which is capable of CR and U)  now we need to use it from within out controller and model to perform the update or insert and interact with the user.
For this, you need 3 actions in your controller, Create, update, and delete (the delete I will not cover for the before mentioned reasons).
class UserController extends Zend_Controller_Action
{
 
    public function newAction ()
    {
        $this->_helper->ViewRenderer->setScriptAction('userform');
        $users = My_Users();
        $user = $users->getNewUserForm($this->getRequest()->getPost());
        if (true === $user) {
            $this->_helper->flashMessenger()->addMessage('New User Created');
            $this->_helper->redirector->gotoUrlAndExit(/** confirmation url here **/);
        }
        $this->view->form = $user;
    }
 
    public function editAction()
    {
        $this->_helper->ViewRenderer->setScriptAction('userform');
        if (false === ($id = $this->_getParam('id', false))) {
            throw new Exception ('Tampered URI');
        }
        $users = My_Users();
        $user = $users->getEditUserForm($this->getRequest()->getPost());
        if (true === $user) {
            $this->_helper->flashMessenger()->addMessage('Details Saved');
            $this->_helper->redirector->gotoUrlAndExit('user/edit/' . $id);
        }
        $this->view->form = $user;
    }
}
To explain what all this is about, Both actions do pretty similar things (infact this can be consolidated into a single action, but for clarity and ease of replication, I am using two).
There are two things which are pretty important in this, one is the use of the redirector, and the other is the checking of the ID before it is used. In my opinion when an id is passed in a url which is invalid, then a 404 should be raised. So in my error controllers i look for a variety of exception types so that i can debug, but only output a 404 for these in production.
Now looking more closely at the action code, we have calls to getxxxxUserForm() on our models. The return value of this is what we wish to inspect, as you could see in the process method we created earlier, we return boolean true, only when the record saves correctly.  So we do a strict check for this boolean value, and if it is true, we know that we can safely redirect our user. The redirect is an important step, it stops the browser trying to post the data again if the user clicks refresh after their record is created / updated. And the final note is that the flashMessenger is used to pass a message to inform that user that their action was completed on.
Also worth noting is that i have set both actions to use the same action name in the viewRenderer. This allows you to consolidate this common script into one. (repeat after me, Dont Repeat Yourself!). The view script for this is pretty simple.
foreach(Zend_Controller_Action_HelperBroker::getStaticHelper('flashMessenger') ->getMessages() as $message) : ?> echo $this->escape($message);?>
endforeach; </div> echo $this->form;?>

The Model

In the model, we now need two simple methods to tie the lot together. Now some people might (read: Will) argue that the model should be performing validation, I would argue that with this method, the Model *is* performing the validation, you are simply making use of a library class to perform this function, much in the same way you use Zend_Db_Row. Feel free to flame about this below, but I’m sticking with this, and it provides clean easy seperation, and provides a good clean mechanism to provide user feedback.
class My_Users
{
    /**
    * @var Zend_Db_Table_Abstract
    */
    protected $_table;
    //....
 
    /**
    * Retrieves a new user record, and processes a form against it.
    *
    * @param array $post
    * @return boolean|Zend_Form Boolean true if a save successfully occurs, a populated form on all other conditions
    */
    public function getNewUserForm(array $post)
    {
        $form = new My_Form();
        return $form->process($post, $this->_table->createRow());
    }
 
    /**
    * Retrieves a user record, and processes a form against it.
    *
    * @param array $post
    * @return boolean|Zend_Form Boolean true if a save successfully occurs, a populated form on all other conditions
    */
    public function getEditUserForm(array $post, $id)
    {
        $row = $this->_table->fetchRow($table->select()->where('id = ?', $id));
        $form = new My_Form();
        return $form->process($post, $row);
    }
}
Well, this really doesn’t need much explanation. this simply provides glue between the parts above. You can make these a little more complex, and infact you probably should (I do!) by sanitizing your input from the controller in the form of the user id, $id, in the edit method.

curtsy: ryan's blog

Comments

Popular posts from this blog

Financial Engineering

Financial Engineering: Key Concepts Financial engineering is a multidisciplinary field that combines financial theory, mathematics, and computer science to design and develop innovative financial products and solutions. Here's an in-depth look at the key concepts you mentioned: 1. Statistical Analysis Statistical analysis is a crucial component of financial engineering. It involves using statistical techniques to analyze and interpret financial data, such as: Hypothesis testing : to validate assumptions about financial data Regression analysis : to model relationships between variables Time series analysis : to forecast future values based on historical data Probability distributions : to model and analyze risk Statistical analysis helps financial engineers to identify trends, patterns, and correlations in financial data, which informs decision-making and risk management. 2. Machine Learning Machine learning is a subset of artificial intelligence that involves training algorithms t...

Wholesale Customer Solution with Magento Commerce

The client want to have a shop where regular customers to be able to see products with their retail price, while Wholesale partners to see the prices with ? discount. The extra condition: retail and wholesale prices hasn’t mathematical dependency. So, a product could be $100 for retail and $50 for whole sale and another one could be $60 retail and $50 wholesale. And of course retail users should not be able to see wholesale prices at all. Basically, I will explain what I did step-by-step, but in order to understand what I mean, you should be familiar with the basics of Magento. 1. Creating two magento websites, stores and views (Magento meaning of website of course) It’s done from from System->Manage Stores. The result is: Website | Store | View ———————————————— Retail->Retail->Default Wholesale->Wholesale->Default Both sites using the same category/product tree 2. Setting the price scope in System->Configuration->Catalog->Catalog->Price set drop-down to...

How to Prepare for AI Driven Career

  Introduction We are all living in our "ChatGPT moment" now. It happened when I asked ChatGPT to plan a 10-day holiday in rural India. Within seconds, I had a detailed list of activities and places to explore. The speed and usefulness of the response left me stunned, and I realized instantly that life would never be the same again. ChatGPT felt like a bombshell—years of hype about Artificial Intelligence had finally materialized into something tangible and accessible. Suddenly, AI wasn’t just theoretical; it was writing limericks, crafting decent marketing content, and even generating code. The world is still adjusting to this rapid shift. We’re in the middle of a technological revolution—one so fast and transformative that it’s hard to fully comprehend. This revolution brings both exciting opportunities and inevitable challenges. On the one hand, AI is enabling remarkable breakthroughs. It can detect anomalies in MRI scans that even seasoned doctors might miss. It can trans...