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:
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:
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:
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
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:'
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:
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:
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:
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:
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:
Here are other interesting stuff you can do with the $loop variable when looping:
Property | Description |
$loop->index | The index of the current loop iteration (starts at 0). |
$loop->iteration | The current loop iteration (starts at 1). |
$loop->remaining | The iterations remaining in the loop. |
$loop->count | The total number of items in the array being iterated. |
$loop->first | Whether this is the first iteration through the loop. |
$loop->last | Whether this is the last iteration through the loop. |
$loop->even | Whether this is an even iteration through the loop. |
$loop->odd | Whether this is an odd iteration through the loop. |
$loop->depth | The nesting level of the current loop. |
$loop->parent | When 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:
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:
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:
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:
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:
I'll conclude the guide here for now, in later guide, we would add more functions.