If you’ve spent any time developing WordPress themes the traditional way, you’re probably intimately familiar with “the loop.” It’s a powerful but often messy construct where database queries, conditional logic, and HTML markup are all woven together in a single file like archive.php or index.php. While it gets the job done, it quickly leads to themes that are hard to read, harder to maintain, and nearly impossible to test programmatically. This is precisely the kind of unmaintainable spaghetti code that page builders like Divi or Elementor hide behind a drag-and-drop interface, only to offload the complexity and bloat into your database.
But what if there was a cleaner way to build WordPress themes? One that embraced modern PHP development principles, separating concerns like an MVC framework, and keeping your presentation layer truly clean? That’s where Sage, powered by Acorn and Blade, truly shines. For me, Sage isn’t just a theme starter; it’s a philosophy – a commitment to an organized, programmatic codebase over database bloat and unmanageable files. Today, I want to walk you through one of my favorite Sage features for achieving this separation: View Composers.
View Composers allow you to prepare data for a Blade view before that view is rendered. Think of them as middleware for your view data. Instead of performing a WP_Query directly within your template, you offload that logic to a dedicated PHP class. Your Blade template then receives the data neatly packaged, with no idea (or concern) about where it came from. This keeps your Blade files purely presentation-focused, which is exactly how they should be.
Let’s consider a practical example. Imagine you have a custom post type called “Project” and you want to display a list of all projects on an archive page or a dedicated custom page template. First, our Blade template, say resources/views/partials/projects-list.blade.php, should only care about displaying the projects, not fetching them:
@if ($projects->isNotEmpty())
<ul class="projects-grid">
@foreach ($projects as $project)
<li>
<h3><a href="{{ $project->permalink }}">{{ $project->title }}</a></h3>
<p>{{ $project->excerpt }}</p>
</li>
@endforeach
</ul>
@else
<p>No projects found.</p>
@endif
Notice how clean and readable this is? There’s no WP_Query, no have_posts(), just a loop over a $projects collection. Now, let’s create the View Composer that will provide this $projects variable. We’ll place this in app/View/Composers/ProjectsComposer.php:
<?php
namespace App\View\Composers;
use Roots\Acorn\View\Composer;
use WP_Query;
class ProjectsComposer extends Composer
{
/**
* List of views served by this composer.
*
* @var array
*/
protected static $views = [
'partials.projects-list',
'template-custom-projects-page', // Example for a specific page template
];
/**
* Data to be passed to view before rendering.
*
* @param \Illuminate\View\View $view
* @return array
*/
public function with($view)
{
$args = [
'post_type' => 'project',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
];
$query = new WP_Query($args);
$projects = collect($query->posts)->map(function ($post) {
return [
'title' => get_the_title($post),
'permalink' => get_permalink($post),
'excerpt' => get_the_excerpt($post),
];
});
return [
'projects' => $projects,
];
}
}
In this ProjectsComposer, we define which views it applies to via the $views property. The with() method is where the magic happens: we perform our WP_Query, then transform the raw WP_Post objects into a clean, easy-to-use collection of associative arrays. This processed data is then returned and automatically made available to the specified Blade views under the key 'projects'.
Finally, we need to tell Sage about our new Composer and which views it should bind to. This is typically done in your config/view.php file, under the 'composers' key. Just add an entry like this:
config/view.php (snippet):
<?php
return [
// ... other configurations ...
'composers' => [
\App\View\Composers\ProjectsComposer::class => [
'partials.projects-list',
'template-custom-projects-page', // Our custom page template
'archive-project', // And the archive page for the 'project' post type
],
// ... other composers
],
];
With this setup, any time you render partials.projects-list, template-custom-projects-page, or the archive-project template, the $projects variable will automatically be available in your Blade view, populated with your project data. Your template stays presentation-focused, your logic is encapsulated and testable in the Composer, and your codebase becomes a joy to work with. This clear separation of concerns is a cornerstone of why I choose Sage for every WordPress project – it allows for robust, maintainable, and modern development, leaving the messy, unmaintainable database bloat to those still wrestling with traditional methods or bloated page builders. Give View Composers a try; you’ll wonder how you ever lived without them.