When I first started diving deep into WordPress theme development, I was instantly frustrated by the common practice of cramming all sorts of data fetching logic directly into template files. You’d open up archive.php or single.php and be greeted by WP_Query instances, conditional checks, and database calls all mixed in with the HTML markup. It was a chaotic mess, a recipe for unmaintainable code, and miles away from any clean architecture principles I’d learned in other development contexts. Then I found Sage, and specifically, its approach to data handling with View Composers, and it completely changed my perspective on WordPress.
Sage, built on top of Laravel’s components via Acorn, offers an MVC-like structure that makes WordPress development feel truly modern and enjoyable. It empowers us, as developers, to write organized, programmatic code, which is a stark contrast to the database bloat and UI-driven complexity of tools like Divi or Elementor. The beauty of Sage lies in its commitment to separating concerns: your Blade templates are purely for presentation, and your data logic lives where it belongs.
Today, I want to walk you through a specific, concrete technique that embodies this philosophy: using a View Composer to fetch custom data and pass it cleanly to your Blade templates. This keeps your templates lean and focused solely on displaying information, while all the heavy lifting of data retrieval happens behind the scenes.
Let’s say you have a custom post type called “Projects” and you want to display the three most recent projects in a sidebar partial (resources/views/partials/recent-projects.blade.php). Traditionally, you might throw a new WP_Query directly into that partial. With Sage, we leverage a View Composer.
First, we create our View Composer. I usually place these in app/View/Composers. So, let’s create app/View/Composers/RecentProjects.php:
namespace App\View\Composers;
use Roots\Acorn\View\Composer;
class RecentProjects extends Composer
{
/**
* List of views served by this composer.
*
* @var array
*/
protected static $views = [
'partials.recent-projects',
];
/**
* Data to be passed to view before rendering.
*
* @return array
*/
public function with()
{
return [
'projects' => $this->getRecentProjects(),
];
}
/**
* Get the three most recent projects.
*
* @return \WP_Post[]
*/
protected function getRecentProjects()
{
$query = new \WP_Query([
'post_type' => 'project',
'posts_per_page' => 3,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'no_found_rows' => true, // Optimize for performance if pagination isn't needed
]);
return $query->posts;
}
}
In this composer, static $views tells Sage which Blade templates this composer should apply to. The with() method returns an associative array, where the keys become variables available in your Blade template. Here, we’re calling a protected helper method getRecentProjects() to encapsulate the WP_Query logic.
Next, we need to register this composer so Sage knows about it. The easiest place for theme-specific composers is usually app/setup.php. Just add it to the compose array:
add_filter('sage.view.composers', function ($composers) {
return array_merge($composers, [
\App\View\Composers\RecentProjects::class,
]);
});
Now, within your resources/views/partials/recent-projects.blade.php file, you can access the projects variable directly, without any WP_Query calls cluttering your markup:
<section class="recent-projects">
<h2>Our Latest Projects</h2>
@if (!empty($projects))
<ul>
@foreach ($projects as $project)
<li>
<a href="{{ get_permalink($project->ID) }}">
<h3>{{ get_the_title($project->ID) }}</h3>
</a>
</li>
@endforeach
</ul>
@else
<p>No recent projects to display.</p>
@endif
</section>
See how clean that template is? It’s purely presentation. All the logic for what projects to fetch, how many, and how to query the database is handled entirely within the RecentProjects View Composer. This pattern provides immense benefits: your templates are easier to read and maintain, your data fetching logic is reusable and testable, and you maintain a clear separation of concerns. This is why Sage stands out as the developer’s choice for building robust, clean, and maintainable WordPress themes. It encourages a structured, programmatic approach that modern web development demands, leaving the old, spaghetti-code ways behind.