Adding data to every view safely in Laravel 5.1

In Laravel 5.1, it’s really easy to share data with every view. Every page of your site might need the currently logged in user, or the footer needs that phone number or those opening hours from your database.

The Laravel 5.1 documentation’s approach to sharing data with every view works for most circumstances, but I recently learned of a case where it breaks things: running artisan.

The problem

The documented approach to sharing data with all views tells us to add code to AppServiceProvider->boot():

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        view()->share('key', 'value');
    }
}

This approach is fine, for certain values.

view()->share('siteName', 'Banana Bill's Boat Boutique');
````

Literal values won't be a problem.

```php
view()->share('currentUser', Auth::user());

Here, if someone’s logged in, Auth:user() will be an instance of the User model. If not, it’ll be null. No problem here either.

view()->share('openingHours', OpeningHours::currentHours());

Here’s our problem. We’re asking the OpeningHours model to get the current opening hours. And most of the time, this will work. However, if you haven’t run your migrations yet, your pages will give you an SQL error saying the opening_hours table is missing.

And so you get the hint and run your migrations to create the table. php artisan migrate

…and you get the same error. You say to the computer “I’m trying to make the table right now! Lay off!”. But no dice, you continue to get the same error.

The issue here is that everything in AppServiceProvider->boot() runs even when you run artisan commands. It’s not normally an issue, but if you’re a new developer installing a project on your dev machine for the first time, this could really throw you.

The solution

Create a new service provider and have that create a View Composer for us instead.

In the example above, we’d comment out our share command, and we’ll get the use of artisan back:

php artisan make:provider ViewComposerServiceProvider

This will create /app/Providers/ViewComposerServiceProvider.php

Add the new service provider to /config/app.php‘s providers array:

    'providers' => [
        ...
        App\Providers\RouteServiceProvider::class,
        ...
    ]

And then in ViewComposerServiceProvider->boot() create a view composer:

    public function boot()
    {
        View::composer('*', function($view)
        {
            $view->with('openingHours', OpeningHours::currentHours());
        });
    }

ViewComposerServiceProvider->boot() will still be called when you invoke artisan, but OpeningHours::currentHours() will only be called when you a view is created. Now your app won’t break during a first time deployment. Hooray!

TL;DR view()->share() with models works great until your database isn’t there. Use view composers instead.


Also published on Medium.