47
Php

Laravel 4 View Composer and Master layout

In Laravel 4 there is a very nice way to bind data automatically with a view using a View Composer. View composers are callbacks or class methods that are called when a view is created. If you have data that you want bound to a given view each time that view is created throughout your application, a view composer can organize that code into a single location.

Defining a view composer according to the Laravel’s documentation is as given below

Now each time the profile view is created, the count data will be bound to the view. You may also attach a view composer to multiple views at once and also it’s possible to use a class based composer, you can read more on Laravel’s documentation.

Well, but what about layout, if we can use this view composer the same way for the master layout then it would be more helpful for us. One real use case is that, in a project using Laravel 4, I’ve a dynamic menu and I wanted the menu to be available automatically in the main layout whenever I show a page by loading a view. So, what I did is that, I’ve used view composer for my master blade layout to bind the menu with the layout, so every time I load a view, my menu becomes available to the page. Here is the code sample, I’m keeping it in filters.php file

This the master blade layout (Just the menu part)

And this is the file that contains the menu building loop (menu.blade.php)

That’s all. Now every time I show a page, my menu just gets loaded automatically with the master layout in the page. In the view composer I’ve used if($view->myApp->isLogedin) to check whether the user is logged in or not and depending on that i’m showing either the login or logout link in the menu. So,the question is that, where the code if($view->myApp->isLogedin) came from, how is it related in the composer function!

Actually, I have used the before filter of Laravel as given below

In the before filter I’m just making a singleton object (check IoC container) and sharing it through out the application with every view, so I can use this object anytime to check the user’s login status and user data instead of using the if (Auth::check()) {...} and Auth::User() again and again, I think it’s better because Auth::User() generates a query every time I use it to access some information for the logged in user. I’m also setting the default title in this singleton IoC container and can access it using $myApp->title or $myApp->user->username from any child view. That’s all.

Laravel 4 has added some other cool features and one of the coolest feature is Model Events. it allows a grate way to fire some events when creating, updating or deleting a model and even more. It lets us do the validation in the model directly. I think this is really a very cool feature and certainly there are a lot more to learn about Laravel 4. Just a matter of time, I’m loving it.

  • smronju

    (Y)

  • Neto

    Thanks!

    But where I can put this logic?

    Where I create a view composer creation? In app/routes.php?

    • Welcome! You can keep it in routes.php or in filters.php, it doesn’t matter.

  • longestdrive

    Hi. Great article. I’m learning L4 and this is exactly what I need.

    I’ve tried to implement as above and placed the code in filters.php, created a menu.blade.php in the views folder.

    On trying to execute I get an error:

    Call to undefined method IlluminateDatabaseQueryBuilder::getMenu()

    I already have a controller named Page so I assume it’s trying to call a function from there. I’m not quite sure what your code:

    $menu – Page::getMenu()

    Is doing so not sure whether it’s a conflict or how to start amending for my set up.

    Your help would be appreciated

    • I’ve replied, please check and let me know if you need more help. Thanks 🙂

  • longestdrive

    Hi

    Great article…just what I needed.

    I’ve tried to implement but have an error with Page::getPage(). I have a controller for Page already – is this function unique to your app or have I missed something? error is can’t find function.

    I removed that line just in case but now have an error:

    Class ‘myApp’ not found

    I’ve placed the code in filters.php as suggested including the before filter you created which I assume generates the myApp class. I’ve run a composer dump-autoload but error still there.

    Again have I missed something here.

    I’m new to L4 – gradually picking up the pace so be gentle!!!

    Ta

    • Hi @disqus_TsKFIciUTJ:disqus, The Page::getPage() is unique to my App, Page is model and getPage() is a function which returns all the menus/pages which has been added from the backend of the app. for example, try this instead of Page::getPage(), this (getPage()) returns a similar array.

      $menus = array(
      array( 'title' => 'Home', 'link' => '#' ),
      array( 'title' => 'About', 'link' => '#' ),
      array( 'title' => 'Contact Us', 'link' => '#' ),
      array( 'title' => 'More', 'link' => array(
      array( 'title' => 'Submenu Item One', 'link' => '#' ),
      array( 'title' => 'Submenu Item Two', 'link' => '#' ),
      )
      )
      );
      if($view->myApp->isLogedin)
      {
      $menus[] = array('title' => 'Logout', 'link' => '#');
      }
      else{
      $menus[] = array('title' => 'Login', 'link' => '#');
      }
      $view->with('menus', $menus);

      • longestdrive

        Hi

        Thanks for your help.

        I’ve changed the code but still getting an error:

        Class ‘myApp’ not found

        So, i’m assuming myApp is not being created? I’ve placed the code:

        App::before(function ($request) {

        in filters.php

        Does this get called automatically or do I need to call it from elsewhere? Do I need to add anything to the route for teh calling page to get this to work?

        Sorry, new to L4 and don’t quite understand ioc etc so following blindly!

        Thanks

        • No, you don’t need to call anything, it should be called automatically.

          • longestdrive

            Hi – me again!

            I’ve tried various methods to get this to work but still getting an error – class ‘myApp’ not found.
            The class doesnt appear to have been created. Is there some other code I’m missing here? Do I need to change anything in the app.php config file?
            Sorry, can’t work this out

          • @disqus_TsKFIciUTJ:disqus , In the App:before(...) you have to use
            $app = App::make('myApp');
            View::share('myApp', $app);
            so you can access the $view->myApp from any View, did you make and shared it ?

  • longestdrive

    Hi

    Great article…just what I needed.

    I’ve tried to
    implement but have an error with Page::getPage(). I have a controller
    for Page already – is this function unique to your app or have I missed
    something? error is can’t find function.

    I removed that line just in case but now have an error:

    Class ‘myApp’ not found

    I’ve
    placed the code in filters.php as suggested including the before filter
    you created which I assume generates the myApp class. I’ve run a
    composer dump-autoload but error still there.

    Again have I missed something here.

    I’m new to L4 – gradually picking up the pace so be gentle!!!

    Ta

  • Anees

    Hii,

    Its really helpful. But one thing I am not getting. I have a master.blade.php layout. All other views extends this master layout.

    I have a feature.blade.php view composer view. I want to call this in my master view.
    Because all my view extends from master, so master view is never called like e.g

    Route::get(‘master’,function() {
    return View::make(‘feature’);
    });

    If I could explain it properly….Please elaborate….Thanks

    • I’m confused about your question anyways, a view composer should be bound to a view, for example, master layout (also a view) and if a view composer is bound to master layout and any view which extends this master layout then it will invoke the view composer.

  • stefanwegner

    Hey,

    I’m new to Laravel and a little bit confused – or maybe i just don’t get it:

    If I do something like this: (it’s a not working code, but i hope you’ll get it)

    class HeroController extends Foo
    {
    protected $layout = ‘my.heromaster’;

    // [retrieve some various data]

    $this->layout->content = View::make(‘my.heroes’)->with(‘hero’, $data);
    }

    And I have also (in filters.php):

    View::composer(‘my.heroes’, ‘HeroesComposer’);

    // composers/heroes.php

    class HeroesComposer
    {
    public function composer($view){
    // … get some data
    $view->with(‘stats’, $stats);}
    }

    … The $stats are not available in my.heromaster

    (I did the @include the same way you did, but the data ($stats) are not available)

    My intention is the protected $layout attribute in HeroesController causes this problem…

    Do you have any idea of make this work?

    Thanks for your article by the way 😉

    • Sorry for the late reply, what is your specific problem ?

      • stefanwegner

        Hi, my problem is, that when i use View::composer in combination with “protected $layout” in my Controller, the Data of the Composer is not passed to the view

        • I don’t think there is a limitation in a protected layout but didn’t try it myself and not sure unless I see the code, so make sure if your implementation is right and it works in a blade layout, it’s kind of debugging, so you can make sure that you didn’t make any mistakes.

          • stefanwegner

            My implementation works fine when I “@extends” the Layout in the view itself, but it’s not working when I just change it to the “protected $layout” – the data I want to pass from the View::composer isn’t available in the Master-View …

  • Craig Tadlock

    This is one of the best ideas that I have come across. It is a MUCH needed addition to the application architecture. It allows for reusable code and minimizes and centralizes the database access. Nice work!

    • @craigtadlock:disqus ,Thanks! Really glad to know that you like the idea 🙂

  • rohu1990

    Can you please share us your entire base app ?

    • Sorry ! Which base app you are talking about ?

  • Hallo,

    how to make simple menu with url like this mywebsite.de/media/image

    i make routers like this:

    <?php
    Route::get(‘media’, function() {
    return View::make(‘pages.media’);
    });

    Route::get(‘media/image’, function() {
    return View::make(‘pages.images’);
    });
    ?>

    and the menu like this:

    <li><a href=”media”>Media</a>
    <li><a href=”media/image”>Image</a>

    the problem is when i’m in mywebsite.de/media/image then i want go back to media the url from media link is mywebsite.de/media/media and i getting error 404 page

    can you help me?

    • @disqus_eoD4IYbEQX:disqus , Not clear abiut the problem. BTW, you may use to generate a link.

      • the problem is when i’m in mywebsite.de/media/image page, then i want go back to home the link home is mywebsite.de/media and i getting error 404 page

        can you make pleas tutorial to make menu with sub menu like this mywebsite.de/media/image

        BTW your link is 404 error.

        • @disqus_eoD4IYbEQX:disqus, Sorry, it wasn’t a link but a code which accidentally converted to a link. I’m writing a book on Laravel so right now can’t write another big article on this but I’ll try to cover it in my book and if possible, I’ll post an article soon.

          • Hallo @sheikh Heera,
            Is ok, i see the problem was base url, link was a href without base url, but when i use {{HTML::link}} from laravel work perfect, because the url use base url, is that possible to put base url Not in header? In routers maybe? So People dont See in header base url?

            Thank

          • It’s possible if you use but you should avoid it.

          • Hallo Sheikh,

            thank you for your answer.

            look this: http://www.balkan-booking.de

            the second menu i have to put in header, and then work good.

            Look the source code then you can see different from those menu: view-source:http://www.balkan-booking.de

            how to put base url not in header?
            if that not impossible, is ok, thank you for all your help.

            Regard
            Wiyono

          • If you don’t put it in the header it would work because {{ url('something') }} would make it a complete url, if you use {{ route('routeName') }} it’ll also set the right url.

          • what do you mena with {{ route(‘routeName’) }}

            can you please simple example?

            i just want that people don’t see the base url in header and the link menu without showing the base url, ex: <a href=”about-us”>About us</a>

            Thank you

          • @disqus_eoD4IYbEQX:disqus, You can generate link using href="{{ url(urlwithoutbase) }}" or href="{{ route('routename') }}" so the route’s url will be generated and for this you have to declare the route and have to assign a name for it during route drclaration. So, you can generate the url using the route name.

          • Ok, thank you for all 🙂

            Regard
            Wiyono

  • Thanks for the post Sheikh. I just want to point out that Auth::user() does not query the database every time it’s called. The user model is cached in IlluminateAuthGuard after the first Auth::user() call.

    • @disqus_72SOZIEzvB:disqus,Thanks mate for pointing that out 🙂

  • Emmanuel

    Is this working now with View Creators instead of View Composer for Laravel 4 ?

    • I think so, but didn’t try View::creator with this example so far, why ?

  • Thanks, you just save me several hours of development

    • @DooBie_DooBie:disqus Welcome! Glad to know that, it helped 🙂

  • MJB

    I keep receiving: “Trying to get property of non-object” on the $view->myApp->isLogedin line in the composer.

    Am I missing a step somewhere? The variable $app is null when i check it from the composer. If I remove that line and just use true as the test it works just fine. I can also access “$myApp->title” from one of the child templates so the object is being created.

    I think they are not being loaded in the correct order.

    • Maybe something went wrong but you may skip that 🙂

  • J Michael Terenin

    Hi Heera,
    Great work on these articles, much needed for a framework that requires so much research.
    If there’s more than one View Composer, how can you determine the order in which they are called? Specifically, I’ve created one for a package that also uses one, and the data array i wish to modify is being instantiated in theirs. Mine is being called first, and the data I add is being erased.

    • J Michael Terenin

      Someone pointed me toward Factory.php in the Illuminate/View namespace, where there’s a 3rd parameter named priority when you register your View Composer. Setting it to -1 made it execute AFTER the package’s View Composer. Nice undocumented tidbit to know

      • Glad you solved it and thanks for sharing the experience 🙂

Latest Blog