Artigo 43 — Blade — Templates no Laravel
Módulo 7 · Semana 43 · Nível: Intermediário
Introdução
Blade é o motor de templates do Laravel. Ele compila templates para PHP puro e os armazena em cache, então não há penalidade de performance. A sintaxe é limpa, expressiva, e evita que você misture lógica de negócio com apresentação — um problema clássico do PHP puro com HTML.
Blade não é apenas uma forma de escrever <?php echo ?> mais curto. Oferece herança de layouts, componentes reutilizáveis, slots, stacks, e diretivas que tornam templates complexos gerenciáveis.
Sintaxe básica
{{-- Comentário Blade — não aparece no HTML final --}}
{{-- Exibir variável com escape automático (equivalente a htmlspecialchars) --}}
{{ $produto->nome }}
{{-- Exibir HTML sem escape — use com cuidado --}}
{!! $produto->descricao_html !!}
{{-- Expressões PHP --}}
{{ strtoupper($produto->nome) }}
{{ $produto->preco_formatado }}
{{ now()->format('d/m/Y') }}
Estruturas de controle
{{-- Condicionais --}}
@if ($produto->estoque > 0)
<span class="badge verde">Em estoque</span>
@elseif ($produto->estoque === 0)
<span class="badge vermelho">Esgotado</span>
@else
<span class="badge cinza">Indisponível</span>
@endif
{{-- unless = if negado --}}
@unless ($usuario->isAdmin())
<p>Acesso restrito a administradores.</p>
@endunless
{{-- isset e empty --}}
@isset($produto->descricao)
<p>{{ $produto->descricao }}</p>
@endisset
@empty($produtos)
<p>Nenhum produto encontrado.</p>
@endempty
{{-- Loops --}}
@foreach ($produtos as $produto)
<div class="produto">
<h3>{{ $produto->nome }}</h3>
<p>R$ {{ number_format($produto->preco, 2, ',', '.') }}</p>
</div>
@endforeach
{{-- $loop — variável especial dentro de foreach --}}
@foreach ($produtos as $produto)
<div class="{{ $loop->even ? 'par' : 'impar' }}">
{{ $loop->iteration }}/{{ $loop->count }}: {{ $produto->nome }}
@if ($loop->first) <span>← primeiro</span> @endif
@if ($loop->last) <span>← último</span> @endif
</div>
@endforeach
{{-- forelse: bloco para quando a coleção for vazia --}}
@forelse ($produtos as $produto)
<p>{{ $produto->nome }}</p>
@empty
<p>Nenhum produto cadastrado.</p>
@endforelse
{{-- for e while --}}
@for ($i = 1; $i <= 5; $i++)
<p>Item {{ $i }}</p>
@endfor
Herança de layouts
O padrão mais comum no Blade é definir um layout principal que as views estendem:
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'Minha Loja')</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
@stack('styles')
</head>
<body>
<header>
<nav>
<a href="{{ route('home') }}">Início</a>
<a href="{{ route('produtos.index') }}">Produtos</a>
@auth
<a href="{{ route('perfil') }}">{{ auth()->user()->nome }}</a>
<a href="{{ route('logout') }}">Sair</a>
@endauth
@guest
<a href="{{ route('login') }}">Entrar</a>
@endguest
</nav>
</header>
<main>
@if (session('sucesso'))
<div class="alerta sucesso">{{ session('sucesso') }}</div>
@endif
@if (session('erro'))
<div class="alerta erro">{{ session('erro') }}</div>
@endif
@yield('content')
</main>
<footer>
<p>© {{ date('Y') }} Minha Loja</p>
</footer>
<script src="{{ asset('js/app.js') }}"></script>
@stack('scripts')
</body>
</html>
{{-- resources/views/produtos/index.blade.php --}}
@extends('layouts.app')
@section('title', 'Produtos | Minha Loja')
@push('styles')
<link rel="stylesheet" href="{{ asset('css/produtos.css') }}">
@endpush
@section('content')
<h1>Produtos</h1>
<div class="grade-produtos">
@forelse ($produtos as $produto)
<div class="card-produto">
<h3>
<a href="{{ route('produtos.show', $produto) }}">
{{ $produto->nome }}
</a>
</h3>
<p>{{ $produto->preco_formatado }}</p>
<p>{{ $produto->categoria->nome }}</p>
</div>
@empty
<p>Nenhum produto disponível.</p>
@endforelse
</div>
{{-- Paginação --}}
{{ $produtos->links() }}
@endsection
@push('scripts')
<script src="{{ asset('js/produtos.js') }}"></script>
@endpush
Componentes
Componentes são o recurso mais poderoso do Blade moderno — permitem criar elementos reutilizáveis com lógica e template encapsulados:
php artisan make:component Alerta
php artisan make:component Produto/Card
<?php
// app/View/Components/Alerta.php
namespace App\View\Components;
use Illuminate\View\Component;
class Alerta extends Component
{
public function __construct(
public readonly string $tipo = 'info', // info, sucesso, erro, aviso
public readonly bool $descartavel = false,
) {}
public function classes(): string
{
return match($this->tipo) {
'sucesso' => 'alerta alerta-sucesso',
'erro' => 'alerta alerta-erro',
'aviso' => 'alerta alerta-aviso',
default => 'alerta alerta-info',
};
}
public function render()
{
return view('components.alerta');
}
}
{{-- resources/views/components/alerta.blade.php --}}
<div {{ $attributes->class([$classes()]) }}>
{{ $slot }}
@if ($descartavel)
<button onclick="this.parentElement.remove()">×</button>
@endif
</div>
{{-- Usando o componente --}}
<x-alerta tipo="sucesso">
Produto criado com sucesso!
</x-alerta>
<x-alerta tipo="erro" :descartavel="true">
Ocorreu um erro ao processar o pagamento.
</x-alerta>
{{-- Componente com múltiplos slots --}}
<x-card>
<x-slot name="header">
<h3>{{ $produto->nome }}</h3>
</x-slot>
<p>{{ $produto->descricao }}</p>
<x-slot name="footer">
<a href="{{ route('produtos.show', $produto) }}">Ver mais</a>
</x-slot>
</x-card>
Formulários e erros de validação
{{-- Formulário de criação de produto --}}
<form method="POST" action="{{ route('produtos.store') }}" enctype="multipart/form-data">
@csrf {{-- Token CSRF automático --}}
<div>
<label for="nome">Nome</label>
<input type="text"
id="nome"
name="nome"
value="{{ old('nome') }}"
class="{{ $errors->has('nome') ? 'erro' : '' }}">
@error('nome')
<span class="mensagem-erro">{{ $message }}</span>
@enderror
</div>
<div>
<label for="preco">Preço</label>
<input type="number"
id="preco"
name="preco"
step="0.01"
value="{{ old('preco') }}">
@error('preco')
<span class="mensagem-erro">{{ $message }}</span>
@enderror
</div>
<div>
<label for="categoria_id">Categoria</label>
<select id="categoria_id" name="categoria_id">
<option value="">Selecione...</option>
@foreach ($categorias as $categoria)
<option value="{{ $categoria->id }}"
{{ old('categoria_id') == $categoria->id ? 'selected' : '' }}>
{{ $categoria->nome }}
</option>
@endforeach
</select>
@error('categoria_id')
<span class="mensagem-erro">{{ $message }}</span>
@enderror
</div>
{{-- Para edição: método PUT --}}
{{-- @method('PUT') --}}
<button type="submit">Salvar</button>
</form>
Resumo
| Recurso | Sintaxe |
|---|---|
| Exibir variável | {{ $var }} (com escape) / {!! $var !!} (sem escape) |
| Condicional | @if / @elseif / @else / @endif |
| Loop | @foreach / @forelse / @for |
| Layout | @extends / @section / @yield |
| Stack | @stack / @push |
| Componente | <x-nome-componente :prop="$valor"> |
| CSRF | @csrf — obrigatório em todo formulário POST |
| Erros | @error('campo') {{ $message }} @enderror |
| Auth | @auth / @guest / @can |
Exercício da semana
-
Crie um layout principal com header, footer e área de flash messages. Crie views para listar, exibir, criar e editar produtos, todas estendendo o layout.
-
Construa um componente
<x-produto-card>que recebe um$produtoe exibe nome, preço, categoria e estoque. Use o componente na listagem. -
Implemente um componente
<x-paginacao>que envolve$colecao->links()com estilos customizados. -
Desafio: crie um componente
<x-data-table>genérico que recebe$colunas(array de configuração) e$dados(coleção) e renderiza uma tabela completa com ordenação por coluna via query string.
Referências
- Blade Templates — Documentação: https://laravel.com/docs/blade
- Blade Components: https://laravel.com/docs/blade#components
- Laravel Collective Forms (helper adicional): https://laravelcollective.com/