Laravel 11 for Beginners: Blade and Breeze

Arlind Musliu Portrait
Arlind Musliu

January 13, 2024 · 5 min read

Laravel Blogpost Image

2024 UPDATE - LARAVEL 11

We're excited to announce that we have updated all of our blog post examples to reflect the new Laravel 11 version! Our previous examples were based on Laravel 10, but with the release of Laravel 11, we wanted to ensure that our readers have access to the most up-to-date information and examples.

Laravel Breeze and Blade

When combined with Laravel Breeze, you get a simple yet effective starting point for authentication views and a clear example of Blade in action. Let's dive into the basics of Laravel Blade and how it's used within Breeze to create a seamless user experience.

What is Laravel Blade?

Blade is Laravel's built-in templating engine, designed to provide developers with a convenient way to write HTML templates while also allowing the use of PHP. It offers an expressive syntax for extending layouts, displaying data, and constructing reusable components.

Blade Syntax and Directives

Blade templates use a mix of HTML and Blade directives – special tokens that Blade recognizes and converts into PHP code. Here are some of the fundamental directives:

  • @extends: Indicates that the template extends a layout.

  • @section and @endsection: Define a section of content.

  • @yield: Used in layouts to display the content of a section.

  • @include: Includes another Blade file within the template.

  • {{ }}: Echoes data, automatically escaping HTML entities for security.

  • @if, @elseif, @else, @endif: Control structures for conditional statements.

  • @foreach, @endforeach: Loops through a data array.

Blade Layouts

Blade allows you to create a master layout that serves as a template for your application's look. Here's a simple example of a layout:

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Head Contents -->
</head>
<body>
    <header>
        <!-- Header Content -->
    </header>

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

    <footer>
        <!-- Footer Content -->
    </footer>
</body>
</html>

Using @yield('content'), you define a placeholder for where the content of your child views will be injected.

Blade Templates with Breeze

Laravel Breeze simplifies authentication by providing a minimal and clean starting point, including Blade views for login, registration, password reset, and email verification. Let's see how Blade and Breeze work together.

After installing Breeze, you'll notice that the views are stored in resources/views/auth. Here's an example of how a login form might look using Blade:

@extends('layouts.app')

@section('content')
<div>
    <form method="POST" action="{{ route('login') }}">
        @csrf

        <!-- Email Input -->
        <div>
            <label for="email">Email</label>
            <input id="email" type="email" name="email" required autocomplete="email" autofocus>
        </div>

        <!-- Password Input -->
        <div>
            <label for="password">Password</label>
            <input id="password" type="password" name="password" required>
        </div>

        <!-- Submit Button -->
        <div>
            <button type="submit">Login</button>
        </div>
    </form>
</div>
@endsection

In this template, we're extending the app layout and defining the content section. We use Blade's @csrf directive to include a CSRF token field in the form for security, and {{ route('login') }} to generate the URL to the login route.

Blade Components

Blade also supports components, which are reusable pieces of the user interface. Breeze utilizes components to keep views DRY (Don't Repeat Yourself). For example, you might have a component for an input field:

<input {{ $attributes }}>

You can use this component in your forms like so:

<x-input id="email" type="email" name="email" required />

Blog Post Views

Let's create some of the views for our blog app example.

View for Displaying All Posts

First, we need to create a new folder named posts inside of the resources/views folder. Then we create a new file index.blade.php.

@extends('layouts.app')

@section('title', 'All Blog Posts')

@section('content')
    <h1>All Blog Posts</h1>
    <ul>
        @forelse ($posts as $post)
            <li>
                <a href="{{ route('posts.show', $post) }}">{{ $post->title }}</a>
                <p>{{ $post->created_at->toFormattedDateString() }}</p>
            </li>
        @empty
            <li>No posts available.</li>
        @endforelse
    </ul>
@endsection

In this view, we're extending a main layout and then defining a section for the content. We loop through all the posts using @forelse, which also handles the case where there are no posts. Each post title is a link to the single post view.

View for Displaying a Single Post

We create another file in the same directory, show.blade.php:

@extends('layouts.app')

@section('title', $post->title)

@section('content')
    <article>
        <h1>{{ $post->title }}</h1>
        <p>Published {{ $post->created_at->diffForHumans() }}</p>
        <div>{{ $post->content }}</div>

        {{-- Check if there are any tags --}}
        @if($post->tags->isNotEmpty())
        <p>Tags:</p>
        <ul>
            @foreach($post->tags as $tag)
                <li>{{ $tag->name }}</li>
            @endforeach
        </ul>
        @endif
    </article>

    <a href="{{ route('posts.index') }}">Back to all posts</a>
@endsection

In the single post view, we show the post title, publication date, and content. The diffForHumans() will display the date as 5 days ago or 2 years ago depending on the time difference between the current moment and the post date.

View for Creating a New Post

We create another file create.blade.php for displaying the form to create a new post.

@extends('layouts.app')

@section('title', 'Create New Post')

@section('content')
    <h1>Create New Post</h1>

    <form method="POST" action="{{ route('posts.store') }}">
        @csrf

        <div>
            <label for="title">Title</label>
            <input type="text" id="title" name="title" value="{{ old('title') }}" required>
            @error('title')
                <p>{{ $message }}</p>
            @enderror
        </div>

        <div>
            <label for="content">Content</label>
            <textarea id="content" name="content" required>{{ old('content') }}</textarea>
            @error('content')
                <p>{{ $message }}</p>
            @enderror
        </div>

 		 <div>
            <label for="tags">Tags</label>
            <select name="tags[]" id="tags" multiple>
                @foreach (\App\Models\Tag::all() as $tag)
                    <option value="{{ $tag->id }}" {{ in_array($tag->id, old('tags', [])) ? 'selected' : '' }}>{{ $tag->name }}</option>
                @endforeach
            </select>
        </div>

        <div>
            <button type="submit">Publish Post</button>
        </div>
    </form>
@endsection

In the create post view, we have a form with fields for the post title and content. We use the @csrf directive to protect against cross-site request forgery. The @error directive is used to display validation errors for each field. The old() function is used to repopulate the fields with the previously entered data in case of a validation error.

Also, change function of the store in PostController:

<? php

// Store a newly created blog post
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
        ]);

        $tags = $request->validate([
            'tags' => 'required|array',
            'tags.*' => 'exists:tags,id'
        ]);

        // the new post belongs to the authenticated user
        $post = auth()->user()->posts()->create($validatedData);

        // we add tags to the post by using the sync() method
        $post->tags()->sync($tags['tags']);

        // PostPublished::dispatch($post);

        return redirect()->route('posts.show', $post);
    }

Layout File

We will create a new folder in the views directory named as layouts. Then we will create a new file inside of that directory app.blade.php.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title') - My Blog</title>
</head>
<body>
    <header>
        <nav>
            <a href="{{ url('/') }}">Home</a>
            <a href="{{ route('posts.index') }}">Blog</a>
            <a href="{{ route('posts.create') }}">Write a Post</a>
        </nav>
    </header>

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

    <footer>
        <p>© My Blog</p>
    </footer>
</body>
</html>

The layout file provides a basic structure for the HTML pages, including a header with navigation links, a main content area where the individual views will be rendered, and a footer. The @yield directive is used to insert the content from the child views into the layout.

These Blade views provide the basic functionality for a blog to display posts and allow users to create new ones. You can customize the HTML and styling to match your blog's design and requirements.

Conclusion

Blade is a powerful templating engine that makes writing and managing your web apps HTML content a breeze, especially when paired with Laravel Breeze for authentication. By mastering Blade's directives, layout system, and components, you can create efficient and elegant views for your Laravel app.

Upcoming Articles in the Series

  1. Laravel for Beginners: Service Providers

  2. Laravel for Beginners: Form Requests and Validation Rules

  3. Laravel for Beginners: Task Scheduling for Automation


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let's get in touch.

Lucky Media is proud to be recognized as a Top Laravel Development Agency

Arlind Musliu Portrait
Arlind Musliu

Cofounder and CFO of Lucky Media

Technologies:

Laravel
Heading Pattern

Related Posts

Stay up to date

Be updated with all news, products and tips we share!