← All posts

Clean CPT Data Delivery: Using a Sage View Composer for Your Project Archive

Clean CPT Data Delivery: Using a Sage View Composer for Your Project Archive

I remember the early days of WordPress theme development, constantly battling global variables and embedding `WP_Query` calls directly into template files. It felt like wrestling a bear while trying to make things look pretty. Discovering Sage transformed my approach, showing me how to build custom, maintainable sites with an architecture akin to a modern MVC framework. It's a world away from the database bloat and convoluted HTML output of page builders like Divi or Elementor, which often lead to unmaintainable codebases. Sage provides a robust, programmatic foundation that prioritizes developer experience and clean separation of concerns.

One of Sage's most potent features for achieving this clean architecture is the View Composer. If you've ever struggled to pass specific data to a WordPress template without cluttering it with query logic, View Composers are the elegant solution. They allow you to prepare all necessary data *before* your Blade template even sees it, ensuring your presentation layer remains purely focused on rendering content.

Imagine you have a custom post type (CPT) for "Projects" and you need to display an archive of these projects on a dedicated page. The traditional WordPress way might involve a `new WP_Query()` directly within your `resources/views/page-projects.blade.php` file. The Sage way is far more refined: we'll create a View Composer specifically for that template, or a partial it includes.

First, let's create the View Composer class itself. I organize these in `app/View/Composers`. We'll create `app/View/Composers/ProjectArchiveComposer.php`:

```php // app/View/Composers/ProjectArchiveComposer.php namespace App\View\Composers;

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

class ProjectArchiveComposer extends Composer { /** * List of views served by this composer. * * @var array */ protected static $views = [ 'partials.content-page-projects', // Target a specific partial 'page-projects', // Or target the main page template ];

/** * Data to be passed to view before rendering. * * @param \Illuminate\View\View $view * @return array */ public function with(\Illuminate\View\View $view) { $projects_query = new WP_Query([ 'post_type' => 'project', 'posts_per_page' => -1, // Get all projects 'post_status' => 'publish', 'orderby' => 'menu_order', 'order' => 'ASC', ]);

$projectData = collect($projects_query->posts)->map(function ($post) { return (object) [ 'id' => $post->ID, 'title' => get_the_title($post), 'permalink' => get_permalink($post), 'thumbnail' => get_the_post_thumbnail_url($post, 'medium'), // Add any other specific data you need from the post object ]; })->all(); // Convert back to a plain array

return [ 'projects' => $projectData, ]; } } ```

In this composer, the `static $views` property automatically registers our `ProjectArchiveComposer` to apply to the specified Blade templates. The `with` method is where the data preparation happens. Here, I'm fetching all 'project' CPTs using `WP_Query`. Crucially, I then use Laravel's `collect()` helper, made available by Acorn, to map each `WP_Post` object into a much cleaner, custom object containing only the `id`, `title`, `permalink`, and `thumbnail` that my template actually needs. This avoids passing the entire, often bloated, `WP_Post` object directly to the view.

Finally, in your `resources/views/partials/content-page-projects.blade.php` (or `page-projects.blade.php`, depending on your target), your code becomes refreshingly simple:

```blade <div class="projects-grid"> @foreach ($projects as $project) <article class="project-item"> @if ($project->thumbnail) <a href="{{ $project->permalink }}"> <img src="{{ $project->thumbnail }}" alt="{{ $project->title }}"> </a> @endif <h2><a href="{{ $project->permalink }}">{{ $project->title }}</a></h2> </article> @endforeach </div> ```

That's all it takes. Your Blade template is now completely devoid of WordPress-specific querying logic. It simply receives a `$projects` variable, pre-prepared and optimized, and renders it. This approach significantly boosts readability, enhances component reusability, and makes your codebase much easier to test and maintain. This is the essence of Sage: leveraging modern PHP standards and robust dependency injection through Acorn to bring order and sophistication to WordPress development, offering a powerful alternative to the unmaintainable complexities of page builders.