views:

2634

answers:

4

I'm relatively new to Object Oriented Programming. I pretty much understand the concepts, but practically speaking, I am having a really hard time finding information about how to best use Models in my Zend Framework applications.

Specifically, I have a Model (that doesn't extend anything) that doesn't use a Database Table. It uses getters and setters to access its protected members. I find myself struggling with how to best display this model in the view. I don't want logic in my view templates, but I find myself in the following situation:

In my controller:

$object = new Object();
$object->setName('Foo Bar');
$this->view->object = $object;

In my view template:

<h2><?= $this->object->getName() ?></h2>

I don't really like calling functions in my view templates but I don't know a better way to do this. I don't want my Model's members to be public, but I basically want to achieve the same results:

<h2><?= $this->object->name ?></h2>

I don't want my controller to do all the work of having to know everything about the model:

$object = new Object();
$object->setName('Foo Bar');
$this->view->object = $object;
$this->view->object->name = $object->getName();

What is the best practice of using models in the Zend Framework? Can anyone recommend any tutorial that would help me understand this Model/View dilemma in Zend Framework?

+1  A: 

If only other developers are going to be working with the templates, I would recommend just passing in the models. Here is a link to a Jeff Atwood post on MVC Understanding Model-View-Controller

zodeus
A: 

This isn't particularly geared towards zend framework, but the problem is rather general, in my mind.

It seems you're on the right path, instead of hard wiring the model to the view, inside the controller. You'd rather have that abstract, especially important if you're mapping a tonne of models, or mapping the same model over and over again.

Something simple would be to write a bunch of mapping functions, which would be fine if all you were avoiding is mapping the same thing over and over.

If you wanted a more general solution, that also addressed avoid writing that boiler plate code, and keeping things more DRY, I suggest creating a mapper class.

You could create a ViewModelMapper, which would take a model, or a few models and map them to the view.

class ViewModelMapper
{
    public function __construct($view)
    {
        //set the properties
    }

    public function addModel($model, $overrideViewProperty = null)
    {
        //add the model to the list of models to map, use the view's property 
        // name to figure out what to map it to? Allow for an override just in case.
    }

    public function getMappedView()
    {
        //take the view, map all the models
    }
}

You could then instance this on your controller, and setup the mappings, so the controller controls the mapping still, but all the boiler plate and coding logic is centralized, for all controller maps, except for the rare exceptions.

Saem
Please provide a relevant example that uses this Mapper class. As of right now all I see is a way to make adding Models to the View more complex.
Noah Goodrich
+2  A: 

One possibility is to use the magic __set and __get methods in PHP. I use them like so within my abstract Model class:

abstract class Model_Abstract
{
    protected $_data;

    // Private Data Members assigned to protected $_data
    public function __construct($data = null)
    {
     // Makes it so that I can pass in an associative array as well as 
     // an StdObject.
     if(!is_object($data)) {
      $data = (object) $data;
     }

     $this->_data = $data;

    }

    public function __get($key)
    {
     if (method_exists($this, '_get' . ucfirst($key))) {
      $method = '_get' . ucfirst($key);
      return $this->$method();   
     }
     else {
      return $this->_data->$key;
     } 
    }

    public function __set($key, $val)
    {
     if ( method_exists( $this, '_set' . ucfirst($key) ) ) {
      $method = '_set' . ucfirst($key);
      return $this->$method($val);   
     }
     else {
      $this->_data->$key = $val;
      return $this->_data->$key;
     }
    }
}


class Model_User extends Model_Abstract
{
    //Example overriding method for the property firstName in the $_data collection.
    protected function _getFirstName()
    {
     // Do some special processing and then output the first name.
    }
}

This makes it so that you can specify getters and setters for properties as necessary but makes it so that you don't have to define boilerplate functions for every property, just the ones where you want to do some sort of processing on it before returning the value. For example I use the functionality in a number of places to change ISO compliant dates (as stored in MySQL) into a more compact and readable format for users.

As far as what to place in your controller, I would recommend looking at this post for some specific feedback on what handling to place within your controller.

Some feel that they would rather have a helper that automatically loads models into the view and skirts the controller altogether. Personally I would say that within the context of Zend Framework and PHP it makes plenty of sense to pass models into the view from the controller because the state of the models in the view frequently depends on what came from the request (which should definitely be handled in the controller).

Update: As per criticisms in the comments, one thing that I would point out is that your database access layer and domain (or model) layer are really two different things, though with the Active Record they are blended together. I asked this question a while back and received some useful feedback on this matter. Whatever you decide to do with the model, you'll want to provide a consistent API for all domain objects regardless of where the data for the model comes from.

I suppose that one benefit offered by Saem's answer is that it offers the ability to directly map properties / function return values from one or more domain objects to the view object. Theoretically the usage within the view then looks like this:

// Mapped from Model_User::_data->last_name and Model_User::_data->first_name
$this->name
Noah Goodrich
This is actually a poor way of doing things. Not only do you give arbitrary access to all properties, whether they're ones that should be (un)serialized to/from a form or not, it also overloads the model with the concept of mapping, which is what was actually asked for.
Saem
Putting form logic into the model mixes the view and model which is also poor form. And as I already stated, overriding the default behavior is very simple and avoids the necessity of creating stupid boilerplate getters and setters.
Noah Goodrich
And if you look at the Zend_Db_Table_Row class you'll find that Zend actually uses a similar principle to expose the fields and their values from tables.
Noah Goodrich
A: 

For a good read on model architecture, read this post. It doesn't specifically talk about the view, but it's definitely worth reading.

I ended up adding a getViewClass() function to my models. The controller calls this function to get the protected variables it wouldn't otherwise have access to, and the view doesn't have to worry about calling any getters.

//controller
$object = new Object();
$object->setName('Foo Bar');
$this->view->object = $object->getViewClass();

//view template
<h2><?= $this->object->name ?></h2>

I don't know if there is a better way to get the job done in the Zend Framework, but this is one solution.

Andrew