PHP: View Object Collections and the Composite View Design

In this second part of a series, I demonstrate how to create a concrete composite view class, capable of rendering single and multiple view templates via the same “render()” method. The definition of this brand new class not only shows the logic that drives the Composite View pattern, but reveals how the pattern takes advantage of the benefits offered by Composition.

As its name suggests, the Composite View design pattern is a subtle variation of the “standard” Composite, which permits you to manipulate indiscriminately a single view object and a collection of them through the same interface. As with many other popular patterns, the implementation of Composite View may vary across different programming languages and applications; when used specifically in PHP, however, it provides a remarkable flexibility for handling (X)HTML templates in all sorts of clever ways, including the creation of nested views and master web page layouts.

Even though at first glance the application of the Composite View pattern in PHP appears to be difficult, suitable only for experienced developers, the truth is its implementation is a fairly simple task that can be mastered with only an average background in the object-oriented programming paradigm.

To demonstrate the truth of my words, in the introductory part of this series I built an abstract parent class for defining the generic structure and behavior of view objects. What’s more, the current functionality of this abstract parent allows you to dynamically assign properties to these objects, and individually render their associated templates via a simple method called, not surprisingly, “render().”

Having at our disposal a class that does all of this with a single view object is fine. But it would be even better to create a class capable of rendering single and multiple view templates equally through the same “render()” method. After all, that is exactly the purpose of the Composite View pattern, isn’t it?

To fit this requirement, in this second tutorial of the series I’m going to define such a class, which will be a refined implementation of the abstract parent created previously.

Are you ready to learn the full details of this refinement process? Then jump forward and begin reading!

{mospagebreak title=Review: building an abstract composite view class}

As usual, before I start showing how to create a concrete composite view class that can manipulate single and multiple view objects via the same interface, I’d like to spend a few moments reintroducing the definition of the abstract parent created in the first part of this series.

Here’s how this abstract class was originally conceived: 

(AbstractView.php)


<?php

abstract class AbstractView
{
    protected $_template = ‘default.php’;
    protected $_properties = array();

    // constructor
    public function __construct($template = ”, array $data = array())
    {
        if ($template !== ”) {
            $this->setTemplate($template);
        }
        if (!empty($data)) {
            foreach ($data as $name => $value) {
                $this->$name = $value;
            }
        }
    }
   
    // set a new view template
    public function setTemplate($template)
    {
        $template = $template . ‘.php’;
        if (!file_exists($template)) {
            throw new ViewException(‘The specified view template does not exist.’);  
        }
        $this->_template = $template;
    }
     
    // get the view template
    public function getTemplate()
    {
        return $this->_template;
    }
     
    // set a new property for the view
    public function __set($name, $value)
    {
        $this->_properties[$name] = $value;
    }

    // get the specified property from the view
    public function __get($name)
    {
        if (!isset($this->_properties[$name])) {
            throw new ViewException(‘The requested property is not valid for this view.’);     
        }
        return $this->_properties[$name];
    }

    // remove the specified property from the view
    public function __unset($name)
    {
        if (isset($this->_properties[$name])) {
            unset($this->_properties[$name]);
        }
    }
   
    // add a new view (implemented by view subclasses)
    abstract public function addView(AbstractView $view);
   
    // remove a view (implemented by view subclasses)
    abstract public function removeView(AbstractView $view);
   
    // render the view template
    public function render()
    {
        if ($this->_template !== ”) {
           extract($this->_properties);
           ob_start();
           include($this->_template);
           return ob_get_clean();
        }
    }
}// End AbstractView class

 

<?php

class ViewException extends Exception{}

If you are familiar with the “__set()” and “__get()” magic PHP 5 methods, the  logic driving the previous “AbstractView” class should be pretty easy for you to grasp, as it uses them to set and retrieve undeclared properties from a view object. Also, the class implements an additional method called “render()” to populate the template associated with each view with these properties. That was fairly simple to follow, right?

Even though all of the concrete methods just explained do play a relevant role, the most interesting ones are “addView()” and “removeView().” They define the interface required for dealing transparently with single and multiple view objects. Since the earlier abstract parent defined the methods in question as abstract, they must be concretely implemented by a subclass (this is Inheritance 101).

That’s precisely what I’m going to do in the following segment. To learn more about this process, click on the link below and keep reading.

{mospagebreak title=Adding and removing view objects}

One of the most appealing — yet confusing – aspects of the Composite pattern, including the Composite View, is that it allows you to handle single objects and collections of them via Composition. Of course, the best way to understand how this can be achieved is by looking at an example. In keeping with this I suggest you look at the definition of the brand new class listed below. It implements the “addView()” and “removeView()” abstract methods declared by the previous “AbstractView” parent. Check it out:

(View.php)


<?php

class View extends AbstractView
{
    protected $_views = array();
       
    // factory method (chainable)
    public static function factory($view, array $data = array())
    {
        return new self($viewfile, $data);
    }
   
    // add a new view object
    public function addView(AbstractView $view)
    {
        if (!in_array($view, $this->_views, TRUE)) {
            $this->_views[] = $view; 
        }
        return $this;
    }
   
    // remove an existing view object
    public function removeView(AbstractView $view)
    {
        if (in_array($view, $this->_views, TRUE)) {
            $views = array();
            foreach ($this->_views as $_view) {
                if ($_view !== $view) {
                    $views[] = $_view;
                }
            }
            $this->_views = $views;
        }
        return $this;
    }
}

As I expressed before, the above “View” class is nothing but a refined implementation of its abstract parent, in this case responsible for adding functionality to the corresponding “addView()” and “removeView()” methods. But why are these methods so useful in the implementation of a Composite View schema? Well, as you can see, they allow you to aggregate (and remove) different view objects, which are stored in a protected array. Therefore, by calling the “render()” method of each of the stored objects (be it just one or many), it would be possible to render their associated templates in one go!

This means that the “View” class would permit you to handle single and multiple view objects indiscriminately, which is exactly the function of a Composite class. Considering that this functionality must be implemented through the same interface, the last thing we need to do is add an enhanced “render()” method to the class. This method will be capable of rendering its own template and the ones corresponding to the injected view objects.

The definition of the aforementioned method will be shown in the upcoming section. So hurry up and read the following lines.

{mospagebreak title=Adding an enhanced render() method}

In consonance with the concepts deployed in the preceding section, below I included for you the finished version of the concrete “View” class, which this time implements an improved “render()” method. Here it is:

(View.php)


<?php

class View extends AbstractView
{
    protected $_views = array();
       
    // factory method (chainable)
    public static function factory($view, array $data = array())
    {
        return new self($viewfile, $data);
    }
   
    // add a new view object
    public function addView(AbstractView $view)
    {
        if (!in_array($view, $this->_views, TRUE)) {
            $this->_views[] = $view; 
        }
        return $this;
    }
   
    // remove an existing view object
    public function removeView(AbstractView $view)
    {
        if (in_array($view, $this->_views, TRUE)) {
            $views = array();
            foreach ($this->_views as $_view) {
                if ($_view !== $view) {
                    $views[] = $_view;
                }
            }
            $this->_views = $views;
        }
        return $this;
    }
   
    // render each partial view (leaf) and optionally the composite view
    public function render()
    {
        $innerView = ”;
        if (!empty($this->_views)) {
            foreach ($this->_views as $view) {
                $innerView .= $view->render();
            }
            $this->content = $innerView;
        }
        $compositeView = parent::render();   
        return !empty($compositeView) ? $compositeView : $innerView;
    }  
}// End View class

As you can see above, the revamped “render()” method implemented by the concrete “View” class iterates over all of the injected view objects and stores their rendered templates in a property called “content.” After that, it renders its own template and returns the outputs to client code for further processing. With this method already digested, do you realize that creating a class capable of manipulating a single view object and a group of them via the same method wasn’t such a hard thing? I bet you do!

Naturally, the method can be improved or tweaked to fit more specific requirements. For the moment, however, I’ll keep it simple and uncluttered, so that it’ll be easier to grasp its underlying logic.

Now that the previous composite view class is up and running, the next logical step is to create another one responsible for spawning single view objects. But for the sake of clarity, this subject will be discussed in the upcoming tutorial.

Final thoughts

In this second installment of the series, I demonstrated how easy it is to create a concrete composite view class, capable of rendering single and multiple view templates indiscriminately via the same “render()” method. As you just saw, the definition of this brand new class not only shows clearly the logic that drives the Composite View pattern, but it reveals how the pattern takes advantage of the benefits offered by Composition.

It’s fair to note, however, that the implementation of the pattern is still incomplete, It’s necessary to built at least an additional concrete class that permits us to create single view objects (this functionality was previously encapsulated within an abstract parent). That’s exactly the topic that I plan to discuss in the next tutorial, so don’t miss it!

Google+ Comments

Google+ Comments