404 Not Found

404 Not Found


nginx

Views do Laravel e o Motor de Template Blade

Blade é o "motor de layout" do Laravel — é aqui que os dados são transformados nas páginas que os usuários veem, e onde layouts, componentes e herança eliminam código de página repetitivo.

1. O Que Você Vai Aprender


2. Uma História Real de um Desenvolvedor Front-End

(1) Dor: Ter que escrever a barra de navegação repetidamente em cada página

Charlie escreveu 10 páginas para o ShopMetrics, copiando manualmente 60 linhas de HTML da barra de navegação em cada uma. Quando Bob pediu para mover o link "Preços" da barra de navegação para um menu suspenso, Charlie teve que editar todos os 10 arquivos um por um — mas quando chegou ao sétimo arquivo, perdeu uma classe, causando inconsistência nos estilos da barra de navegação no site ao vivo, o que gerou três reclamações da Alice.

(2) Solução para o Layout Blade

O Blade usa @extends para herdar o layout e @section para preencher o conteúdo; a barra de navegação é definida apenas uma vez e é sincronizada automaticamente em todo o site.

PHP
// resources/views/layouts/app.blade.php — Definir layout uma vez
<body>
    <nav>...</nav>  <!-- Navegação aparece em cada página -->
    @yield('content')  <!-- Cada página preenche este slot -->
</body>

// resources/views/shops/index.blade.php — Estender e preencher
@extends('layouts.app')
@section('content')
    <h1>Todas as Lojas</h1>
@endsection

(3) Resultado

Depois que Charlie implementou o layout Blade, a barra de navegação exigiu manutenção de apenas um único arquivo. O pedido de mudança do Bob passou de "modificar 10 arquivos" para "modificar 1 arquivo", com zero omissões.


3. Herança de Layout Blade

(1) Definindo o Layout

HTML
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>@yield('title', 'ShopMetrics')</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
    @include('partials.navbar')
    <main class="container mx-auto">
        @yield('content')
    </main>
    @include('partials.footer')
</body>
</html>

(2) Herdando Layouts

HTML
<!-- resources/views/shops/index.blade.php -->
@extends('layouts.app')

@section('title', 'Todas as Lojas')

@section('content')
    <div class="grid grid-cols-3 gap-4">
        @foreach($shops as $shop)
            <div class="card">{{ $shop->name }}</div>
        @endforeach
    </div>
@endsection
Comando Função Analogia
@extends Especificar o layout a herdar Selecionar um modelo
@section Definir blocos de conteúdo Preencher os espaços do modelo
@yield Emitir bloco de conteúdo Espaço reservado no modelo
@include Importar Subvisão Inserir Snippet Público

(1) ▶ Exemplo: Layout de Duas Colunas do ShopMetrics

HTML
<!-- resources/views/layouts/dashboard.blade.php -->
@extends('layouts.app')

@section('content')
    <div class="flex">
        @include('partials.sidebar')
        <div class="flex-1 p-6">
            @yield('dashboard-content')
        </div>
    </div>
@endsection

<!-- resources/views/dashboard/analytics.blade.php -->
@extends('layouts.dashboard')

@section('title', 'Painel de Análise')

@section('dashboard-content')
    <h1>Visão Geral de Análise</h1>
    <div id="chart"></div>
@endsection

Saída:

TEXT
// Execução Bem-Sucedida

4. Estruturas de Controle

(1) Declarações Condicionais

PHP
@if($shop->is_active)
    <span class="badge-green">Ativo</span>
@elseif($shop->is_suspended)
    <span class="badge-yellow">Suspenso</span>
@else
    <span class="badge-gray">Inativo</span>
@endif

@auth
    <a href="{{ route('dashboard') }}">Painel</a>
@endauth

@guest
    <a href="{{ route('login') }}">Login</a>
@endguest

(2) Declarações de Loop

PHP
@foreach($shops as $shop)
    <tr>
        <td>{{ $shop->name }}</td>
        <td>{{ $shop->revenue }}</td>
    </tr>
    @if($loop->last)
        </tbody></table>
    @endif
@endforeach

@forelse($orders as $order)
    <li>{{ $order->total }}</li>
@empty
    <li>Nenhum pedido ainda.</li>
@endforelse
Variável do Loop Descrição
$loop->index Índice Atual (começando em 0)
$loop->iteration Rodada Atual (começando em 1)
$loop->first É o primeiro?
$loop->last É o último?
$loop->count Total

(1) ▶ Exemplo: Renderização Condicional no Painel do ShopMetrics

HTML
<!-- resources/views/dashboard/index.blade.php -->
@extends('layouts.dashboard')

@section('dashboard-content')
    @auth
        <h1>Bem-vindo, {{ auth()->user()->name }}!</h1>
    @endauth

    @if($shops->count() > 0)
        <div class="grid grid-cols-3 gap-4">
            @foreach($shops as $shop)
                <div class="card {{ $loop->first ? 'border-blue-500' : '' }}">
                    <h3>{{ $shop->name }}</h3>
                    <p>Receita: ${{ number_format($shop->revenue, 2) }}</p>
                </div>
            @endforeach
        </div>
    @else
        <p>Nenhuma loja ainda. <a href="{{ route('shops.create') }}">Crie uma!</a></p>
    @endif
@endsection

Saída:

TEXT
// Execução Bem-Sucedida

5. Componente Blade

(1) Componentes Anônimos

HTML
<!-- resources/views/components/shop-card.blade.php -->
@props(['shop', 'highlight' => false])

<div class="card {{ $highlight ? 'border-gold' : '' }}">
    <h3>{{ $shop->name }}</h3>
    <p>{{ $shop->description }}</p>
    {{ $slot }}
</div>

<!-- Uso -->
<x-shop-card :shop="$shop" :highlight="true">
    <p>Destaque desta semana!</p>
</x-shop-card>

(2) Componentes de Classe

BASH
php artisan make:component Alert
# Cria: app/View/Components/Alert.php
# E:     resources/views/components/alert.blade.php
PHP
// app/View/Components/Alert.php
class Alert extends Component
{
    public function __construct(
        public string $type = 'info',
        public ?string $message = null,
    ) {}

    public function render(): View
    {
        return view('components.alert');
    }
}
HTML
<!-- resources/views/components/alert.blade.php -->
<div class="alert alert-{{ $type }}">
    {{ $message ?? $slot }}
</div>

<!-- Uso -->
<x-alert type="success" message="Loja criada!" />
<x-alert type="warning">Verifique suas configurações.</x-alert>
Dimensão Anônimo Classe
Arquivo Apenas Templates Blade Classes PHP + Templates Blade
Lógica Nenhuma/Mínima Pode incluir lógica de negócio
Props Declaração @props() Parâmetros do Construtor
Adequado para Componentes de exibição simples Componentes que exigem lógica

(1) ▶ Exemplo: Componente de Cartão de Estatísticas do ShopMetrics

HTML
<!-- resources/views/components/stat-card.blade.php -->
@props(['label', 'value', 'icon', 'trend' => null])

<div class="bg-white rounded-lg shadow p-6">
    <div class="flex items-center justify-between">
        <div>
            <p class="text-gray-500 text-sm">{{ $label }}</p>
            <p class="text-2xl font-bold">{{ $value }}</p>
            @if($trend)
                <p class="text-sm {{ $trend > 0 ? 'text-green-500' : 'text-red-500' }}">
                    {{ $trend > 0 ? '+' : '' }}{{ $trend }}%
                </p>
            @endif
        </div>
        <span class="text-3xl">{{ $icon }}</span>
    </div>
</div>

<!-- Uso no painel -->
<x-stat-card label="Receita Total" value="$12,450" icon="$" :trend="15.3" />
<x-stat-card label="Lojas Ativas" value="23" icon="#" :trend="-2.1" />

Saída:

TEXT
// Execução Bem-Sucedida

6. View Composer

Os View Composers injetam automaticamente dados compartilhados nas views, eliminando a necessidade de passá-los repetidamente em cada método do controller.

PHP
// app/Providers/AppServiceProvider.php
public function boot(): void
{
    // Compartilhar informações do tenant com todas as views do painel
    View::composer('dashboard.*', function ($view) {
        $view->with('tenant', Tenant::current());
    });

    // Compartilhar dados de navegação com views específicas
    View::composer(['layouts.app', 'shops.*'], NavigationComposer::class);
}
Tipo Quando Executar Propósito
View::composer() Cada vez que a view é renderizada Calcular dados dinamicamente
View::creator() Ao instanciar uma view Vincular dados mais cedo
View::share() Todas as Views Constantes Globais

(1) ▶ Exemplo: View Composer de Navegação do ShopMetrics

PHP
// app/View/Composers/NavigationComposer.php
class NavigationComposer
{
    public function compose(View $view): void
    {
        $view->with([
            'navShops' => auth()->check()
                ? auth()->user()->shops()->take(5)->get()
                : collect(),
            'navNotifications' => auth()->check()
                ? auth()->user()->unreadNotifications()->count()
                : 0,
        ]);
    }
}

// Registrar no AppServiceProvider
View::composer('layouts.app', NavigationComposer::class);

Saída:

TEXT
// Execução Bem-Sucedida

7. Assets de Front-End: Vite

O Laravel 11 usa o Vite por padrão para compilar assets de front-end.

(1) Instalar Tailwind CSS e Alpine.js

BASH
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
npm install alpinejs

(2) Configurar Vite

JAVASCRIPT
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: [
                'resources/css/app.css',
                'resources/js/app.js',
            ],
            refresh: true,
        }),
    ],
});

(3) Introdução ao Blade

HTML
<!-- No head do layout -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
Comando Função
npm run dev Iniciar o servidor de desenvolvimento Vite (hot reload)
npm run build Compilar Assets de Produção (Comprimido)

(1) ▶ Exemplo: Navegação do ShopMetrics com Interações Alpine.js

HTML
<!-- resources/views/partials/navbar.blade.php -->
<nav class="bg-white shadow" x-data="{ open: false }">
    <div class="container mx-auto px-4">
        <div class="flex justify-between h-16">
            <a href="{{ route('home') }}" class="font-bold text-xl">ShopMetrics</a>

            <div class="flex items-center">
                @auth
                    <div class="relative" @click="open = !open">
                        <button class="flex items-center">
                            {{ auth()->user()->name }}
                            @if($navNotifications > 0)
                                <span class="badge-red">{{ $navNotifications }}</span>
                            @endif
                        </button>
                        <div x-show="open" x-transition class="dropdown">
                            <a href="{{ route('dashboard') }}">Painel</a>
                            <a href="{{ route('shops.index') }}">Lojas</a>
                            <form action="{{ route('logout') }}" method="POST">
                                @csrf
                                <button type="submit">Sair</button>
                            </form>
                        </div>
                    </div>
                @else
                    <a href="{{ route('login') }}">Login</a>
                @endauth
            </div>
        </div>
    </div>
</nav>

Saída:

TEXT
// Execução Bem-Sucedida

8. Exemplo Abrangente: Página do Painel do ShopMetrics

HTML
<!-- ============================================
     Abrangente: Página do Painel do ShopMetrics
     Conteúdo: herança de layout, componentes, diretivas, Alpine.js
     ============================================ -->

<!-- resources/views/dashboard/index.blade.php -->
@extends('layouts.dashboard')

@section('title', 'Painel — ShopMetrics')

@section('dashboard-content')
    <div class="space-y-6" x-data="{ period: '7d' }">
        <!-- Seletor de Período -->
        <div class="flex gap-2">
            <button @click="period = '7d'"
                    :class="period === '7d' ? 'btn-primary' : 'btn-secondary'">7 Dias</button>
            <button @click="period = '30d'"
                    :class="period === '30d' ? 'btn-primary' : 'btn-secondary'">30 Dias</button>
            <button @click="period = '90d'"
                    :class="period === '90d' ? 'btn-primary' : 'btn-secondary'">90 Dias</button>
        </div>

        <!-- Cartões de Estatísticas -->
        <div class="grid grid-cols-4 gap-4">
            <x-stat-card label="Receita Total" value="${{ number_format($totalRevenue, 0) }}" icon="$" :trend="$revenueTrend" />
            <x-stat-card label="Pedidos" value="{{ number_format($orderCount) }}" icon="#" :trend="$orderTrend" />
            <x-stat-card label="Lojas Ativas" value="{{ $activeShops }}" icon="#" :trend="$shopTrend" />
            <x-stat-card label="Taxa de Conversão" value="{{ number_format($conversionRate, 1) }}%" icon="%" :trend="$conversionTrend" />
        </div>

        <!-- Pedidos Recentes -->
        <div class="bg-white rounded-lg shadow">
            <h2 class="p-4 border-b font-semibold">Pedidos Recentes</h2>
            @forelse($recentOrders as $order)
                <div class="p-4 border-b flex justify-between">
                    <span>{{ $order->product_name }}</span>
                    <span>${{ number_format($order->total, 2) }}</span>
                </div>
            @empty
                <p class="p-4 text-gray-500">Nenhum pedido neste período.</p>
            @endforelse
        </div>
    </div>
@endsection

❓ Perguntas Frequentes

P Qual é a diferença entre {{ }} e {!! !!} no Blade?
R {{ }} escapa HTML automaticamente (para prevenir XSS), enquanto {!! !!} emite HTML bruto sem escape. Use {!! !!} apenas quando o conteúdo é confiável, como saída de um editor de texto rico.
P Qual é a diferença entre @include e @component?
R @include é usado para simplesmente incluir snippets de view sem isolamento de prop; @component tem declarações de prop explícitas e um mecanismo de slot, tornando-o mais adequado para componentes UI reutilizáveis. Use @include para snippets simples e @component para componentes independentes.
P Qual é a diferença entre componentes Blade e componentes Vue?
R Componentes Blade são renderizados no servidor e geram HTML estático; componentes Vue são renderizados no cliente e suportam vinculação de dados reativa. Em projetos Laravel, Blade é tipicamente usado para estrutura de página, enquanto Alpine.js ou Vue é usado para interatividade.
P Qual é a diferença entre Vite e Mix?
R Vite é a ferramenta de build frontend padrão do Laravel 9 e posterior; usa um servidor de desenvolvimento ES Module, que inicia rapidamente e suporta hot reloading. Mix (Laravel Mix) é baseado no Webpack; é fácil de configurar mas mais lento. Use Vite para novos projetos.
P Como depurar variáveis no Blade?
R Use {{ dd($variable) }} para despejar e pausar a página diretamente; ou instale a extensão Laravel Debugbar para visualizar todas as variáveis, consultas SQL e uso de memória.
P Qual é a diferença entre como View Composers e Controllers passam dados?
R Controllers passam dados explicitamente usando view('xxx', ['key' => $value]), adequado para dados específicos daquela view; View Composers injetam automaticamente dados compartilhados, adequados para dados necessários por múltiplas views (como barra de navegação ou número de notificações).

📖 Resumo


📝 Exercícios

  1. Exercício Básico (⭐): Crie um arquivo de layout layouts/app.blade.php para o ShopMetrics que inclua uma barra de navegação e rodapé, depois crie uma página inicial home/index.blade.php que herde deste layout e preencha com conteúdo.

  2. Exercício Avançado (⭐⭐): Crie um componente Blade <x-alert> que suporte um type (success/warning/error) e conteúdo de slot, para ser usado em cenários do ShopMetrics onde a criação é bem-sucedida ou a validação falha.

  3. Desafio (⭐⭐⭐): Use Alpine.js para implementar um componente de barra lateral colapsável, integre-o em um layout de painel, suporte colapso automático em dispositivos móveis e salve o estado no localStorage.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%