Register additional Laravel Components in Jigsaw
Tags: jigsaw, php, laravel
Published: 2021-04-17 07:51:35

---
title: Register additional Laravel Components in Jigsaw
published: true
date: 2021-04-16 20:00:00 UTC
tags: jigsaw, php, laravel
canonical_url: https://stefanzweifel.dev/posts/2021/04/16/register-additional-laravel-components-in-jigsaw
---

If you ask the wider web developer community,[Jigsaw](https://jigsaw.tighten.co/) is probably not well known to be a static site generator, but rather known as a toy. But Laravel developers have surely heard about it in the last few months (I hope!).

I love Jigsaw! And so does the rest of my team. We've used Jigsaw in 40+ projects so far and are very pleased with it.

My most recent project relied heavily on [remote collections](https://jigsaw.tighten.co/docs/collections-remote-collections/) and custom [helper methods](https://jigsaw.tighten.co/docs/helper-methods/). Many of those helper methods made API requests to third party services.  
To keep the local builds fast and snappy I've integrated Laravel's Cache component into Jigsaw to cache the API responses for a couple of minutes.

To make all of this work, I first had to figure out how to register something complex – like the cache component from Laravel – in Jigsaw's IoC Container.

That's what we cover first: How to work with the Container in Jigsaw. Then we will add the `illumiante/cache` package to a Jigsaw project and connect everything together.

Last, I will show you an example how you can use the Jigsaw Container to access your sites configuration in plain old PHP Classes.

## The Jigsaw Container[#](#the-jigsaw-container "Permalink")

I took quite a while for me to understand that Jigsaw already comes with an [IoC Container](https://laravel.com/docs/master/container).

In essence, you can call `\Illuminate\Container\Container\Container::getInstance()` anywhere in your code to get access to the Jigsaw container.

```
/** @var Container $cache */
$container = \Illuminate\Container\Container\Container::getInstance();
```

Then, when you have the container, you can call `$container->make(ContractInterface::class)` to get a `ContractInterface` instance resolved from the container. (In a Laravel app, you would use `app()`, `resolve()` or Constructor Injection for this.)

It's that simple! I use Laravel's Container all the time in other projects and I know how it works, but it never clicked for me that I could use it in Jigsaw sites too. ¯\_(ツ)_/¯

Well, now that we have learned that we have a full blown IoC container at our fingertips, let's use it to register the cache component in our project.

## Add Caching Component[#](#add-caching-component "Permalink")

First we need to include the `illuminate/cache` package in our Jigsaw project. Note that at the time of writing this article, Jigsaw doesn't support Laravel v8 components yet. That's why we stick to `v7`.

Run the following command in your favourite terminal to add the package.

```
composer require "illuminate/cache:^7"
```

Next we need to register our installed packages in Jigsaw. For that we open `bootstrap.php` and add the following `beforeBuild`-hook.

```
use Illuminate\Cache\CacheManager;
use Illuminate\Filesystem\Filesystem;

/**
 * Register Cache Component in Jigsaw
 */
$events->beforeBuild(function (Jigsaw $jigsaw) {
    $container = $jigsaw->app;

    $container['config'] = $container['config']->merge(collect([
        'cache.default' => 'file',
        'cache.stores.file' => [
            'driver' => 'file',
            'path' => __DIR__. '/storage/cache',
        ]
    ]));

    $container['files'] = new Filesystem;

    $container->singleton(CacheManager::class, function () use ($container) {
        return new CacheManager($container);
    });
});
```

The code above does the same as what Laravel does in it's Service Providers:

- We take the `$container` from `$jigsaw->app` (We will use `Container::getInstance()` later)
- Add caching related values to the `$config`
- Register a new `Filesystem`
- Create a new singleton instance of the `CacheManager` and provide it with our container / configuration.

(Hat tip to the [mattstauffer/Torch](https://github.com/mattstauffer/Torch/blob/master/components/cache/index.php) project for this example.)

Now, whenever we resolve an instance of `CacheManager` and store values by using `$cache->remember()` or `$cache->put()`, the values are cached in the `./storage/cache`-folder.

Note that the folder has to exist to make this work. Best create the folder now by running the following command.

```
mkdir -p ./storage/cache
touch ./storage/cache/.gitkeep
```

Commit the changes we've just made. (Including the `./storage/cache/.gitkeep` file.) Next time the repo is cloned, everything will work as expected.

But to prevent commiting your cache, update `.gitignore` with the following line.

```
./storage/cache
```

## Use the Cache Manager[#](#use-the-cache-manager "Permalink")

As mentioned in the beginning, my project uses a lot of API heavy helper methods. So let me show you how to use the `CacheManager` in a helper method. (The concept can be applied to every other PHP class or function though.)

Let's say we have a `fetchWeather()`-helper method which makes an API request to get the current weather data of a location. The method returns a JSON string.

```
// config.php
'fetchWeather' => function ($page) {
    return file_get_contents('http://wttr.in/Zurich?format=j1');
},
```

This is fine if you rebuild the site every other hour and only use the `fetchWeather()` helper in a single page. But it significantly slows down your build process if you use `fetchWeather()` on every single site of your project or when you use a file-watcher to rebuild your site constantly.

Adding caching is straightforward. Here is the updated `featchWeather()` method.

```
// config.php

use Carbon\Carbon;
use Illuminate\Cache\CacheManager;

'fetchWeather' => function ($page) {
    /** @var CacheManager $cache */
    $cache = Container::getInstance()->make(CacheManager::class);

    $cacheValidFor = Carbon::now()->addMinutes(15);

    return $cache->remember('wttr-data', $cacheValidFor, function () {
        return file_get_contents('http://wttr.in/Zurich?format=j1');
    });
},
```

A quick run down what we have done here:

1. We resolve an instance of the `CacheManager` by using `Container::getInstance()`.
2. We define for how long the response should be cached. (15 minutes)
3. We wrap the API call in [`$cache->remember()`](https://laravel.com/docs/master/cache#retrieve-store).

The API request is now only made on the first build, and then cached for all subsequent builds for the next 15 minutes.

If this is too strict for you, you could update the method to look for the `production` config value. You can access your project configuration like so:

```
Container::getInstance()->config['production'];
```

## Access Jigsaw Configuration in plain PHP Classes[#](#access-jigsaw-configuration-in-plain-php-classes "Permalink")

A couple of months (years?) ago, [Ryan Chandler](https://twitter.com/ryangjchandler) showed me, that he replaced the default Markdown parser of Jigsaw with his own version. (He now uses a full blown Laravel app for his website). This allowed him to register and use [CommonMark extensions](https://commonmark.thephpleague.com/1.5/extensions/overview/).

I was intrigued! First this introduced me to the world of Markdown Extensions. And second, I've learned about [Block Rendering](https://commonmark.thephpleague.com/1.5/customization/environment/#addblockrenderer).

For years I've used [highlight.php](https://github.com/scrivo/highlight.php) to generate the syntax highlighting for code samples during build time. This allowed me to ditch JavaScript completely for this website.

My implementation was … special: I've used a couple of Regex expressions to search the already rendered HTML pages for code snippets and then used `str_replace` to patch in the syntax highlighting. 🥴

But now that I know I can simply _swap_ Jigsaw's Markdown parser with my own version, I could finally clean up that code and use something more stable like [spatie/commonmark-highlighter](https://github.com/spatie/commonmark-highlighter).

But I digress. What I want to show you, is how you can access your Jigsaw page configuration in a PHP class; like my custom Markdown parser.

Let's imagine that my custom parser is registered in Jigsaw's `bootstrap.php` file like this:

```
$container->bind(MarkdownParser::class, CustomParser::class);
```

And now let's say, that I only want to highlight code snippets in production. (Code highlighting slows the build down by a few seconds). In my `CustomParser` class I can achieve this by adding this code:

```
// ...

$environment = Environment::createCommonMarkEnvironment();

if (Container::getInstance()->config['production']) {
    $environment->addBlockRenderer(FencedCode::class, new FencedCodeRenderer(['html', 'php', 'js']));
    $environment->addBlockRenderer(IndentedCode::class, new IndentedCodeRenderer(['html', 'php', 'js']));
}
// ...
```

I resolve the `Container`, check the configuration value for `production` and only add my BlockRenderers if the current build is in production.

You can find the whole `CustomParser` class [here](https://gist.github.com/stefanzweifel/b16ae32e5829ae9605d2e94c62e04de1).

## Going further[#](#going-further "Permalink")

I hope this excourse gave you some ideas on how to add more functionality to your Jigsaw projects. Be it by adding Illuminate Components or maybe some Markdown extensions.

If you want to add more Laravel Illuminate components to a Jigsaw project and need examples, I can highly recommend [Matt Stauffer's](https://twitter.com/stauffermatt) [Torch](https://github.com/mattstauffer/Torch) project. ([Here](https://github.com/mattstauffer/Torch/blob/master/components/translation/index.php) is an example on how to add the translation component).