Introducing Laravel Blade with WordPress - Ultimate Guide for Beginners

In this article, on the example using Laravel Blade and WordPress, I'll explore the reasons why the concept of templating engines in PHP is worth trying, and how it can improve the way you build applications.

tl;dr

In the dynamically changing world of web dev, the need for structured and efficient coding practices is a key to success in the long run. One powerful concept that addresses these demands in PHP projects is using templating engines to organize visual layers. So in this lesson I'll dive-in to this topic to help you answer the following questions:

  1. What is Laravel Blade and how can it be used in WordPress?
  2. How to integrate Blade with WordPress with a few simple steps (no plugins)?.
  3. Why it's worth to try to learn and use Blade in your workflow?.

If you build WordPress solutions like custom themes or plugins, or you search for ways to learn something new maybe even outside the WordPress world, I insist you check out this article and make your first steps in the Laravel world. Enjoy 👋


What is Laravel Blade and how does it work?

Blade is the simple, yet powerful templating engine included with Laravel. It is designed to simplify and enhance the way developers create views for web applications, making it easier to manage and display dynamic content in a structured and organized manner.

Blade templates are defined in the blade.php files and consist of typical HTML mark-up boosted up with placeholders used for displaying dynamic data, directives that can be used for extending layouts, and directives that are shortcuts for common PHP control structures. How it can be used for defining views in WordPress-based applications?

Most web apps utilize the same general layout across various pages and maintaining them would be hard if I had to repeat the entire HTML in every view I create. Of course, WordPress has functions that help avoid this, but, let's give it a try and check how it can be achieved in a more structured manner.

<!doctype html>
<html {!! language_attributes() !!}>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    {!! wp_head() !!}
  </head>

  <body {!! body_class() !!}>
    {!! do_action('wp_body_open') !!}
    {!! do_action('get_header') !!}

    <div class="app">
      @section('content')
        <main class="app__content">
          {!! the_content() !!}
        </main>
      @show

      @yield('sidebar')
    </div>

    {!! do_action('get_footer') !!}
    {!! do_action('wp_footer') !!}
  </body>
</html>

General layout of my application in Blade consists of typical HTML markup with more clear syntax for displaying the data {{ }}. However, take note of the @section and @yield directives which can be used for defining slots to fill by child templates.

  1. @section Defines slot that can be overwritten and displays it using @show.
  2. @yield Defines slot that can be filled with content and displayed immediately.

I mostly use only @yield directives, because I don't need the default structure to be set. I just want to display slots content, but there is full freedom when it comes to using it.

It's time to create the first index.blade.php template that will be equivalent to the default index.php file used in WordPress themes. The structure there will be simple - for all the default templates in my theme I just need to show the_content results in the main content and a simple widget with external links in the sidebar.

@extends('base')

@section('sidebar')
  <sidebar class="app__sidebar">
    {!! do_action('get_sidebar') !!}
  </sidebar>
@endsection

So I just need to extend the base template using @extend('base') directive at the beginning and fill the sidebar slot defined by @yield('sidebar') with content using @section('content') and @endsection. The engine will render template using general layout and custom structure set in slot. It already looks great to me 🤩

While working with WordPress I mostly build more than one view so let's add single.blade.php which will be equivalent to single.php in the default template hierarchy. The layout should include the post title and content without a sidebar.

@extends('base')

@section('content')
  <main class="app__content">
    <h1>
      {!! the_title() !!}
    </h1>

    {!! the_content() !!}
  </main>
@endsection

To achieve the required results I just need to overwrite the default structure defined with @section('content') directive and add the title there. When it comes to the sidebar, I don't need to do anything because the sidebar slot is empty by default.

Now, it would be great to have some header across the whole application. The best way to do this is to modify the base.blade.php layout that is already inherited by other templates and inject the header using the @include directive. I don't need to specify the full path to the partial that needs to be included. I rather pass a template name which is something like a path chain. Template located in the resources/views/partials/header.blade.php will have partials.header key.

(...)

<div class="app">
  @section('header')
    @include('partials.header')
  @show

  @section('content')
    @include('partials.content')
  @show

  @yield('sidebar')
</div>

(...)

As the last step, during the render process, the engine compiles Blade templates to plain PHP code, caches them and serves them when requested. The cached results used by the theme look as follows:

<?php $__env->startSection('content'); ?>
  <main class="app__content">
    <h1>
      <?php echo the_title(); ?>

    </h1>

    <?php echo the_content(); ?>

  </main>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('base', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?

Why to use Laravel Blade in WordPress?

Blade does not restrict from using plain PHP in the templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified meaning Blade adds essentially zero overhead to the application which combined with new learning opportunities (Laravel) is enough argument for giving it a try, at least for me 👌

Let's check what problems it might solve in the WordPress application.

#1 Separation of Concerns: Tackling Spaghetti Code

Blade forces to think more about the Separation of Concerns concept, which in terms of templating emphasizes a clear division between the business logic and the presentation layer. Developers are encouraged to structure their code in a more organized manner.

Speaking in simple words, it just helps avoid spaghetti code, which by the way is one of the biggest problems in the whole WordPress ecosystem for me. It's not hard to find code with mixed logic and presentation, with hard-to-read and not clear structure even in the WordPress core, making maintenance and collaboration a nightmare in some cases.

Such separation streamlines development and allows different teams to work on various aspects of the project without interfering with each other. Front-end developers can focus on designing and perfecting the user interface without worrying about the underlying PHP code, while back-end developers can implement the logic without being entangled in the presentation layer intricacies.

#2 Templating: Enhancing Code Reusability

Blade takes a different approach to template inheritance. Developers can easily define parent layouts that establish the overall structure of a page using @yield and @section directives which indicate where the child views will inject their content.

This powerful mechanism allows developers to provide specific content for each page without altering the parent layout's core structure. It reduces potential duplication, improves readability and makes maintenance over time easier. It's like applying some SOLID rules to templating.

#3 Readability: Clean and Intuitive Syntax

Blade stands out for its clean and easy-to-read syntax. Building on familiar constructs like curly braces and directive tags, Blade templates remain intuitive for both PHP developers and front-end developers. This clarity leads to better code comprehension and reduces the learning curve for new team members.

For PHP developers accustomed to writing inline PHP within HTML, Blade provides a refreshing change. Instead of embedding PHP code within HTML tags, Blade lets developers use double curly braces to echo variables or employ directives like @if, @foreach, and @include, making the code more legible and maintainable.


How to implement Blade in WordPress?

Some developers would probably say "Just install the plugin!", but If you follow me for a longer period, you know that I don’t use such an approach by default, especially when it comes to such simple things. Also, my mission is to inspire you to learn how exactly WordPress works, so I'll integrate Blade from scratch with a few simple steps, without plugins. Once done, can be used in all projects.

#1 Install Blade

Blade is a part of the Laravel framework, but fortunately, it can be used standalone, just as any other tool. So as a first step, I install illuminate/view component from Laravel.

composer install illuminate/view

#2 Initialize Engine

Now, I need to initialize the engine which will take care of compiling templates. In the Laravel application, it's available out of the box, but once I use Blade outside its context I need to do it manually using the view Factory, which as the name suggests, produces output for Blade template.

If you're interested in more detailed information about this object, how it is created, and what happens in init() function I insist you to check out the notes section.

namespace FM\Templates;

use Illuminate\Events\Dispatcher;
use Illuminate\View\Factory;
use Illuminate\View\FileViewFinder;
use Illuminate\View\Compilers\BladeCompiler;
use Illuminate\View\Engines\EngineResolver;
use Illuminate\View\Engines\CompilerEngine;

class Provider
{
    private ?Factory $factory = null;

    public function __construct()
    {
        add_action('after_setup_theme', fn() => $this->init());
    }

    public function render(string $template, array $data = []): void
    {
        echo $this->generate($template, $data);
    }

    public function generate(string $template, array $data = []): string
    {
        return fm()->filesystem()->exists($template)
            ? $this->factory->file($template, $data)->render()
            : $this->factory->make($template, $data)->render();
    }

    private function init(): void
    {
        $compiler = new BladeCompiler(fm()->filesystem(), fm()->config()->get('cache.path'));
        $resolver = new EngineResolver();
        $finder = new FileViewFinder(fm()->filesystem(), [fm()->config()->get('views.path')]);
        $dispatcher = new Dispatcher();

        $resolver->register('blade', fn() => new CompilerEngine($compiler));

        $this->factory = new Factory($resolver, $finder, $dispatcher);
    }
}

I do this in the after_setup_theme action and make sure that the object is stored as a private property to prevent usage outside the scope. The only way to use it is just by generate and render functions which can be used for rendering output based on the template path or name and the data that should be injected. Encapsulation rulez! 😈

The template provider is ready to use so now, I need to make it available in the application. I create a new Templating module, initialize Provider there, add a render shortcut to reduce chain length, and inject the module into the app facade.

namespace FM\Templates;

use FM\Templates\Provider;

class Templates
{
    private Provider $provider;

    public function __construct()
    {
        $this->provider = \FM\App::init(new Provider());
    }

    public function render(string $template, array $data): void
    {
        $this->provider->render($template, $data);
    }
}

If you don’t get what happens here, I insist you check out material I’ve created about structuring application logic using Singleton and facades 👋

Now I’m able to render Blade templates using render function by providing a template path or name and the data that should be injected in any place I want.

fm()->templates()->render('single');

fm()->templates()->render('resources/views/single.blade.php');

If you develop plugins, you can end up here, because you probably render views manually anyway, but If you develop themes, an additional step needs to be done for everything to make sense.

#3 Integrate with WordPress

The template hierarchy algorithm reduces the work needed for developing themes by resolving proper PHP files based on the current view. It basically checks what template is available in a specific hierarchy, and renders it when found. I don’t want to break this when using Blade so I need to integrate both with Resolver class.

namespace FM\Templates;

class Resolver
{
    /**
     * @filter 404_template_hierarchy
     * @filter archive_template_hierarchy
     * @filter attachment_template_hierarchy
     * @filter author_template_hierarchy
     * @filter category_template_hierarchy
     * @filter date_template_hierarchy
     * @filter embed_template_hierarchy
     * @filter frontpage_template_hierarchy
     * @filter home_template_hierarchy
     * @filter index_template_hierarchy
     * @filter page_template_hierarchy
     * @filter paged_template_hierarchy
     * @filter privacypolicy_template_hierarchy
     * @filter search_template_hierarchy
     * @filter single_template_hierarchy
     * @filter singular_template_hierarchy
     * @filter tag_template_hierarchy
     * @filter taxonomy_template_hierarchy
     *
     * @see https://github.com/WordPress/WordPress/blob/master/wp-includes/template.php#L30-L62
     */
    public function relocate(array $templates): array
    {
        $relpath = str_replace(fm()->config()->get('path') . '/', '', fm()->config()->get('views.path'));
        $templates = array_map(fn($template) => str_replace('.php', '.blade.php', "{$relpath}/{$template}"), $templates);

        return $templates;
    }

    /**
     * @filter template_include
     */
    public function render(string $template): string
    {
        fm()->templates()->render($template, []);

        return fm()->config()->get('path') . '/index.php';
    }
}

I need to inform WordPress that it should search for templates in another location which now is resources/views and with another extension which is blade.php. Those processes are done by hooking some filters to relocate function [5-33].

Then I need to force WordPress to use Blade templates instead of native one using previously created render function. I do this by hooking into template_include filter which provides a path to the template as a parameter. Thanks to the previous step it already includes blade.php file so I just need to use the render function and return the path to an empty index.php file to not change native flow [35-43].

/**
 * @filter template_include
 */
public function handle(string $template): string
{
    $this->render($template, []);

    return fm()->config()->get('path') . '/index.php';
}

And that's all! The native WordPress templating system currently supports writing templates in Blade. Once I create a new template based on the native hierarchy, it will be automatically loaded.


Summary

In conclusion, adopting Laravel Blade within WordPress offers many benefits. From improving code organization, to enhancing readability and maintainability with its clean syntax, and finally, promoting code reusability through template inheritance - Blade takes development to new level.

Whether you are a backend developer valuing the structured approach or a frontend developer appreciating the power of template inheritance, Laravel Blade brings something valuable to the table for everyone. Embracing this templating engine empowers WordPress developers to build sophisticated, efficient, and collaborative projects that will for sure have an impact for your success.


Community

What others think about this topic? Here, you'll find a chorus of voices, each adding their unique hue to the canvas of discussion. Check out them as well as and explanations in the following threads 🔥


Notes

Factory Description

  • $compiler Takes care of compiling Blade templates. It requires a Filesystem object used for various file-related operations and a path to the directory where the output files will be stored (for me it's wp-content/uploads/cache/fm/templates).
  • $resolver Allows to register and resolve multiple template engines with unique names. I can specify which CompilerEngine should be used for rendering a particular view. I register Blade, but there is no problem with doing it for Twig.
  • $finder Locates templates based on their keys. When I ask for partials.header it will resolve resources/views/partials/header.blade.php as path. Requires a Filesystem object and array that contains paths which will be searched for templates. I set it as resources/views, but you can pass as many paths as you need.
  • $dispatcher Required, but no specific events are dispatched or listeners attached.

Feedback

How satisfied you are after reading this article?