Wire data into Blade with view composers
What is a View Composer?
Is a View Composer a Laravel term? No. A view composer is a design pattern used in web development that helps to separate the concerns of generating view data and rendering views in a web application.
In a web application, views often require data from multiple sources, such as a database or API, to be rendered properly. A view composer allows you to encapsulate the logic for retrieving this data and prepare it for the view, so that it can be reused across different views without duplicating code.
Overall, a view composer helps to improve the maintainability and scalability of a web application, by providing a way to encapsulate and reuse view-specific logic, and by reducing the number of database or API requests required to render views.
The Way We Do It Now
Let’s focus on eliminating code-redundancy with the use of View Composers. In our very simple example, we’ll display how to implement a view composer for an application that extract data out of, you should be able to guess it by now, our cars
table.
If you’ve been following along, you should already have a cars
migration and model.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cars', function (Blueprint $table) {
$table->id();
$table->string('make');
$table->string('model');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cars');
}
};
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Car extends Model
{
use HasFactory;
protected $fillable = [
'make', 'model',
];
}
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Car>
*/
class CarFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'make' => $this->faker->word(),
'model' => $this->faker->word(),
];
}
}
<?php
namespace Database\Seeders;
use App\Models\Car;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class CarSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Car::factory()->count(50)->create();
}
}
# php artisan db:seed CarSeeder
INFO Seeding database.
#
You can verify that the data does exist inside the database.
# php artisan tinker
Psy Shell v0.11.9 (PHP 8.1.13 — cli) by Justin Hileman
> Car::all();
[!] Aliasing 'Car' to 'App\Models\Car' for this Tinker session.
= Illuminate\Database\Eloquent\Collection {#4857
all: [
App\Models\Car {#4859
id: 1,
make: "et",
model: "minima",
created_at: "2023-02-07 18:56:39",
updated_at: "2023-02-07 18:56:39",
},
App\Models\Car {#4860
id: 2,
make: "natus",
model: "nemo",
created_at: "2023-02-07 18:56:39",
updated_at: "2023-02-07 18:56:39",
},
App\Models\Car {#4861
id: 3,
make: "nulla",
model: "nihil",
created_at: "2023-02-07 18:56:39",
updated_at: "2023-02-07 18:56:39",
},
Next, let’s create a controller with a couple of methods: index
and show
.
# php artisan make:controller FastCarController
INFO Controller [app/Http/Controllers/FastCarController.php] created successfully.
#
Our index
method will grab all the cars from the database and display them in our views/fastcars/index.blade.php
file. The show
method will also need all of the cars, but this time so that we can switch to a different car. It will display one primary car up top.
index
For the index method, we’ll grab all of the cars from the database and pass it to fastcars.index
view.
<?php
namespace App\Http\Controllers;
use App\Models\Car;
use Illuminate\Http\Request;
class FastCarController extends Controller
{
public function index()
{
$cars = Car::all();
return view('fastcars.index')->with('cars', $cars);
}
}
index
view is pretty simple. It just loops through the cars and outputs them.<h1>Cars</h1>
<ul>
@foreach($cars as $car)
<li>{{ $car->make }} {{ $car->model }}</li>
@endforeach
</ul>
We simply now need a route to view it.
use App\Http\Controllers\FastCarController;
Route::get('/fast-cars', [FastCarController::class, 'index']);
Check the output by visiting the route.
show
Next, let’s create our show
method.
<?php
namespace App\Http\Controllers;
use App\Models\Car;
use Illuminate\Http\Request;
class FastCarController extends Controller
{
// ...
public function show(Car $car)
{
$cars = Car::all();
return view ('fastcars.show', [
'fastcar' => $car,
'cars' => $cars,
]);
}
}
show
method accepts a car and also grabs all the cars at the same time. Both data-sets are then sent to the fastcars.show
view. The view displays the car in question and loops through each of the other cars creating a link to the next car.<h1>{{ $fastcar->make }} {{ $fastcar->model }}</h1>
<ul>
@foreach($cars as $car)
<li>
<a href="/fast-cars/{{ $car->id }}">{{ $car->make }} {{ $car->model }}</a>
</li>
@endforeach
</ul>
We simply need a route now to test the code.
use App\Http\Controllers\FastCarController;
Route::get('/fast-cars/{car}', [FastCarController::class, 'show']);
Visiting the page, we can see that the vehicle is display at the top and we also get clickable links that will change the content up top.
The Laravel View Composer Way
You might have noticed that all of the cars being brought into the application are not sorted. Sorting them is pretty straightforward. We simply need to go into the index
and show
methods and add the orderby
method.
<?php
namespace App\Http\Controllers;
use App\Models\Car;
use Illuminate\Http\Request;
class FastCarController extends Controller
{
public function index()
{
// $cars = Car::all();
$cars = Car::orderby('make')->orderby('model')->get();
return view('fastcars.index')->with('cars', $cars);
}
public function show(Car $car)
{
// $cars = Car::all();
$cars = Car::orderby('make')->orderby('model')->get();
return view ('fastcars.show', [
'fastcar' => $car,
'cars' => $cars,
]);
}
}
What if we needed to do this in more places and not just in one controller? That’s where view composers start to make sense.
We could just use the View::share
like we did in the previous article, right?
<?php
namespace App\Providers;
use App\Models\Car;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
View::share('cars', Car::orderby('make')->orderby('model')->get());
}
}
Technically, yes. What this does is makes the cars
available to all the views, which means that a database query will occur any time that a view is called.
It’s not something that we want to do frequently, and you won’t see it frequently either, but it does work. We can now eliminate the need to fetch the data from our cars
table from the FastCarController
.
<?php
namespace App\Http\Controllers;
use App\Models\Car;
use Illuminate\Http\Request;
class FastCarController extends Controller
{
public function index()
{
return view('fastcars.index');
}
public function show(Car $car)
{
return view ('fastcars.show')->with('fastcar', $car);
}
}
With a view composer, we can attach data to specific views. We’ll still work in our AppServiceProvider
,but instead of using the share
method, we’ll use the composer
method.
The composer
method accepts either a string (the name of the view) or an array of strings (numerous view names). The second argument will be a callback function that accepts our view as an argument and attaches the data that we want to provide.
<?php
namespace App\Providers;
use App\Models\Car;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
View::composer(['fastcars.index', 'fastcars.show'], function ($view) {
$view->with('cars', Car::orderby('make')->orderby('model')->get());
});
}
}
We can return to our views and refresh the page to verify that it is in fact working.
This is great for simple lines of code. If it’s rather intensive, you wouldn’t want to shove everything into the boot
method of your AppServiceProvider
.
Although the documentation does not state where you should place your view composers, but it does state the following: “you could create an app/View/Composers
directory to house all of your application’s view composers.”
I’ll create a View/Composers
directory and then create a CarComposer
class inside there. There’s no artisan command for this one. We’ll simply need to create a class called CarComposer
.
The composer has a required method compose
and that compose
method will inject an Illuminate\View\View
dependency into it. In the AppServiceProvider
we used the View
facade.
The compose
method will simply add the with
method to our $view
like we did in the AppServiceProvider
.
<?php
namespace App\View\Composers;
use App\Models\Car;
use Illuminate\View\View;
class CarComposer
{
public function compose(View $view): void
{
$view->with('cars', Car::orderby('make')->orderby('model')->get());
}
}
What do we do now? We need to reference our view composer class in the AppServiceProvider
.
<?php
namespace App\Providers;
use App\Models\Car;
use App\View\Composers\CarComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
View::composer(['fastcars.index', 'fastcars.show'], CarComposer::class);
}
}
If you check your routes again, you’ll notice that nothing has changed, which is exactly what we wanted.
That’s really all there is to view composers. It’s just another way to pass data around and make your code cleaner.
Laravel Series
Continue your Laravel Learning.
Keep every Blade view in sync—share once, use everywhere
Laravel – P61: Sharing Data With All Views
In part 61 of our Laravel fundamentals series, learn techniques to share global data with every Blade view. Explore View::share, service-provider boot methods, view composers, and caching tips for consistent, performant layouts across your app.
Wire data into Blade with view composers
In part 62 of our Laravel fundamentals series, discover view composers—dedicated classes that push data into one or many Blade views automatically. Learn to register composers, keep controllers lean, and boost render performance.
Render faster, shine brighter
Laravel – P63: Optimizing Views
Part 63 of our Laravel fundamentals series dives into squeezing every millisecond out of your Blade views. Learn view caching, compiled templates, smart component design, and asset optimization to deliver snappy, user-friendly pages.