Lazily loading template data in PHP with Twig and Shoot
Tags: webdev, tutorial, php, opensource
Published: 2019-03-05 20:06:12

---
title: Lazily loading template data in PHP with Twig and Shoot
published: true
description: If you've ever found yourself frustrated from passing template data down several levels of your template tree, polluting the global namespace or finding other creative solutions, lazily loading template data with Twig and Shoot might be the solution for you.
tags: webdev,tutorial,php,opensource
cover_image: https://thepracticaldev.s3.amazonaws.com/i/m5rzl0efssq0mu0rb5na.jpg
---

*Disclaimer: this is a rewrite (new and improved) of a post I've originally shared on my personal blog*

If you've ever worked with a relatively large codebase with many nested templates, you have probably found yourself in a situation where you needed to either pass variables way down the template tree, pollute the global (template) scope with many variables only relevant to deeply nested templates, or find other creative solutions for injecting data into these sub templates (with Twig extensions for example).

At [Coolblue](https://www.coolblue.nl) we are using Twig for template rendering and we have definitely found ourselves in that position. Over the years we have tried numerous approaches for working around that problem, including but not limited to injecting fully prerendered views/templates into other templates. Nothing ever felt really clean, maintainable or futureproof, but fortunately there is now a solution that basically solves all of our problems in that area.

[Shoot](https://github.com/shootphp/shoot) is a Twig extension built by [Victor Welling](https://github.com/victorwelling), also a developer at Coolblue. What Shoot allows you to do is tie a `presentation model` to a template. The model defines and limits the variables that are in scope when rendering the template. These presentation models are pretty much plain old PHP objects extending Shoots `PresentationModel`, containing a protected field for every variable. Where the magic comes in though, is that these models can declare themselves as "having a presenter" by implementing the `HasPresenterInterface`. A presenter populates the fields in a model at runtime, by accessing its (optional) dependencies and a [PSR-7 ServerRequest](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface). Basically this means a template now fetches its own data.

#Benefits
The benefits we're getting out of using Shoot over conventional handling of templates and template data:
* Performance (data is only retrieved if the template is *actually* being rendered)
* Way better maintainability
* Improved testability
* More isolation (no more leakage of template scope)
* Increased template reuse

In the Shoot README we can find a clear overview of how the paradigm shifts from preloading all data to "lazily loading" the data, based on rendered templates:

```
+---------------+          +---------------+
|    Request    |          |    Request    |
+-------+-------+          +-------+-------+
        |                          |     +---------+
        |                          |     |         |
+-------v-------+          +-------v-----v-+     +-+-------------+
|   Load data   |          |  Render view  +----->   Load data   |
+-------+-------+          +-------+-------+     +---------------+
        |                          |
        |                          |
+-------v-------+          +-------v-------+
|  Render view  |          |   Response    |
+-------+-------+          +---------------+
        |
        |
+-------v-------+
|   Response    |
+---------------+
```

# How to use Shoot
In order to actually connect your template and a presentation model, you simply add the `{% model %}` tag to the template pointing the fully qualified class name of your model:

```twig
{% model 'ShootDemo\\Presentation\\BlogPostModel' %}

<html lang="en">
<head>
    <title>{% if post_exists %}{{ post_title }}{% else %}404 - Not Found{% endif %}</title>
    <meta charset="UTF-8">
</head>
<body>
{% if post_exists %}
    <h1>{{ post_title }}</h1>
    {% for paragraph in post_content %}
        <p>{{ paragraph }}</p>
    {% endfor %}
{% else %}
    <h1>Post not found</h1>
    <p>
        This post could not be retrieved. Check out the other posts:
    </p>
{% endif %}

<a href="/?postId=1">Post 1</a> &nbsp; <a href="/?postId=2">Post 2</a>
</body>
</html>
```

As you can see we're pointing to the following model: `ShootDemo\Presentation\BlogPostModel`. There are three variables used in this template and these all need to be defined in the model.

```php
class BlogPostModel extends PresentationModel
{
    /** @var string[] */
    protected $post_content = '';

    /** @var bool */
    protected $post_exists = false;

    /** @var string */
    protected $post_title = '';
}
```

This model now provides the variables with these values to the template, dynamically, without you needing to pass them in from the parent. This is a static model though, the post would never exist. We could step it up a notch and make it dynamic, based on the current request, by making the model implement the `HasPresenterInterface`:

```php
class BlogPostModel extends PresentationModel implements HasPresenterInterface
{
    /** @var string[] */
    protected $post_content = '';

    /** @var bool */
    protected $post_exists = false;

    /** @var string */
    protected $post_title = '';

    /**
     * @return string
     */
    public function getPresenterName(): string
    {
        return BlogPostPresenter::class;
    }
}
```

The interface defines a single method, `getPresenterName(): string`. That method should return the alias by which the actual presenter can be retrieved from a [PSR-11](https://www.php-fig.org/psr/psr-11/) compliant container. Since we register our presenters by their fully qualified class name, that's also what we return here. It gives the added benefit of easy traversal in your IDE. The presenter might look something like this:

```php
class BlogPostPresenter implements PresenterInterface
{
    /** @var BlogPostRepositoryInterface */
    private $blogPostRepository;

    /**
     * @param BlogPostRepositoryInterface $blogPostRepository
     */
    public function __construct(BlogPostRepositoryInterface $blogPostRepository)
    {
        $this->blogPostRepository = $blogPostRepository;
    }

    /**
     * @param ServerRequestInterface $request
     * @param PresentationModel      $presentationModel
     *
     * @return PresentationModel
     */
    public function present(ServerRequestInterface $request, PresentationModel $presentationModel): PresentationModel
    {
        $postId = (int)($request->getQueryParams()['postId'] ?? -1);

        try {
            $blogPost = $this->blogPostRepository->fetchBlogPost($postId);
        } catch (UnableToFetchBlogPostException $ex) {
            // Unable to load blog post return presentation model, explicitly setting post_exists to false
            return $presentationModel->withVariables([
                'post_exists' => false,
            ]);
        }

        $variables = [
            'post_content' => $blogPost->content(),
            'post_exists'  => true,
            'post_title'   => $blogPost->title(),
        ];

        return $presentationModel->withVariables($variables);
    }
}
```

This is it, your template now automatically gets populated with the relevant values for that request. Whenever the template is rendered, it will load its model, thereby automatically calling the presenter to update the properties of the model. No more trying to determine what your template might need in your controller/request handler.

As you can see the presenter can have its own dependencies like the `$blogPostRepository`, by configuring it in your DI-container. It also gets access to the request in the `->present()` method. The second argument it receives is the model we've just defined. You're now free to write any logic you desire in this method, as long as you return an instance of `PresentationModel`. A common pattern here is to use the `->withVariables()` method on the model that's passed to the presenter, by passing it an associative array of values.

Keep in mind that this example is still fairly simple and passing the values down from the controller might not be hard here, but it doesn't matter where/how deep your template is located. This would work exactly the same for a template that's nested 20 layers deep or reused across different pages.

# How to install Shoot
Maybe the best thing about starting to use Shoot, is that it requires very minimal changes to your setup, because you can simply continue rendering templates as you're currently doing. If you don't define a model on your template, nothing changes, it's therefore very much opt-in on a per template basis.

Installation can be done through Composer:

```
$ composer require shoot/shoot
```

After that it's a matter of instantiating the Shoot `Pipeline` and attaching it to Twig:

```php
$presenterMiddleware = new PresenterMiddleware($container);
$pipeline = new Pipeline([$presenterMiddleware]);
$installer = new Installer($pipeline);

// Add Shoot to Twig
$twig = $installer->install($twig);
```

As you can see Shoot has its own middleware, the most important one being the `PresenterMiddleware`, this is the one that enables the behavior I described above. There are other Shoot middlewares that ship with Shoot like a `SuppressionMiddleware` which suppresses `RuntimeException`s thrown from within an `{% optional %}` tag in your templates, a `LoggingMiddleware` that logs which templates are rendering including how long that takes and an `InspectorMiddleware` that logs information about template rendering to your browser console. All these are optional, although you'll want to add the `PresenterMiddleware` to enable the lazy data loading we're talking about.

Now you'll want to add Shoots [PSR-15 middleware](https://www.php-fig.org/psr/psr-15/) to your applications middleware stack. This grants Shoot access to your PSR-7 request that it can use in presenters to dynamically populate presentation models. In the example below I'm using the [Idealo Middleware Stack](https://github.com/idealo/php-middleware-stack):

```php
$middlewareStack = new Stack(
    new EmptyResponse(),
    new ShootMiddleware($pipeline),
    $requestHandler
);
```

Now Shoot is completely set up and it's only a matter of actually executing your middleware stack and emitting the response.

# Getting started
I've made a demo project available, which you can fork/clone/download.

{% github erikbooij/shootdemo %}

When you've got your local copy, it's a matter of running two commands (the second one needs elevated privileges because it binds to port 80 by default):

```bash
$ composer install
$ sudo composer run
```

Now you can go to http://localhost/?postId=1 to see Shoot in action.

# Follow up
There is more you can do with Shoot, like passing down prepopulated models, or feeding data from your (parent) template back to a presenter. I'll cover this in a next write-up if there's any interest for it (feel free to express your interest by liking this post or following me).

If you have comments, questions or a request for some support, I hope I'll see you in the comments. Cheers!