Posts by Tag "MVC"

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.

On Publishing Webservices within MVC Frameworks

Webservices are a very important part in todays enterprise applications. They tie applications of different age or programming languages together or allow applications of different subcontracters to speak to each other. Because they use HTTP, a stateless network protocol, considerable overhead floods the pipes when you use them, which should be minimized.

Martin Fowler writes in his PEAA book, that if you have the option not to use distributed objects (which are implemented via webservice) you should not distribute them. Considerable effort has to be brought into keeping complex webservices performant.

Still people make mistakes about webservices all the time (me included for example proposing a dispatcher for the ZF that could be used for webservices).

When people report problems with the Zend Soap component they often post a stripped down example that involes their webservice being instantiated within a controller. This is a very bad decision based on different arguments:

  • Dispatching overhead: Dispatching, Routing, Pre- and Postfiltering is costly in all frameworks. You give up the performance of having numerous PHP scripts that act as controller on their own. You get centralized filtering, authentication and other benefits. But those benefits generally do not aply to XML, JSON or SOAP requests, because you cannot parse them or access their properties. You give up the performance of a page controller for webservices to gain mostly nothing.
  • HTTP Request uselessness: Web frameworks work with HTTP request objects. The request of webservices facilitates HTTP to act as a far more complex request. No framework I know off, allows to work with the webservice requests outside the Webservice handler. What a SOAP or XML-RPC request does in your MVC is only get passed through numerous costly stages that offer no benefit, before it hits the target. Only the parsing of HTTP-Headers might offer additional benefit, but the gain is low, since they are available to PHP scripts at no cost.
  • Webservices already seperate concerns: Take the PHP SOAPServer as an example. It is an MVC application on its own, the controlling aspect of the SOAPServer parses the SOAP Request and sends it to the model, a class given by the user, which in turn works and returns the result as an SOAP Response View. You have to decouple model and view for a webservice handler otherwise it would generate invalid responses. Why nest a perfectly separated operation into another one? You gain no more of this additional separation, except performance decrease.

So what are good practices to implement webservices?

  • Use a page controller that generates no MVC overhead. In context of the Zend Framework: Add a new php script to your web root and add a new route into your .htaccess file that redirectes the desired location of the webservice to the script that overwrites the standard catch-all incoming requests to the front controller script.
  • Use the proxy pattern and the invaluable __call() method to implement wrapper objects for authentication and session management of the webservice. These classes can easily be reused by all webservice page controllers of your site. If you do your homework you can even share parts of these objects inside your Web-MVC application to keep the code DRY. Those proxies keep authentication logic out of your service class.
  • Use the remote facade pattern to implement a few, powerful methods that delegate the service request to underlying domain objects. Never ever publish direct access to domain objects with your webservices. As a rule of thumb, talking to a webservice during a logical operation should never involve more than one or two calls. The first call is for data fetching, the second for data saving. Authentication should be handled via HTTP Authentication to save an additional call.

If you follow these simple rules, you should get around the performance issues that generally come with webservices, without loosing flexibility at all.

REST and Ajax Aware controllers in ezcMvcTools

There are essentially two major different ways to implement a restful application using a web framework. You either implement a router that routes to different controller actions based on the HTTP method used on a requested resource. This is the fancy way, which sadly is not always so practical because many browsers do not support sending more than GET and POST requests. The other way would be to define suburls of a resource such as /user/1/delete for the resource /user/1 and take GET a a request for deleting and POST for the confirmation of the delete.

ezcMvcTools HTTP Request Parser and routing mechanisms currently offer no real help to decide on this issues, but its easy to extend this missing functionality. What we first add are simple checks of the current http request method. We extend ezcMvcHttpRequestParser which will return a derived ezcMvcRequest object that implements 7 new methods: isPost(), isGet(), isDelete(), isPut(), isXmlHttpRequest(), isMethod() and getMethod(). These methods can now be easily used on the request object to determine which action will be undertaken:

class myMvcRequest extends ezcMvcRequest{  public function isPost()  {    return $this->isMethod("POST");  }  public function isGet()  {    return $this->isMethod("GET");  }  public function isPut()  {    return $this->isMethod("PUT");  }  public function isDelete()  {    return $this->isMethod("DELETE");  }  public function isXmlHttpRequest()  {    if(isset($this->raw['HTTP_X_REQUESTED_WITH'])      && strtolower($this->raw['HTTP_X_REQUESTED_WITH']) == "xmlhttprequest") {      return true;    }    return false;  }  public function getMethod()  {    if(isset($this->raw['REQUEST_METHOD']))      return strtolower($this->raw['REQUEST_METHOD']);    }    return false;  }  public function isMethod($method)  {    if(isset($this->raw['REQUEST_METHOD']) &&       $this->getMethod() == strtolower($method)) {      return true;    }    return false;  }}class myMvcHttpRequestParser extends ezcMvcHttpRequestParser{  /**   * Uses the data from the superglobals.   *   * @return ezcMvcRequest   */  public function createRequest()  {    $this->request = new myMvcRequest;    $this->processStandardHeaders();    $this->processAcceptHeaders();    $this->processUserAgentHeaders();    $this->processFiles();    $this->processAuthVars();    $this->request->raw = &$_SERVER;    return $this->request;  }}

This helps us to implement simple decision mechanisms in a single controller action that takes both POST and GET requests. From the point separation of concerns this is not a good design decision. We need routes that can point to different controller actions based on their request method, so that two requests GET /user/1/delete and POST /user/1/delete lead to different methods, for example userController::doDeleteDialog and userController::doDelete. We will simply extend the ezcMvcRailsRoute to support decision based on http request methods:

class myMvcRestRoute extends ezcMvcRailsRoute{  protected $method;  public function __construct( $method, $pattern, $controllerClassName, $action = null, array $defaultValues = array() )  {    $this->method = $method;    parent::__construct($pattern, $controllerClassName, $action, $defaultValues);  }  public function matches( ezcMvcRequest $request )  {    if(strtolower($this->method) == strtolower($request->raw['REQUEST_METHOD'])) {      return parent::matches($request);    }    return null;  }}

This very simple extension is independent of the advanced request parser given above, so you could use it separatly. In the light of our delete user example, you would use the new router in the following way:

class myRouter extends ezcMvcRouter{  public function createRoutes()  {    return array(      new myMvcRestRoute( 'GET', '/users/:id/delete', 'UserController', 'deleteDialog' ),      new myMvcRestRoute( 'POST', '/users/:id/delete', 'UserController', 'delete' ),    );  }}

We have now simple rest route support in our ezcMvcTools application.

ezComponents 2008.2 Beta - Mvc separation for win

I have written on the new ezComponents MvcTools component before already. Just yesterday the beta of this component was released with the general beta of the 2008.2 version of ezComponents. Several bugfixes and enhancements were included into the MvcTools which make it a perfect component for any Mvc based application.

I reviewed lots of the code myself and can only say i love the beauty of the code. Its very simple but by default the most powerful mvc solution in the PHP market. People working with unittests will like it very much, since all the parts are perfectly separated from each other allowing to test controllers, views, routers and filters in complete separation.

To show one very simple example howto benefit of the seperation of view and controllers. If we need a view in both html and pdf, this should generally make no difference for the controller. We add two routes, one for the pdf one for the html view that execute the same controller and action:

class myRouter extends ezcMvcRouter{ public function createRoutes() {  return array(   new ezcMvcRailsRoute( '/pdf', 'SomeController', 'index'),   new ezcMvcRailsRoute( '/', 'SomeController', index' ),  ); }}class SomeController extends ezcMvcController{ public function doIndex() {  $result = new ezcMvcResult();  $result->variables['items'] = Model::retrieveLotsOfItems();  return $result; }}

Now howto decide between PDF and Html view? We use the createView method of our dispatcher configuration, but we still only need the http response writer, nothing more.

class myMvcConfiguration implements ezcMvcDispatcherConfiguration { [...] function createView( ezcMvcRoutingInformation $routeInfo, ezcMvcRequest $request, ezcMvcResult $result ) {  if(strstr($routeInfo->matchedRoute, "/pdf")) {   return new myHtmlView( $request, $result );  } else {   return new myPdfView( $request, $result );  } } function createResponseWriter( ezcMvcRoutingInformation $routeInfo, ezcMvcRequest $request, ezcMvcResult $result, ezcMvcResponse $response ) {  return new ezcMvcHttpResponseWriter( $response ); } [...]}

Now both myHtmlView and myPdfView can create their ezcMvcResponse objects that fill the response body depending on their type. Please note that overwritting createResponseBody() in myPdfView is a shortcut that circumventes me having to write a new PDF View Handler (which would be the way to go).

class myHtmlView extends ezcMvcView { function createZones( $layout ) {  $zones = array();   $zones[] = new ezcMvcPhpViewHandler( 'content', '../templates/index.phtml' );  $zones[] = new ezcMvcPhpViewHandler( 'page_layout', '../templates/layout.phtml' );  return $zones; }}class myPdfView extends ezcMvcView { function createZones() {  // empty, abstract method that has to be defined. } function createResponseBody() {  // Set PDF Content-Type Response Header  $this->result->content->type = "application/pdf";  $pdf = new Zend_Pdf();  // do pdf stuff  return $pdf->render(); }}

Now all the the logic that is potentially in the controller is completly seperated from the view handling that may depend on the routing information not on the controller. And views can be tested seperatly from the controller result. Testability is very high.

Overwrite ezcMvcController - A bit more rapid

ezComponents gets Mvc in its 2008.2 version. I have played around a bit with the alpha version, since I am currently searching for a good framework for a high performance application. My first benchmarks on ezcMvc just say: wh000pie! Alot faster as compared to the Zend Framework.

Still ezcMvc is VERY loosly coupled and you have to write lots of lines to get where ZF gets you with less (more magic involved). As you can see from the tutorial on the ezcMvcTools component it is currently a bit unwieldily to work with ezcMvcController since you have to create and return result objects everywhere. This is very nice since it abstracts from the actual response type (could be www, mail, cli, anything).

I have created a very little extension of the ezcMvcController class that hopefully serves you quite some time. You can append variables to the ezcMvcResult object by calling the magic __get and __set on the controller. Plus it offers a method to use the ezcMvcInternalRedirect instead of the result. See for yourself:


class myController extends ezcMvcController
{
    protected $result;
 
    public function createResult()
    {
        $actionMethod = $this->createActionMethodName();
 
        if ( method_exists( $this, $actionMethod ) ) {
            $status = $this->$actionMethod();
            if($status != 0) {
                $this->getResult()->status = $status;
            }
            return $this->getResult();
        } else {
            throw new ezcMvcActionNotFoundException( $this->action );
        }
    }
 
    protected function _redirect($uri)
    {
        $request = clone $this->request;
        $request->uri = $uri;
        $this->result = new ezcMvcInternalRedirect($request);
    }
 
    public function __get($name)
    {
        if(isset($this->getResult()->variables[$name])) {
            return $this->getResult()->variables[$name];
        }
        return null;
    }
 
    public function __set($name, $value)
    {
        $this->getResult()->variables[$name] = $value;
    }
 
    public function __isset($name)
    {
        return isset($this->getResult()->variables[$name]);
    }
 
    protected function getResult()
    {
        if($this->result === null) {
            $this->result = new ezcMvcResult();
        }
        return $this->result;
    }
}

You can now use a controller in the following way:

class dashboardController extends myController
{
    public function doIndex()
    {
        $this->cookie = "Cookie!"; // Proxy to $ezcMvcResult->variables['cookie']
    }
 
    public function doRedirect()
    {
        $this->_redirect("/");
    }
}

Very nice! The next thing I have to extend in ezcMvc is automagical matching of controller and action names to view output names with a special View Handler that takes care of this. This saves another bunch of work you have to cope with in the current standard setup.