← Other topics

Laravel In a Nutshell - Views & Blade (#4)

Series contents:

Video Notes

Views recap

Here’s what we've learned about Views so far in this series:

  • When we want to produce HTML content in Laravel, we organize that content in Views.
  • View files are put in the /resources/views directory.
  • Views can be returned using Laravel’s view method, as we saw first in our routes file and later in our showProductsByCategory method within the ProductsController (code below).
  • when returning a View, you can pass data to that View by chaining on the with method.
public function showProductsByCategory($category) {

    $productsByCategory = []; // Array content redacted for brevity

    $products = $productsByCategory[$category];
    
    # How to return the view located at `/resources/views/products.blade.php`
    return view('products')
        ->with('products', $products)
        ->with('category', $category);
}

Building on this information, let’s dig deeper into Views...

Blade templating language

When we create view files, they have a .blade.php extension. Blade is a templating language that you’ll use in your View files along with regular HTML. Blade aims to simplify many of the aspects of constructing your View output.

For example, to output data such as PHP variables with Blade, you use Blade’s double curly bracket syntax:

{{ $category }}

Applying this, we can update our the heading in our products page so that instead of using a PHP echo:

Before:

<h1>Products: <?php echo $category?></h1>

After:

<h1>Products: {{ $category }}</h1>

Blade directives

In addition to outputting content, Blade provides terse shortcuts - referred to as Blade directives - for common PHP display-related logic such as loops and conditionals.

For example, we can replace the PHP foreach loop to iterate through our products...

<ul>
<?php foreach($products as $product) { ?>
    <li><?php echo $product ?></li>
<?php } ?>
</ul>

...with Blade’s syntax for a foreach loop:

<ul>
    @foreach($products as $product)
        <li>{{ $product }}</li>
    @endforeach
</ul>

Note how Blade directives (@foreach, @endforeach) start with a @ sign.

Here’s an example of a conditional using Blade directives:

@if (count($products) === 0)
    No products found
@else
    Code here to display products...
@endif

Take a moment to skim through the Blade docs and observe there are a lot of directives available to help you construct your pages.

Layouts

Another advantage of Blade is it allows you to define layouts that can be shared amongst many pages.

For example, let’s create a “main” layout for our application which all the pages will inherit from. To do this, place the following code in a new file at: /resources/views/main.blade.php:

<!doctype html>
<html lang='en'>
<head>
    <title>My Store</title>
    <meta charset='utf-8'>
    <link href=data:, rel=icon>
</head>
<body>

<header>
    <h1>My Store</h1>
</header>

<nav>
    <ul>
        <li><a href='/products'>Products</a></li>
        <!-- If we link to /products without a specifying category, this will produce an error. I’ll address that later in this guide. -->
        <li><a href='/contact'>Contact</a></li>
        <!-- A contact page doesn’t yet exist, but I’m including it here just as a demonstration of links you might have in a nav bar. For practice, try your hand at creating a contact page. -->
    </ul>
</nav>

<main>
    @yield('page-content')
</main>

</html>

Now we can update our individual page views to inherit this layout. For example, here’s our new products.blade.php:

@extends('main')

@section('page-content')
    <h1>Products: <?php echo $category?></h1>

    <ul>
    <?php foreach($products as $product) { ?>
        <li><?php echo $product ?></li>
    <?php } ?>
    </ul>
@endsection

Note how the @extends directive is used to indicate this page should inherit the layout main (which will resolve to the file /resources/views/main.blade.php).

Also observe how the Blade directives @section and @end-section are used to define a section of content called page-content that will be merged into the main layout where we invoke @yield('page-content') directive is located.

This same pattern can be followed for our welcome view:

@extends('main')

@section('page-content')
<p>Welcome to My Store</p>
@endsection

The end result is that both the welcome and products page on our site share the same overarching page template.

The above technique is referred to as Template Inheritance, but it’s not the only way you can build layouts in Blade. You could also construct layouts using Components, which you can read more about here...

Before you go...

Before we proceed to our next topic, form processing, let’s rewind and fine-tune our products page. As it’s written, you can only view products if you specify a category (e.g. /products/health). If you visit just /products, you will see a 404 not found error.

To address this, we’ll first update our /products/{category} route, adding a ? mark to make the category portion of the URL optional:

Route::get('/products/{category?}', [ProductsController::class, 'showProductsByCategory']);

Then we’ll add logic to our showProductsByCategory to handle a situation were a category is not yet specified:

public function showProductsByCategory($category = null)
{
    $productsByCategory = [
        'health' => [
            'Band-Aids',
            'Johnson’s Baby Powder',
            'Tylenol'
        ],
        'tech' => [
            'GoPro Action Camera',
            'FitBit Fitness Watch',
            'Nintendo Switch'
        ],
        'books' => [
            'The Martian',
            'The Great Gatsby',
            'Joy Luck Club'
        ]
    ];

    # Extract just the categories from our data so we can display links to each category in the view
    $categories = array_keys($productsByCategory);

    # products will be set to an array of products if $category is set, otherwise it will be set to null
    $products = $productsByCategory[$category] ?? null;

    return view('products')
        ->with('products', $products)
        ->with('category', $category)
        ->with('categories', $categories);
}

And adapt our view to handle both situations where a category is and is not specified:

@extends('main')

@section('page-content')
    <h1>Products</h1>

    @if ($category)
        <p>Filtering by the category:
            <strong>{{ $category }}</strong>
        </p>

        @if ($products)
            <ul>
                @foreach ($products as $product)
                    <li>{{ $product }}</li>
                @endforeach
            </ul>
        @else
            <p>No products found</p>
        @endif
    @endif

    <p>Choose a category:</p>
    <ul>
        @foreach ($categories as $category)
            <li><a href='/products/{{ $category }}'>{{ $category }}</a></li>
        @endforeach
    </ul>

    <a href='/'>Home</a>
@endsection
If this info helped you out, toss a coin in the tip jar?
← Other topics