Close search
Hoa

Hack book of Hoa\View

Views are complex and diversified systems. Hoa\View provides an interface able to match most of the requirements through a modular and extensible mechanism.

Table of contents

  1. Introduction
  2. One interface
  3. Example
  4. Conclusion

Introduction

A view is a mechanism allowing to graphically organize data for a user. There are many approaches that might be very different to each other and some of them can be very complex. The Hoa\View library provides one single interface that gathers the essential features in order to integrate a view inside any other library.

One interface

The main mechanism of a view is almost always the same:

Thus we have four notions: data, a “renderer”, a router and an output channel where to write the result.

The Hoa\View library provides only one interface: Hoa\View\Viewable. This latter defines a method for each preceding notion:

And that's all! To be more accurate, here are the expected returned values for each method:

Even if this approach may seem simplistic at first sight, this interface covers the majority of requirements, and allows you to write the view you like with your own tools or existing ones.

Example

The Hoa\Xyl library uses Hoa\View\Viewable, but it is a little bit complex for a first example (which must not prevent you to take a look at it!). That's why we are going to give a more simple and basic example.

Let's start to define what we want. We would like to have a mechanism of views and sub-views, answering to the following characteristics:

Let's start by writing the SuperView class:

class SuperView implements Hoa\View\Viewable
{
    protected $_in     = null;
    protected $_out    = null;
    protected $_data   = null;
    protected $_router = null;

    public function __construct(
        $in,
        Hoa\Stream\IStream\Out $out,
        Hoa\Router\Router $router = null,
        StdClass $data            = null
    ) {
        if (null === $data) {
            $data = new StdClass();
        }

        $this->_in     = $in;
        $this->_out    = $out;
        $this->_data   = $data;
        $this->_router = $router;

        return;
    }

    public function getOutputStream()
    {
        return $this->_out;
    }

    public function getData()
    {
        return $this->_data;
    }

    public function render()
    {
        $data   = $this->getData();
        $router = $this->getRouter();

        ob_start();
        require $this->_in;
        $content = ob_get_contents();
        ob_end_clean();

        $this->getOutputStream()->writeAll($content);

        return;
    }

    public function getRouter()
    {
        return $this->_router;
    }
}

Very classic. Our constructor asks a file to read, an output stream, an optional router, and optional data. We find our getOutputStream, getData, render and getRouter methods. The render method will include the $in file by having formerly declared two variables (in addition to $this), namely $data and $router, to ease the usage. Finally the line 46 uses the output stream to write computed data.

Now, let's test by writing a Test.php file:

// Output.
$output = new Hoa\Http\Response();

// Router.
$router = new Hoa\Router\Http();
$router->get('a', '/Foo\.html');

// View.
$superview   = new SuperView('Out.phtml', $output, $router);

// Data.
$data        = $superview->getData();
$data->title = 'foobar';
$data->foo   = (object) ['bar' => 'baz', 'qux' => 'hop'];

// Render.
$superview->render();

And finally, let's write our Out.phtml file:

<h1><?php echo $data->title; ?></h1>

Now, let's take a look at the result of the execution of Test.php:

$ php Test.php
<h1>foobar</h1>

Excellent. Now, we will add the import method to create sub-views. Thus in the SuperView class:

    public function import($in, $data = null)
    {
        $new = new static(
            $in,
            $this->getOutputStream(),
            $this->getRouter(),
            $data
        );
        $new->render();

        return;
    }

We use the static keyword to make a static reference to the class itself or to its children (see Late Static Bindings).

Let's modify Out.phtml in order that it uses Sub.phtml which will be a new view. To this sub-view, we will just give a subset of data (only foo):

<h1><?php echo $data->title; ?></h1>

<?php $this->import('Sub.phtml', $data->foo); ?>

Then in Sub.phtml, we are free to use those data. Bonus: we will use the router to create a link (as a reminder, the router defined the a rule to /Foo.html). Thus:

<p>Sub-view! This is a link:
   <a href="<?php echo $router->unroute('a'); ?>">click click</a>!</p>

<ul>
<?php foreach($data as $key => $value): ?>
    <li><?php echo $key, ' => ', $value; ?></li>
<?php endforeach; ?>
</ul>

Let's execute Test.php again to see the result:

$ php Test.php
<h1>foobar</h1>

<p>Sub-view! This is a link:
   <a href="/Foo.html">click click</a>!</p>

<ul>
    <li>bar => baz</li>
    <li>qux => hop</li>
</ul>

Our sub-view is in place, with our link and our isolated data (we iterate over all of them without having title, only foo).

This SuperView class is very basic but the performances are interesting because the result of the rendering is sent directly in the output stream, without manipulating heavy strings. This becomes particularly interesting for example a lot of nested sub-views are handled/.

Conclusion

The Hoa\View library defines only one interface with four methods. After a period of use we understand that those methods are enough to describe and manipulate many view systems that may be complex.

An error or a suggestion about the documentation? Contributions are welcome!

Comments

menu