← All posts

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 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 <?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:

```blade <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:

```php // 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.