Laravel Tutorials - Laravel Layout using Blade templates

Laravel Layout using Blade templates

2020-03-21 Lukas Markevičius
Laravel Layout using Blade templates featured image

Today we are going to learn more about blade templates and how to use it to create layouts in Laravel. If you have followed my previous tutorials on How to create step by step Laravel project you might have noticed that every view file had .blade.php extension which means that it uses blade templating engine. Although we have already used some of the authentication directives such as @auth or if statements @if, we haven't fully used what Blade could offer us. So today we are going to explore how could we improve our layout and get advantages of using Blade.

If you have followed my previous tutorials you might have noticed that we had similar views and repetitive code through out the project. For all the posts page, create page, edit page, categories page there are always the same html tags, head tag with repetitive scripts, body tag, navigation is always there. There are so many places where we could miss something or make an error and it would be hard to track it. Imagine that we would need to add a menu item to the navbar, now we would need to go through each file where it has navbar and add one menu item. Wouldn't it make more sense to create a navbar in one place and just include in pages where it's needed? That way if you would need to update the navbar you could just do it in one place and the changes would be visible everywhere. Lucky for us Blade templating engine offers us @include directive which lets us solve exactly this problem. So let's jump right into it!

First of all, let's define our views folder structure. I like to have a folder layouts where it holds main layout file called app.blade.php. Depending on complexity of your project you can define several layout files and use it through out the project if it requires different kind of layouts. So the folder structure should look like this:

resources
  | assets
  | lang
  | views
    | auth
    | categories
    | layouts
    | partials
    | post
    home.blade.php

If you have read my other post on How to make Laravel authentication you might have already have the layouts folder if you don't have don't worry we will go through it together. As I have mentioned before we will use layouts folder for keeping layouts files. The other folder that we will need is partials. In this folder we will keep all the repetitive parts of our project such as header, navbar, flash messages, etc. So let's look into our app.blade.php file inside our layouts folder:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

    @include('partials._header')

    <body>

        @include('partials._navbar')

        @include('partials._messages')

        <div class="main mt-5">
            @yield('content')
        </div>

        @include('partials._footer')

    </body>
</html>

As you can see I left only html tag which contains @include directive for including header section and inside body tag we have included navbar, messages for session messages, footer and a bit different directive called @yield. The difference between @include and @yield directives is that @include includes static sections of our code and using @yield we can extend our files by putting our code structure inside these tags. You will see how in a moment. So let's define our sections. So in resources/views/partials folder we will create file called _header.blade.php in which we will copy header part of our older view files:

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>

Now we can create _navbar.blade.php file:

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="{{ route('post.index') }}">{{ config('app.name') }}</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
        aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item {{ Request::is('post') ? "active" : "" }}">
                <a class="nav-link" href="{{ route('post.index') }}">Home</a>
            </li>

            <li class="nav-item {{ Request::is('category') ? "active" : "" }}">
                <a class="nav-link" href="{{ route('category.index') }}">Categories</a>
            </li>
        </ul>

        <ul class="navbar-nav ml-auto">
            @guest
                <li class="nav-item {{ Request::is('login') ? "active" : "" }}">
                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                </li>
                <li class="nav-item {{ Request::is('register') ? "active" : "" }}">
                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                </li>
            @else
                <li class="nav-item {{ Request::is('post/create') ? "active" : "" }}">
                    <a href="{{ route('post.create') }}" class="btn btn-success my-2 my-sm-0">Create Post</a>
                </li>
                <li class="nav-item dropdown">
                    <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                        {{ Auth::user()->name }} <span class="caret"></span>
                    </a>

                    <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                        <a class="dropdown-item" href="{{ route('logout') }}"
                            onclick="event.preventDefault();
                                            document.getElementById('logout-form').submit();">
                            {{ __('Logout') }}
                        </a>

                        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                            @csrf
                        </form>
                    </div>
                </li>
            @endguest
        </ul>
    </div>
</nav>

You can see that the code is almost identical with just a little tweaks. The main difference is that when using sections with @include directive we need to make sure that certain parts would stay dynamic. One of those parts are navbar links. When you visit certain page certain element in the navbar should have 'active' class which makes it lighter color to make visible difference for a user and let him know which page is currently active. So we can achieve this by using {{ Request::is('category') ? "active" : "" }} inside our class. It checks if the current URL has category part in it and if it does we can add class name 'active' to the li class attribute making it visible for the user. Of course for different navbar items we need to check for different URL parts but you get an idea.

Lets now create _messages.blade.php file:

@if (Session::has('success'))
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        <h4 class="alert-heading">Success!</h4>
        <p>{{ Session::get('success') }}</p>

        <button type="button" class="close" data-dismiss="alert aria-label="Close">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
@endif

@if (Session::has('errors'))
    <div class="alert alert-danger alert-dismissible fade show" role="alert">
        <h4 class="alert-heading">Error!</h4>
        <p>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </p>

        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
@endif

And finally _footer.blade.php file:

<script
    src="https://code.jquery.com/jquery-3.4.1.min.js"
    integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
    crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>

@yield('scripts')

You can see that we are also using @yield directive called scripts. As you can guess this part will be used to include scripts into certain pages because not in all pages you need to load certain scripts.

For the final part, let's just take one view file as an example to show you how to use your pages together with layout file. So in this case we will edit resources/views/post/index.blade.php file:

@extends('layouts.app')

@section('content')
    <div class="container py-3">
        <div class="row">
            @foreach($posts as $post)
                <div class="col-md-4 mb-3">
                    <div class="card">
                        <div class="card-header">
                            <h3>{{ $post->title }}</h3>
                            <p class="text-muted">{{ $post->category ? $post->category->name : 'Uncategorized' }}</p>
                        </div>
                        <div class="card-body">
                            <p>{{ substr($post->description, 0, 100) }}</p>
                            <a href="{{ route('post.show', $post->slug) }}" class="btn btn-primary btn-block">Read More</a>
                        </div>
                    </div>
                </div>
            @endforeach
        </div>
    </div>
@endsection

So with @extends('layouts.app') directive we specify the path to the layout file we want to use. Including only this part it would render our all other sections we included in our layout file. All the header parts, navbar, messages and footer parts. Now we can use @section('content') directive to put our unique code for the certain page. To close our section we need to put @endsection tag. And this directive works together with @yield directive that we have declared in our layouts file. Just keep in mind that the name inside the tag can be called however you like just that it needs to match.

And that's it! If you haven't missed anything you shouldn't notice any difference in the browser. But isn't it much more clearer and readable? Now if you would like to add something to the navbar or add additional scripts to all pages you can do it just in one file and the changes would be visible through out all the pages. Pretty sweet, huh? To get a source code you can get it from this repository and don't forget to checkout to better-structure tag to get exactly to the end of this tutorial:

$ git checkout better-structure -b <your-branch-name>

Remember there are multiple ways of how to structure your view files and this is a basic idea of how I am using it. If you see a room for improvement or if you have a better way, feel free to leave a comment down below with your suggestions.

Laravel Layout using Blade templates pinterest image

Related Tutorials

How to Generate a Simple XML Sitemap using Laravel

Posted 2020-03-08 Lukas Markevičius

Today we are going to learn how to generate a simple XML sitemap using Laravel. Mainly you can create it either manually or create some sort of automated solution. To create it manually you can make y...

How to Create Slugs in Laravel

Posted 2020-03-27 Lukas Markevičius

Today we are going to learn how to create slugs in Laravel. You are going to learn: Why use slugs How to create slugs How to show objects with the slugs How to edit slugs Why use Slugs If yo...

Back