Skip to main content
Sculpting Clean Data for Your Sage Templates with View Composers

Sculpting Clean Data for Your Sage Templates with View Composers

Building modern WordPress themes with a focus on maintainability and a clean codebase often feels like an uphill battle, especially if you're coming from the…

Building modern WordPress themes with a focus on maintainability and a clean codebase often feels like an uphill battle, especially if you’re coming from the world of drag-and-drop page builders. While tools like Divi or Elementor promise quick results, they inevitably lead to a bloated database, vendor lock-in, and an unmanageable codebase that no developer wants to inherit. This is precisely why Sage, powered by Acorn and Blade, has become my go-to choice. It offers an MVC-like architecture for WordPress development, ensuring your logic, data, and presentation layers are beautifully separated. Today, I want to dive into one of its most powerful features for achieving this separation: View Composers.

Too often in WordPress theme development, I see developers fetching data directly within their template files. Maybe it’s a WP_Query loop, or a call to get_post_meta() scattered throughout single.php or archive.php. While it “works,” it’s a direct violation of the principle of separation of concerns. Your templates should be purely concerned with presentation – displaying data, not fetching or processing it. This bad habit quickly makes templates hard to read, difficult to debug, and impossible to reuse without duplicating logic.

This is where Sage’s View Composers come to the rescue, borrowing a concept familiar to Laravel developers. A View Composer is a class that executes when a specific view (or set of views) is rendered, allowing you to bind data to that view before it’s ever displayed. It’s the perfect place to gather your custom post types, options, or any other data your template needs, keeping your Blade files pristine and purely focused on markup. Think of it as your backstage manager, preparing all the props and actors before the main show begins.

Let’s walk through a concrete example. Imagine we have a custom post type called “Service” and we want to display a list of all active services in a sidebar partial, resources/views/partials/services-sidebar.blade.php. Instead of dumping a WP_Query directly into that partial, let’s use a View Composer.

First, we create a new composer file, say app/View/Composers/ServicesComposer.php:

<?php

namespace App\View\Composers;

use Roots\Acorn\View\Composer;
use WP_Query;

class ServicesComposer extends Composer
{
    /**
     * List of views served by this composer.
     *
     * @var array
     */
    protected static $views = [
        'partials.services-sidebar',
    ];

/**
     * Data to be passed to view before rendering.
     *
     * @param  \Illuminate\View\View  $view
     * @return array
     */
    public function with(\Illuminate\View\View $view)
    {
        return [
            'services' => $this->getServices(),
        ];
    }

/**
     * Get an array of active services.
     *
     * @return array
     */
    protected function getServices()
    {
        $query = new WP_Query([
            'post_type' => 'service',
            'post_status' => 'publish',
            'posts_per_page' => -1,
            'orderby' => 'menu_order',
            'order' => 'ASC',
        ]);

return array_map(function ($post) {
            return (object) [
                'title' => get_the_title($post),
                'permalink' => get_permalink($post),
                // Add any other data you need for the display
            ];
        }, $query->posts);
    }
}

In this ServicesComposer, we define $views to specify which Blade templates this composer should apply to. The with() method returns an associative array, where the keys become available as variables in our targeted view. Here, we’re calling a protected getServices() method to encapsulate our WP_Query logic, returning a cleaned-up array of service objects.

Now, our resources/views/partials/services-sidebar.blade.php template becomes incredibly simple and readable:

<aside class="sidebar-services">
    <h2>Our Services</h2>
    @if (!empty($services))
        <ul>
            @foreach ($services as $service)
                <li><a href="{{ $service->permalink }}">{{ $service->title }}</a></li>
            @endforeach
        </ul>
    @else
        <p>No services currently available.</p>
    @endif
</aside>

Notice how clean that template is! No WP_Query, no have_posts() or the_post(), just plain logic iterating over a $services variable that we know will be there. The template only worries about how to display the data, not where it comes from.

To make sure our composer is picked up by Sage, we register it. The simplest way for a single theme is in app/setup.php, within the after_setup_theme action, by adding it to the Sage::loadViewComposers array:

// app/setup.php
add_action('after_setup_theme', function () {
    // ... other setup code ...

/**
     * Load theme composers.
     *
     * @see https://roots.io/docs/sage/10.x/view-composers/
     */
    Sage::loadViewComposers(); // Make sure this is called

// Add your composer here:
    app('view')->composer('partials.services-sidebar', App\View\Composers\ServicesComposer::class);
}, 20); // Make sure this runs after Sage has loaded its own composers

With this pattern, your data fetching logic is centralized, reusable, and easily testable. Your templates remain purely presentational, leading to a much more organized, programmatic codebase that developers will genuinely enjoy working with. Sage, with its reliance on modern PHP practices and tools like Blade and Acorn, truly transforms WordPress development from a hacky mess into an elegant, maintainable art form. Embrace View Composers, and you’ll immediately feel the difference in clarity and control over your WordPress projects.