Contents

Filters and Layout

After a response has been generated Rhubarb will run the response through a list of filters. Filters are installed by modules by adding ResponseFilter objects to their $responseFilters array, usually in the initialise method.

protected function initialise()
{
    parent::initialise();

    $this->responseFilters[] = new MyOutputFilter();
}

Response filters have a single method processResponse which is passed a (Response)[response] object and can either return a new response or modify the one passed.

The most common use of filters are for Layouts

Pre Response Filters

A filter can specialise as a 'pre-response' filter. This is one which operates on the empty response object before URL handlers have had a chance to generate the primary response. This is a useful hook for handling global level features such as UI elements contained within layout classes.

To specify a filter as a pre-response filter, simply override the isResponseFilter() method and return true.

Most pre-response filters are designed to throw a ForceResponseException() to output a set response and stop URL Handlers from processing the request - is certain conditions are met.

Layouts

The normal pattern for generating HTML output is to let your response generating classes create the core HTML of the content area of a page and then surround the content with HTML provided by a layout class. The layout of a page frequently includes the header, footer and navigation elements of a page which are usually the same on every page.

Layout support is provided by the LayoutModule which supplies a filter to activate layout support.

Layout Classes

A layout class extends Layout and should at a minimum override the printLayout($content) method:

class MyLayout extends Layout
{
    protected function printLayout($content)
    {
        ?>
        <html>
            <title>My site</title>
        </html>
        <body>
        <?php
        print $content;
        ?>
        </body>
        </html>
        <?php
    }
}

The argument $content contains the HTML for the centre portion of the page and the class simply needs to print this string in context of the surrounding HTML.

The default layout

Every application can have one default layout. This matches the requirements of nearly all web applications where nearly all pages share a common layout while perhaps only the login page differs radically enough to warrant a separate layout.

To register the default layout you simply pass it as an argument when constructing the LayoutModule class during module registration. For example in your layout module:

class MyModule extends Module
{
    public function getModules()
    {
        return [
            new LayoutModule(MyLayout::class)
        ];
    }
}

Changing Layouts

To switch from using the default layout to a specific layout for an individual page you can call the static function LayoutModule::setLayoutClassName() at any point during response generation - as long as it is before the layout itself is used to filter the response. You cannot therefore decide to change the layout from within the default layout itself.

Alternatively instead of passing the class name of the default layout to the constructor of the LayoutModule you can pass a callback function instead. This function will be called for every request and should return the name of the class name to use. It can access the request object or any other contextual information to make this choice:

class MyModule extends Module
{
    public function getModules()
    {
        return [
            new LayoutModule(function(){
                $request = Application::current()->request();

                if (stripos("/login", $request->uri) === 0){
                    return LoginLayout::class;
                }

                return MyLayout::class;
            })
        ];
    }
}

Extending Layouts

As layouts are normal PHP classes they are easily extended from boiler plates. It's a good pattern to start building a library of boiler plates for different types of applications to make layout creation easier.

When adopting this strategy your base class should implement printLayout and call a range of other protected functions to make it easier to extend without having to cut and paste large blocks of HTML:

class MyBaseLayout extends Layout
{
    protected function getTitle()
    {
    }

    protected function printNavigation()
    {
    }

    protected function printLayout($content)
    {
        ?>
        <html>
            <title><?=getTitle();?></title>
        </html>
        <body>
            <div class="nav">
            <?php
            $this->printNavigation();
            ?>
            </div>
            <div class="main">
            <?php
            print $content;
            ?>
            </div>
        </body>
        </html>
        <?php
    }
}

Resource Manager and injection of other head and body items

Many modules of Rhubarb use the ResourceLoader to load javascript and css files. In addition many individual features of websites will require injecting HTML into the head or body tags of the output. To accommodate both of these features you should make the calls below:

class MyLayout extends Layout
{
    protected function printLayout($content)
    {
        ?>
        <html>
            <title>My site</title>
            <?= ResourceLoader::getResourceInjectionHtml(); ?>
            <?= LayoutModule::getHeadItemsAsHtml(); ?>
        </html>
        <body>
            <?= LayoutModule::getBodyItemsAsHtml(); ?>
            <?php
            print $content;
            ?>
        </body>
        </html>
        <?php
    }
}

With these in place, to inject your own HTML during response generation simply call the static methods LayoutModule::addHeadItem() and LayoutModule::addBodyItem() with the HTML you need injected.

Disabling layouts

If during content generation you decide you need to turn layout filtering off, simply call the static method LayoutModule::disableLayout(). LayoutModule::enableLayout() will reverse that decision.