Posts by Tag "DesignPatterns"

Subscribe to posts of this tag

Zend_Form and the Model: Yet another perspective using a Mediator

Matthew Weier O'Phinney of the Zend Framework Devteam wrote a controversial post on integrating Zend_Form and the Model last month. He separated concerns of view and model that communicate via a Form, by calling just thee validation functions on the Form inside the mode. On request you could retrieve the model to the controller and view layers. I already wrote into his comments that I didn't like the solution because it relys on implicit rules for the developers to use the Form component correctly in all layers. Additionally the building of the form using this approach would be performed inside the model, although strictly speaking this is responsibility of the View Layer. Another negative point is duplication of input filtering code that has to be performed to use certain variables inside the controller or when different forms talk with the same model.

Jani took it up and proposed writing validators for forms and attaching them to the Form as sort of a mediator. I am not a fan of this approach either, because the validator would have to include domain logic but is not really a part of the domain logic anymore but just a validator. Developers might forget using the validator inside the model for all their actions or there would be duplication of code in some places. In a perfect world, only functions of the models public interface should be called for validation.

My personal favorite for Form and Model integration is a mediator object between the two layers. Your model will have to include an additional interface with one function acceptFormRequest($values); which accepts an array of validated Zend Form field values. It then tries to apply the validated data into a record. Additional required validations of the model can take place in this function, which separates the concerns of Form validation and Model data validation. Still the mediator merges those differences together: You can throw an Exception and it will be attached as a custom error message to the Form. The following very short code will show the required interface and the mediator code. This code is very simple and might produce maintenance overhead fast, but I propose some refactoring enhancements later in the discussion.

interface WW_Model_AcceptFormRequest
{
    /**
     * Acceept a form request
     * @param array $values
     * @return WW_Record_Interface
     */
    public function acceptFormRequest($values);
}
class WW_Model_FormMediator
{
    /**
     * Try to push the form request to the model
     * 
     * @param Zend_Form $form
     * @param WW_Model_AcceptFormRequest $model
     * @return WW_Record_Interface
     */
    public function pushFormToModel(Zend_Form $form, WW_Model_AcceptFormRequest $model)
    {
        if(!$form->isValid()) {
            throw new Exception("Form not valid!");
        } else {
            $values = $form->getValues();
            try {
                $record = $model->acceptFormRequest($values);
            } catch(Exception $e) {
                // This exception message comes from the model, because validation failed
                $form->addErrorMessage($e->getMessage());
                throw new Exception("Form request not accepted by model!");
            }
        }
        return $record;
    }
}

You can see the mediator has two different stages where errors can occour: When the form is not valid or the model is not valid. Both exits can be catched inside the controller and are the indicator that the form has to be displayed again for further input corrections. When successful the model returns a valid record that applies to the form and model requirements and can be displayed. If this record should be persistent this would have been done inside the acceptFormRequest function already. An example using a very simple Model using the a BankAccount example. We have a form that validates all the incoming request data for a withdrawel of money, though does not validate it against the models internal state. Our BankAccountModel implements the WW_Model_AcceptFormRequest interface and returns a valid BankAccount. If found the given amount is withdrawn.

class BankAccountModel implements WW_Model_AcceptFormRequest {
    public function acceptFormRequest($values)
    {
        $bankAccount = $this->getBankAccountBy($values['bankAccountNumber'], $values['pin']);
        if($values['action'] == "withdraw") {
            $bankAccount->withdraw($values['amount']);
            $this->save($bankAccount);
        } else {
            // unknown action...
        }
    }
    public function getBankAccountBy($key, $password) {
        // Find by Primary Key returning 'BankAccount' instance or exception if not found.
    }
    public function save(BankAccount $ba) {
        // Sql for saving the Bank Account
    }
}
 
class BankAccount
{
    public function withdraw($amount)
    {
        if( ($this->getBalance()-$amount) < 0 ) {
            throw new Exception("You cannot withdraw more money than your bank account holds!");
        }
        $this->balance -= $amount;
    }
}

Two exceptions might be thrown in this case: The Bank Account number does not exist or the password is wrong. Or you are not allowed to withdraw the given amount of money. If any of those exceptions is thrown the Model does not accept the form data and the form will have to be displayed again for the client showing the new error message that was returned from the model. The controller handling this process would lool like this:

class BankAccountController extends Zend_Controller_Action {
    public function performWithdrawlAction() {
        $form = new BankAccountWithdrawlForm(); // extends Zend_Form and builds the form
 
        if($this->getRequest()->isPost()) {
            $mediator         = new WW_Model_FormMediator();
            $bankAccountModel = new BankAccountModel();
            try {
                $bankAccount = $mediator->pushFormToModel($form, $bankAccountModel);
 
                $this->view->assign('bankAccount', $bankAccount); // Show new balance in view!
            } catch(Exception $e) {
                $this->view->assign('withdrawlForm', $form);
                $this->_redirect('showWithdrawl');
            }
        } else {
            $this->view->assign('withdrawlForm', $form);
            $this->_redirect('showWithdrawl');
        }
    }
}

You can see the mediator tightly integrates Form and Model without both components knowing too much of each other. Still you can add error messages recieved from the model into the Form and redisplay it. One negative point of this approach is the fact that you only have one method for accepting form data, which could result in variable checking and redispatching in the case of many different operations that can be performed on the same model. For this case you might want to either:

  1. Rewrite the mediator to accept a specific model class (not the interface) and call the required custom method that matches the forms request. (Best approach for separation concerns)
  2. Rewrite the mediator to also pass the get_class($form); value to the model for decision making (Faster approach)

There is still some overhead on using the mediator. Since its generic you could build an Action Helper for it and use the direct call mechanism to save some lines of code.

Dependency Injection via Interface in PHP: An example

Personal projects and work currently both force me to think about application design and I came accross dependency injection the other day. Its quite a complex Design Pattern and hard to grasp, even with the number of examples you can find in the internet. Basically Dependency Injection means you register components that might be a dependency for other components and when loading new classes via the dependency injection framework, it knows of this relationsships and assembles the object in need and all its dependencies and returns them fully initialized. This pattern leads to completly encapsulated objects where any class in the object graph can be exchanged for a new implementation without breaking all its dependencies. It also makes testing complex components and object relationsships very easy.

Asfar as I could find out, three different types of Dependency Injection implementations exist: Via Constructor, via Setter and via Interfaces. There exist implementations of Dependency Injection for Java (Spring) that are configured via XML config files and seem very complex. They are also hard to read in code I pressume, since you always have to be aware of the current application configuration. There are also some more lightweight implementations (PicoContainer) that assemble their relationsships in the application and work with config methods that act as configuration of the dependancies. Martin Folwer also discusses an implementation via interfaces, which seems most accessible for me.

All PHP clones of any dependancy injection framework seem to go the complex way via configuration though. With traits and multiple class inheritance on the horizon, some kind of interface injection seems mighty powerful though. So I implemented a really lightweight implementation of Interface Dependency Injection for PHP. The Container works with static method only, is therefore in the global scope, such that configuration is reduced to a minimum. Generally you have to write interfaces for object injection, for example:

interface InjectDbAdapter{  public function injectDbAdapter(Zend_Db_Adapter_Abstract $db);}

Other potential examples include "InjectLogger", "InjectSoapClient", or "InjectAppConfig". You then have to register a component for usage with this interface:

Whitewashing_Container::registerComponent('InjectDbAdapter', Zend_Db::factory($dbConfig));
Now any class that implements the InjectDbAdapter interface can be instantiated via:
$obj = Whitewashing_Container::load('Class');

and the loader takes care of calling the 'Class' implementation of injectDbAdapter with the given Database Adapter. A negative consequence of this approach is that you have to implement the inject methods for all interfaces in your concrete class implementations. With Traits (multiple class inheritence) being a new feature in PHP soon, injection via parent classes seems to become a very powerful approach though, which can handle the concrete implementations.

The Container takes care of all the dependancy building, so when testing your components you can register lots of mock objects. You can also exchange dependancies for only a subset of objects very easily. I implemented a method "Whitewashing_Container::registerClassComponent", which registers a dependency component that is used with higher priority in construction of the given class. You can also specify a third parameter $localInterfaceOverride for the highest priority:

$obj = Whitewashing_Container::load('Class', null, array('InjectDbAdapter' => $newDbAdapter));

Speedwise the usage of the Container reduces class instatiation by about 50%, from 0.5 sec for 10000 classes with setting dependencies to 1 sec on my machine. But naturally only classes with dependancies and lots of them should be using this mechanism and with good application design, this shouldn't be to many. You can download Container and Example sourcecode, to take a look. Currently this Container is not really creating new dependencies for each new class generation but rather inverts the usage of a registry. It should be an easy task to extend the registering method to decide between new class generation and using the globally registered class instance.

Teaching Zend_Form some MVC

Lots of people complain that Zend Form runs counter to the MVC spirit, because it handles validation, business logic and view elements all in one acting as Model, Controller and View.

Extending the Zend Form component to handle all this different aspects in different layers of the application is rather easy though. What we want of a MVC compatible Form object is the following:

  • The model escapes and validates all the data that is put into the form.
  • The view decides on how the form is displayed.
  • The controller moves data from the model to the view and back, handling the stages of the form request.

The first step is, allowing any Model that implements Zend_Validator_Interface to hook into the Zend_Form Validation process. We generate a new class, WW_Form_Mvc and allow a function setModel() to insert any Model object that implements Zend_Validator_Interface into the form. We extend isValid(), getMessages() and getErrors() to not only check all the form elements validators, but also the models validators. Please note that the array_merge() solution is not the correct way of how this snippet should work. Any merge operation of the messages and errors has to be on a field key level, which is not currently done.

class WW_Form_Mvc extends Zend_Form
{
    protected $model = null;
 
    /**
     * Extends isValid() method of Zend Form to check for validity of specified model
     * @param Array $data
     * @return Boolean
     */
    public function isValid($data)
    {
        $valid = parent::isValid($data);
 
        if($valid == true && !is_null($model)) {
            $valid = $this->model->isValid($data) && $valid;
        }
        return $valid;
    }
 
    /**
     * Extends getMessages() Validator Interface implementation of Zend Form to also
     * return the messages of the Model validation.
     * @return Array
     */
    public function getMessages($name = null, $suppressArrayNotation = false)
    {
        $messages = parent::getMessages($name, $suppressArrayNotation);
 
        if(!is_null($model)) {
             $form_messages = $this->model->getMessages();
 
             $messages = array_merge($messages, $form_messages);
        }
 
        return $messages;
    }
 
        /**
     * Extends getErrors() Validator Interface implementation of Zend Form to also
     * return the errors of the Model validation.
     * @return Array
     */
    public function getErrors($name = null)
    {
         $messages = parent::getErrors($name);
 
        if(!is_null($model)) {
             $form_messages = $this->model->getErrors();
 
             $messages = array_merge($messages, $form_messages);
        }
 
        return $messages;
    }
 
    /**
     * Set a Model object, which has to implement Zend_Validate_Interface
     * @throws Zend_Exception
     */
    public function setModel($model)
    {
        if($model instanceof Zend_Validate_Interface) {
            $this->model = $model;
        } else {
             throw new Zend_Exception('WW_Form_Mvc expects a model of type Zend_Validate_Interface');   
        }
    }
}

We then extend the WW_Form_Mvc class to disable the automatic loading of decorators in the Constructor and additionally allow to pass a Model to the constructor as second argument:

class WW_Form_Mvc
{
    protected $model = null;
 
    /**
     * this overrides the original Zend_Form Constructor and skips
     * the decorator initilisation, because this is now being handled
     * by View Helpers
     */
    public function __construct($options=null, $model=null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        } elseif ($options instanceof Zend_Config) {
            $this->setConfig($options);
        }
 
        // Extensions...
        $this->init();   
 
        if(!is_null($model)) {
             $this->setModel($model);   
        }
    }
 
    // All the other stuff here
}

In our views we want to use helper methods to manage the displaying of the form. For each different style of form displaying, we can generate different helpers. For example a helper that would only apply the default decorators would look like this:

class WW_View_Helper_FormDefault
{
    /**
     * Load only default decorators on this Zend_Form object
     *
     * @param Zend_Form $form
     */
    public function formDefault(Zend_Form $form)
    {
        if($form instanceof Zend_Form) {
            $form->loadDefaultDecorators();
            return $form;
        }
    }
}

We can now use this helper and in any template say: <?= $this->formDefault($this->someForm); ?> We can now look at our controller action that implements this form and we will see that it does not look different from what we would have done before:

function formAction()
{
    $model = new SomeModel();
 
    $form = new WW_Form_Mvc();
    $form->setModel($model);
 
    // generate form here, adding elements and stuff
 
    if($form->isValid($_POST)) {
        $model->insert($form->getValues());
        $this->view->form = "Form was submitted!";
    } else {
        $this->view->form = $form;
    }
}

Isnt that nice? Now each part of the equation is doing what its supposed to do.