Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel View Composer variables not accessible in views that extend layout

I am using a Laravel view composer to share a couple of variables with all views.

app/composers.php:

View::composer('layouts.base', 'MyApp\Composers\BaseComposer');

My understanding here is that anything that uses layouts.base will get the view composer data.

BaseComposer@compose, simplified:

 public function compose($view) {
      // Some logic left out here that pulls in some data

      $data = array(
        'name' => 'John',
        'status' => 'active'
      );

      $data = (object) $data;

      return $view->with('global', $data);
    }

Given this layouts.base:

  {{ $global->name }}

  @include('layouts.partials.header')

  @yield('content')

$global->name is found and so is this in the included layouts.partials.header:

  {{ $global->status }}

But, a view that extends layouts.base throws an Undefined variable: global error:

home.blade.php

@extends('layouts.base')
@section('content')
  {{ $global->name }}
@stop

Everything works fine if I modify composers.php to reference home:

View::composer(['layouts.base', 'home'], 'MyApp\Composers\BaseComposer');

I'd like to understand why if home extends layouts.base it can't see the view composer variables without this extra step.

like image 679
Jkleg Avatar asked Oct 08 '14 20:10

Jkleg


1 Answers

The problem here is the order of how things happen. View composers get called before the matching view gets rendered. That means in your case:

  1. view home gets rendered
  2. view composer for layouts.base gets called
  3. view layouts.base gets rendered

This results in the variable global not being available in the home view.

Here are some solutions that may help

Change the composer pattern

Change your composer match pattern to include all the views that need the variable. Make use of the wildcard * if possible.
You can even match each and every view:

View::composer('*', 'MyApp\Composers\BaseComposer');

Don't use composers at all

If you need your data in (nearly) every view anyways you can use View::share during start of the application.

Put this in app/filters.php, app/start/global.php or a similar file

$data = array(
    'name' => 'John',
    'status' => 'active'
);

$data = (object) $data;

View::share('global', $data);

Rethink

Why do different views need the same global data? Could you maybe move the part of the view that needs the data into a partial view which you can then include?

like image 63
lukasgeiter Avatar answered Nov 09 '22 05:11

lukasgeiter