Building a theme system in Laravel
The key to building a theme system in Laravel is making your active theme the first location that Laravel should look in order to load views. All of the rest of the functionality around a theme engine is opinionated, implementation detail.
In order to add a location to the view finder, we need to get an instance of Illuminate\View\Factory
. In Laravel, there are a couple of ways to get access to a dependency. My favorite way is to auto-resolve via the constructor
by type-hinting the class you need, for example:
<?php
namespace App;
use Illuminate\View\Factory;
class ThemeManager
{
protected $view;
public function __construct(Factory $view)
{
$this->view = $view;
}
}
Once you have an instance of Illuminate\View\Factory
, you have two options to add a view location. The first is to use addLocation()
and while it will work, it appends the location rather than prepends.
public function setupTheme($themePath)
{
$this->view->addLocation($themePath);
}
We want to prepend. Using the Illuminate\View\Factory
instance we already have, we can get an instance of the Illuminate\View\ViewFinderInterface
by calling getFinder()
. By default, Laravel is configured to use a local file finder, specifically Illuminate\View\FileViewFinder
, which implements a method prependLocation($location)
.
Ideally, the Illuminate\View\ViewFinderInterface
would enforce the prependLocation()
method we rely upon but it doesn't so we're going to have to insist that our theme engine will only support Illuminate\View\FileViewFinder
instances or a subclass.
public function setupTheme($themePath)
{
$this->view->getFinder()->prependLocation($themePath);
}
That's all there is, that's the secret, the rest is packaging up the service provider, some configuration and modeling the theme. The Stylist package is a good example of using this technique, if you don't want to roll your own. As always, you can contact me on Twitter @eoghanobrien with any questions.