facebook youtube pinterest twitter reddit whatsapp instagram

Laravel Blade Templating Engine

In our last guide, we discussed the basics of Laravel routing and controllers, in this guide, you'll learn how to customize your Laravel views with the Blade templating engine.

So far, here is my views structure:

Laravel Pages views

frontpage.blade.php contains:


<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{config('app.name', 'SimpleApp')}}</title>
</head>
<body class="antialiased">

<h1> Welcome To SimpleApp</h1>
<p>This is our simple application</p>

</body>
</html>

dashboard.blade.php contains:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{config('app.name', 'SimpleApp')}}</title>
</head>
<body class="antialiased">

<h1>DashBoard Page</h1>
<p>Welcome To Users Dashboard</p>

</body>
</html>


Defining and Extending Blade Layout

Both of these files have a whole lot in common, and as I progress in my app development, there is a high probability that I might reuse some common component in the view files, so, instead of copying and pasting every time I wanna reuse a certain component, I'll create a basic layout for all of them.

So, in your views folder, create a folder named layout, and inside the layout folder, create a new file titled app.blade.php:

2. Layout_app.blade

Inside the app.blade.php view, we use the following code:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{config('app.name', 'SimpleApp')}}</title>
</head>
<body class="antialiased">
    @yield('content')
</body>
</html>

The only thing different here is that I added @yield('content') to the body of the app.blade.php file.

The @yield directive is used to display the contents of a given section, by doing this means we are defining a layout for our application, so, let's define a child page view that would inherit the layout.

Now, open the frontpage.blade.php, get rid of everything except the h1 tags and the p tags like so:

@extends('layout.app')

@section('content')
    <h1> Welcome To SimpleApp</h1>
    <p>This is our simple application</p>
@endsection

likewise in the dashboard.blade.php:

@extends('layout.app')

@section('content')
    <h1>DashBoard Page</h1>
    <p>Welcome To Users Dashboard</p>
@endsection

We use the Blade @extends directive to specify which layout our child view should inherit, in our case the frontpage.blade.php and the dashboard.blade.php are our child views.

Views that extend a Blade layout may inject content into the layout's sections using @section directives, in our case, we are extending the section with the h1 and p tag, you can add more, of course, doing things this way can save us the hassle of repeating ourself in multiple places.

Displaying/Parsing Data

You may display or parse data passed from the controller to your Blade views by wrapping the variable in curly braces. or you can even pass it from the route directly to your view:

Route::get('/test', function () { // testpage
    return view('frontpage', ['user' => 'The_Devsrealm_Guy']);
});

and in my frontpage view, I can access the user data like so:

@extends('layout.app')

@section('content')
    <h1> Welcome To SimpleApp, {{$user}}</h1>
    <p>This is our simple application</p>
@endsection

and when I go to the /test page, I get:

Access data in blade view

who told you, you can't use PHP functions:

@extends('layout.app')

@section('content')
    <h1> Welcome To SimpleApp, {{$user}}</h1>
    <p>This is our simple application</p>
    <p> The current date is {{date('l jS \of F Y h:i:s A')}}</p>
@endsection


PHP function in blade

You are not limited to displaying the contents of the variables passed to the view. You can put any PHP code you wish inside of a Blade echo statement.

In our previous guide, the controller is the one delegating the view, you can delete the test route, and we can do it in our PagesController like so:

public function index() { // Frontpage
        $user = 'The_Devsrealm_Guy';
        return view('pages.frontpage')->with('user', $user);
    }

The first with() parameter is what we want to call the variable inside of the view, and the second parameter is the variable you are referring to.

So, if you want to access the $user variable in your view, you simply use the double curly braces as we've done before:

@extends('layout.app')

@section('content')
   <h1> Welcome To SimpleApp, {{$user}}</h1>
   <p>This is our simple application</p>
@endsection

The double curly braces are the same as

<h1> Welcome To SimpleApp, <?php echo e($user); ?></h1>

in PHP, the blade template just makes it easier to write things, writing <?php ?> can be tiresome if you ask me, but really you don't even have to use the blade template engine, use whatever you want.

Let's progress, if you want to pass in multiple values in your controller, you can use an associative array, you would then use the key-value pair of whatever you want to parse in your view, here is an example:

public function index() { // Frontpage
    $data = [
        'title'=>'Welcome To SimpleApp',
        'Description'=>'This is our simple application',
        'author'=>'The_Devsrealm_guy'
    ];
    return view('pages.frontpage')->with($data);
}

Normally, you'll think you would be able to use something like $data['title'] in your view (I'll show you how to do that below), but that won't work, to access the data in your view, just do:

@extends('layout.app')

@section('content')
    <h1> {{$title}}</h1>
    <p>{{$description}}</p>
@endsection


Which would translate to:'

Passing array from controller to view

If you do this:

$data = [
    'title'=>'Welcome To SimpleApp',
    'description'=>'This is our simple application',
    'author'=>'The_Devsrealm_guy'
];
return view('pages.frontpage')->with($data);

Laravel would split each key in the array into a variable, meaning, to access the title in your view, you do: {$title}}, to access the description in your view, you do {{$description}}, and so on. Doing things this way means, you won't be able to loop through the array itself, if you want to loop through the array, you can do:

    public function index() { // Frontpage

        $data = [
            'title'=>'Welcome To SimpleApp',
            'description'=>'This is our simple application',
            'author'=>'The_Devsrealm_guy'
        ];
        return view('pages.frontpage')->with('data', $data);

    }

I passed in the array variable in the first parameter position of the with() function. To then loop through it in your view, you can do:

@extends('layout.app')

@section('content')
    <h1> {{$data['title']}}</h1>
    <p>{{$data ['description']}}</p>

    @foreach ($data as $item)
    <li>{{$item}}</li>
    @endforeach

@endsection

The output:

Laravel blade loop

Easy right? Let's proceed. Let's do something interesting, look at the following code in my controller:

public function index() { // Frontpage

    $data = [
        'title'=>'Welcome To SimpleApp',
        'description'=>'This is our simple application',
        'author'=>'The_Devsrealm_guy',

            'post' => ['Post 1', 'Post 2', 'Post 3']
    ];
    return view('pages.frontpage')->with('data', $data);

}

This is what the data array contains:

Array
(
    [title] => Welcome To SimpleApp
    [description] => This is our simple application
    [author] => The_Devsrealm_guy
    [post] => Array
        (
            [0] => Post 1
            [1] => Post 2
            [2] => Post 3
        )

)

A multidimensional or nested array right? Here is what we can do, I'll write a conditional that checks if there is value in the [post] child array, if there is, we would output all of 'em, if otherwise, we would output 'No Post'.

So to pull data out of the nested array, we call the actual array position, which has a key of [post], and we then call it’s children, e.g [0], [1], [2], etc, the below is an example:

@extends('layout.app')

@section('content')
    <h1> {{$data['title']}}</h1>
    <p>{{$data ['description']}}</p>

    <ul>
        <li>{{$data['post'][0]}}</li>
        <li>{{$data['post'][1]}}</li>
        <li>{{$data['post'][2]}}</li>
    </ul>

@endsection

Output:

Multidimensional array

We can make things easy for ourselves by using a foreach loop instead:

@extends('layout.app')

@section('content')
    <h1> {{$data['title']}}</h1>
    <p>{{$data ['description']}}</p>

    <ul>
        @foreach($data['post'] as $item)
            <li>{{$item}}</li>
        @endforeach
    </ul>

@endsection

Now, back to the condition I wanted to create, if there is a post in the array we return it, if otherwise, we return no post, here is how you can write that:

@extends('layout.app')

@section('content')
    <h1> {{$data['title']}}</h1>
    <p>{{$data ['description']}}</p>

    @if(count($data['post']) > 0 )
        <ul>
            @foreach($data['post'] as $item)
               <li>{{$item}}</li>
            @endforeach
        </ul>
    @else
        <p>No Post</p>
    @endif

@endsection

As I said previously, you can use any PHP function with blade, blade is just a template engine that makes writing PHP easy, nothing more.

So, if we have items in the array we would get:

Multidimensional array

If there is no item in the array:

    public function index() { // Frontpage

        $data = [
            'title'=>'Welcome To SimpleApp',
            'description'=>'This is our simple application',
            'author'=>'The_Devsrealm_guy',

                'post' => [] // No Item
        ];
        return view('pages.frontpage')->with('data', $data);

    }

You get:

No Post in nested array

Interesting ;)

Let's make it even more interesting, when looping, a $loop variable will be available inside of your loop. This variable provides access to some useful bits of information such as the current loop index and whether this is the first or last iteration through the loop:

@extends('layout.app')

@section('content')
    <h1> {{$data['title']}}</h1>
    <p>{{$data ['description']}}</p>

    @if(count($data['post']) > 0 )
        <ul>
            @foreach($data['post'] as $item)

                @if ($loop->first)
                    {{$item}} - Is The First Iteration
                @endif

                <li>{{$item}}</li>

                @if ($loop->last)
                    {{$item}} - Is The Last Iteration
                @endif

            @endforeach
        </ul>
    @else
        <p>No Post</p>
    @endif

@endsection

Output:

The Loop variable

Here are other interesting stuff you can do with the $loop variable when looping:

PropertyDescription
$loop->indexThe index of the current loop iteration (starts at 0).
$loop->iterationThe current loop iteration (starts at 1).
$loop->remainingThe iterations remaining in the loop.
$loop->countThe total number of items in the array being iterated.
$loop->firstWhether this is the first iteration through the loop.
$loop->lastWhether this is the last iteration through the loop.
$loop->evenWhether this is an even iteration through the loop.
$loop->oddWhether this is an odd iteration through the loop.
$loop->depthThe nesting level of the current loop.
$loop->parentWhen in a nested loop, the parent's loop variable.

You can create your own personal CMS with ease by harnessing some of laravel helpful helper functions.

Blade also allows you to define comments in your views. However, unlike HTML comments, Blade comments are not included in the HTML returned by your application:

{{-- This is a comment but will not be present in the rendered HTML --}}

Loading Stylesheets

You can easily load style-sheet with ease in the blade view, I recently discovered a CSS framework tittle Halfmoon, it is a framework with a built-in dark mode and full customizability using CSS variables; which is great for building dashboard, tools, and whatever.

You can get it here: gethalfmoon if you are interested. Download, and extract it.

  • Include halfmoon-variables.min.css inside public\css folder, create a CSS folder if none is there
  • Include halfmoon.min.js inside public\js folder, create a JS folder if none is there

If you are not using halfmoon, change it to whatever you want, and place it in the above folder. To load the stylesheet and the javascript, we open up the app.blade.php file in our layout folder, and I include the CSS within the head tag and js at the end of the <body> tag:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="stylesheet" href="{{ URL::asset('css/halfmoon-variables.min.css') }}">
    <title>{{config('app.name', 'SimpleApp')}}</title>
</head>
<body class="antialiased">
    @yield('content')

    <script src="./js/halfmoon.min.js"></script>
</body>
</html>

By just doing this, I got the following styles out of the box:

Halfmoon application

Cool, if you ask me ;)

If you are not using a CSS framework, you can place your files in the designated folder I linked to above, and edit things away.

To spice things up a little I wrapped the content of my app layout with a container class:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="stylesheet" href="./css/halfmoon-variables.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
    <title>{{config('app.name', 'SimpleApp')}}</title>
</head>
<body class="antialiased">

<div class="page-wrapper with-navbar">
    <div class="content-wrapper"> {{-- Content Wrapper Starts --}}
        <div class="container">
            @yield('content')
        </div>
    </div> {{-- Content Wrapper Ends --}}
</div> {{-- page-wrapper Ends --}}

    <script src="./js/halfmoon.min.js"></script>
</body>
</html>

Which then gives me:

wrap the content with a container

This pushes it to the side a little compared to the first one. So, let's add a navbar to make it look classical. I also added font-awesome icon resources, you'll see the full effect below.

Still, in our layout, I'll create navigation.blade.php:

Navigation.blade layout

So, to include it in our app layout you use the include directive like so:

...
<body class="antialiased">

<div class="page-wrapper with-navbar">

@include('layouts.navigation')

<div class="content-wrapper"> {{-- Content Wrapper Starts --}}
<div class="container">
@yield('content')
</div>
</div> {{-- Content Wrapper Ends --}}
</div> {{-- page-wrapper Ends --}}

<script src="./js/halfmoon.min.js"></script>
</body>
...

Whatever we place in the navigation.blade.php, would automatically be included in the app.blade.php, easy enough right.

In the navigation.blade.php, I'll use the following:

<!-- Navbar (immediate child of the page wrapper) -->
<nav class="navbar">
    <!-- Navbar content (with toggle sidebar button) -->
    <div class="navbar-content">
        <button class="btn btn-action" type="button">
            <i class="fa fa-bars" aria-hidden="true"></i>
            <span class="sr-only">Toggle sidebar</span> <!-- sr-only = show only on screen readers -->
        </button>
    </div>
    <!-- Navbar brand -->
    <a href="/" class="navbar-brand">
        <img src="https://cdn.devsrealm.com/serve_file_path_987654321/d5c3f610914fc1ab071adb48ee747519b07e0f40395f1ab2872b9b4b7048e436?render"  alt="SimpelApp">
    </a>
    <!-- Navbar nav -->
    <ul class="navbar-nav d-none d-md-flex"> <!-- d-none = display: none, d-md-flex = display: flex on medium screens and up (width > 768px) -->
        <li class="nav-item active">
            <a href="/dashboard" class="nav-link font-size-16">Dashboard</a>
        </li>
        <li class="nav-item">
            <a href="/about" class="nav-link font-size-16">About</a>
        </li>
    </ul>
    <!-- Inline form with input group -->
    <form class="form-inline d-none d-md-flex ml-auto" action="..." method="..."> <!-- ml-auto = margin-left: auto -->
        <div class="input-group">
            <input type="text" class="form-control" placeholder="Search" required="required">
            <div class="input-group-append">
                <button class="btn" type="submit">
                    <i class="fa fa-search" aria-hidden="true"></i>
                    <span class="sr-only">Search</span> <!-- sr-only = show only on screen readers -->
                </button>
            </div>
        </div>
    </form>
    <!-- Navbar content with sign up button -->
    <div class="navbar-content d-none d-md-flex">
        <button class="btn btn-primary" type="button">Sign up</button>
    </div>
    <!-- Navbar content (with the dropdown menu) -->
    <div class="navbar-content d-md-none ml-auto"> <!-- d-md-none = display: none on medium screens and up (width > 768px), ml-auto = margin-left: auto -->
        <div class="dropdown with-arrow">
            <button class="btn" data-toggle="dropdown" type="button" id="navbar-dropdown-toggle-btn-1">
                Menu
                <i class="fa fa-angle-down" aria-hidden="true"></i>
            </button>
            <div class="dropdown-menu dropdown-menu-right w-200" aria-labelledby="navbar-dropdown-toggle-btn-1"> <!-- w-200 = width: 20rem (200px) -->
                <a href="#" class="dropdown-item">Docs</a>
                <a href="#" class="dropdown-item">Products</a>
                <div class="dropdown-divider"></div>
                <div class="dropdown-content">
                    <form class="form-inline ml-auto" action="..." method="..."> <!-- ml-auto = margin-left: auto -->
                        <div class="input-group">
                            <input type="text" class="form-control" placeholder="Search" required="required">
                            <div class="input-group-append">
                                <button class="btn" type="submit">
                                    <i class="fa fa-search" aria-hidden="true"></i>
                                    <span class="sr-only">Search</span> <!-- sr-only = show only on screen readers -->
                                </button>
                            </div>
                        </div>
                    </form>
                    <!-- Navbar content with sign up button -->
                    <div class="navbar-content">
                        <button class="btn btn-primary" type="button">Sign up</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</nav>

Haha, I know the markup is really ugly, but that is not the point, the point is how I include the relevant link to the rest of the pages, e.g. The brand logo links to the homepage when clicked:

<a href="/" class="navbar-brand">
        <img src="https://cdn.devsrealm.com/serve_file_path_987654321/d5c3f610914fc1ab071adb48ee747519b07e0f40395f1ab2872b9b4b7048e436?render"  alt="SimpelApp">
</a>

slash(/) means homepage. To go to dashboard, I do /dashboard:

<li class="nav-item active">
    <a href="/dashboard" class="nav-link font-size-16">Dashboard</a>
</li>

You get the idea. Here is the final output:

Added Navigational styles

Thanks to halfmoon, haha.

I'll finalize this tutorial by beautifying the main page a little:

In frontpage.blade.php I'll add:

@extends('layouts.app')

@section('content')
    <h1> {{$data['title']}}</h1>
    <p>{{$data ['description']}}</p>

    <div class="d-flex">
    @if(count($data['post']) > 0 )
            @foreach($data['post'] as $item)
                <div class="w-600 mw-full d-flex align-items-center"> <!-- w-400 = width: 40rem (400px), mw-full = max-width: 100% -->
                    <div class="card p-0 "> <!-- p-0 = padding: 0 -->
                        <img src="https://www.gethalfmoon.com/static/site/img/image-1.png" class="img-fluid rounded-top" alt="..."> <!-- rounded-top = rounded corners on the top -->
                        <!-- Nested content container inside card -->
                        <div class="content">
                            <h2 class="content-title">
                                {{$item}}
                            </h2>
                            <p class="text-muted">
                                This is an Excerpt from {{$item}}
                            </p>
                            <div class="text-right"> <!-- text-right = text-align: right -->
                                <a href="#" class="btn">Read more</a>
                            </div>
                        </div>
                    </div>
                </div>
            @endforeach
    @else
        <p>No Post</p>
    @endif
    </div>

@endsection

This isn't trivial at all, it's just an halfmoon container plus a couple of flexbox, and that gives me:

Final simpleapp for now

I'll conclude the guide here for now, in later guide, we would add more functions.

Related Post(s)

  • Laravel - Basic Routing & Controllers

    If you haven't read my guide on Creating a Tiny PHP MVC Framework From Scratch then you should do that immediately, the concept of the guide applies to the way things work in Laravel behind the scen

  • Laravel - Edit and Delete Data (+ User Authentication)

    In this guide, you'll learn how to edit and delete post data in Laravel, before proceeding you should read the previous guides as this guide would be the continuation, here are the previous guides:

  • Guide To Laravel - Model and Database Migrations

    I don't know if you have read my guide on Creating a Tiny PHP MVC Framework From Scratch where we create a tiny MVC framework in the hope of understanding the concepts of how major frameworks imple

  • Best Way To Implement a Non-Breaking Friendly URL In PHP or Laravel or Any Language

    I was working on the link structure of my new Laravel app, and out of the blue I said: "What would happen if a user changes the slug of a post?" First Attempt - 301 Redirection The first solution I t