Table of Contents
The Problem
Your admin panel has a product filter form. Every controller method that renders this view needs to pass the same dropdown data:
// ProductController@index
public function index()
{
$categories = Category::pluck('name', 'id');
$brands = Brand::pluck('name', 'id');
$statuses = ['active', 'draft', 'archived'];
return view('products.index', compact('categories', 'brands', 'statuses'));
}
// ProductController@create
public function create()
{
$categories = Category::pluck('name', 'id');
$brands = Brand::pluck('name', 'id');
$statuses = ['active', 'draft', 'archived'];
return view('products.create', compact('categories', 'brands', 'statuses'));
}
// ProductController@edit
public function edit($id)
{
$product = Product::findOrFail($id);
$categories = Category::pluck('name', 'id');
$brands = Brand::pluck('name', 'id');
$statuses = ['active', 'draft', 'archived'];
return view('products.edit', compact('product', 'categories', 'brands', 'statuses'));
}
Duplicated logic across multiple methods. When you add a new filter dropdown, you update 5+ methods.
The Fix
Use a view composer to bind data automatically:
// app/Providers/ViewServiceProvider.php
use Illuminate\Support\Facades\View;
use App\Models\Category;
use App\Models\Brand;
public function boot()
{
View::composer('products.*', function ($view) {
$view->with([
'categories' => Category::pluck('name', 'id'),
'brands' => Brand::pluck('name', 'id'),
'statuses' => ['active', 'draft', 'archived'],
]);
});
}
Now your controllers stay thin:
public function index()
{
return view('products.index');
}
public function create()
{
return view('products.create');
}
public function edit($id)
{
$product = Product::findOrFail($id);
return view('products.edit', compact('product'));
}
How It Works
The composer runs before every view that matches products.*. The data is automatically injected, so your Blade templates can access $categories, $brands, and $statuses without the controller passing them.
When to Use It
- Shared dropdown data across multiple views (categories, statuses, countries)
- Default filter values that every page needs
- Navigation menus or sidebars that appear on many pages
- Current user permissions for conditional UI elements
Trade-Offs
The data is fetched on every view render, even if the template doesn’t use it. If your composer queries are expensive, cache them or use class-based composers with dependency injection for better control.
But for simple dropdown data, view composers eliminate duplication and keep your controllers focused on actions, not view preparation.
Leave a Reply