Laravel 8 View Composers

What is View Composer?

Laravel view composers are either callbacks or class which is called when specified view is rendered. We will take a practical example to understand view composers in depth.

Imagine you have a blog website you have following two pages:

  • Landing Page where you list all recent posts
  • Posts Page where you list all recent posts

Let's look at the diagram to understand the flow of data in above case:

Here, we have defined two routes:

  • Home Page: /
  • Post Page: /posts

Above two routes are connected with appropriate controller methods using routes/web.php file as shown below:

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('home');
});

Route::get('/posts', function () {
    return view('post');
});

For each routes we have corresponding views:

layout/base.blade.php

<html>
<head>
    <title>@yield('title')</title>
</head>
<body>
    @yield('content')
</body>
</html>

home.blade.php:

@extends('layout.base')
@section('title', 'Home Page')

@section('content')
    @include('post.list')
@endsection​

post.blade.php

@extends('layout.base')
@section('title', 'Post Page')

@section('content')
    @include('post.list')
@endsection​

post/list.blade.php

@foreach($posts as $post)
  <h1>{{ $post->title }}
@endforeach​

Let's consider following scenarios with above structure:

  • We are using same view for two different pages
  • Let's assume that post.list view takes $posts variable which is a list of posts and then generates list of blogs in html format
  • We can think of passing this $posts variable in following way:
    • Either you can pass $posts variable from home or post route or controller and use it in out post.list view
    • What if this view is included in some other pages is it fisible to pass $posts variable from all the routes where this view is shared?
    • We pretty much duplicating the same code across different routes which is not ideal in such case we would use view composer

How to use Laravel view composer?

We can use view composer where views are commonly shared across different routes or controllers. What we want to do here is that whenever post.list view is included in any pages we would call a callback or a class and prepare the data that we need to pass into our post.list view.

This way we do not need to pass in data that our view needs from different controllers. Consider following diagram:

What is happening here?

  • When home or post page is loaded it includes post.lists view
  • When post.list view is rendered it calls view composer
  • When view composer is called it calls the post model and gets all the posts and sends $posts variable to called view i.e. post.list

Create ViewServiceProvider

In order to create view composer let us first create a service provider which help us organize our view composers in one place. Open your terminal and type following command:

# if you are not using sail
php artisan make:provider ViewServiceProvider

# if you are using laravel sail
./vendor/bin/sail artisan make:provider ViewServiceProvider​

For purpose of our demo I assume that you have Post model with title column. I would assume following:

  • you have a posts table with id, title column
  • you generated post migration in laravel already
  • you have Post model defined

Once it generates ViewServiceProvider open created file and add following code:

<?php

namespace App\Providers;

use App\Models\Post;
use App\View\Composers\PostComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ViewServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Use following code if you prefer to create a class
        // Based view composer otherwise use callback
        View::composer('post.list', PostComposer::class);


        // Use following code if you want to use callback
        // Based view composer instead of class based view composer
        View::composer('post.list', function ($view) {

            // following code will create $posts variable which we can use
            // in our post.list view you can also create more variables if needed
            $view->with('posts', Post::orderByDesc('created_at')->paginate());
        });
    }
}

If you are using class based view composer then you need to create a new class called PostComposer under app\View\Composers:

<?php

namespace App\View\Composers;

use App\Models\Post;
use Illuminate\View\View;

class PostComposer
{
    /**
     * @param View $view
     */
    public function compose(View $view)
    {
        $view->with('posts', Post::orderByDesc('created_at')->paginate());
    }
}

Finally, you need to tell laravel to load your newly created service provider. Open config/app.php file and add your ViewServiceProvider to following list:

<?php

// some other code

'providers' => [

    // Some other providers goes here

    // Add View Composer Provider
    App\Providers\ViewServiceProvider::class
 ],

That's all now when you go to your browser and hit either http://localhost or http://localhost/posts you will see list of posts displayed on your browser.