Basics Blade
{{-- resources/views/articles/index.blade.php --}}
{{-- Afficher une variable (échappée HTML) --}}
{{ $article->title }}
{{-- HTML non échappé (dangereux avec contenu utilisateur !) --}}
{!! $article->body_html !!}
{{-- Commentaires Blade (non rendus dans le HTML) --}}
{{-- Ce commentaire n'apparaît pas dans la source --}}
{{-- PHP inline --}}
@php
$formattedDate = $article->created_at->format('d/m/Y');
$isAdmin = auth()->user()?->isAdmin();
@endphp
{{-- Escape les accolades pour JS --}}
@{{ variable_javascript }}
{{-- Raccourci for JSON --}}
<script>var data = @json($articles);</script>
Layouts
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'Mon Blog') — MonSite</title>
<link rel="stylesheet" href="/css/app.css">
@stack('styles')
</head>
<body>
@include('partials.navbar')
<main class="container mx-auto py-8">
@yield('content')
</main>
@include('partials.footer')
<script src="/js/app.js"></script>
@stack('scripts')
</body>
</html>
{{-- resources/views/articles/show.blade.php --}}
@extends('layouts.app')
@section('title', $article->title)
@section('content')
<article>
<h1>{{ $article->title }}</h1>
<p>Par {{ $article->author->name }}</p>
<div>{!! $article->body_html !!}</div>
</article>
@endsection
@push('scripts')
<script>// Script spécifique à cette page</script>
@endpush
Components
# Composant de classe (avec logique PHP)
php artisan make:component Alert
# Composant anonyme (juste un fichier Blade)
# → créer resources/views/components/alert.blade.php
{{-- resources/views/components/alert.blade.php --}}
@props(['type' => 'info', 'dismissible' => false])
@php
$classes = match($type) {
'success' => 'bg-green-100 border-green-500 text-green-700',
'error' => 'bg-red-100 border-red-500 text-red-700',
'warning' => 'bg-yellow-100 border-yellow-500 text-yellow-700',
default => 'bg-blue-100 border-blue-500 text-blue-700',
};
@endphp
<div {{ $attributes->merge(['class' => "border-l-4 p-4 {$classes}"]) }}>
{{ $slot }}
@if($dismissible)
<button onclick="this.parentElement.remove()">✕</button>
@endif
</div>
{{-- Utilisation du composant --}}
<x-alert type="success">
Article créé avec succès !
</x-alert>
<x-alert type="error" :dismissible="true" class="mt-4">
Une erreur est survenue.
</x-alert>
{{-- Passer des variables PHP --}}
<x-card :title="$article->title" :author="$article->author">
{{ $article->excerpt }}
</x-card>
Directives
{{-- Conditionnelles --}}
@if($article->isPublished())
<span class="badge">Publié</span>
@elseif($article->isDraft())
<span class="badge">Brouillon</span>
@else
<span class="badge">Archivé</span>
@endif
@unless($user->isAdmin())
<p>Accès restreint</p>
@endunless
{{-- Boucles --}}
@foreach($articles as $article)
<div>
{{ $loop->iteration }}. {{ $article->title }}
@if($loop->first) ← Premier @endif
@if($loop->last) ← Dernier @endif
</div>
@endforeach
@forelse($articles as $article)
<li>{{ $article->title }}</li>
@empty
<li>Aucun article</li>
@endforelse
{{-- Auth --}}
@auth
Bonjour, {{ auth()->user()->name }} !
@endauth
@guest
<a href="/login">Se connecter</a>
@endguest
{{-- Permissions (avec Gate/Policy) --}}
@can('update', $article)
<a href="/articles/{{ $article->id }}/edit">Modifier</a>
@endcan
@cannot('delete', $article)
<p>Vous ne pouvez pas supprimer cet article.</p>
@endcannot
{{-- Environnement --}}
@env('local')
<div class="debug-bar">ENV: local</div>
@endenv
Slots nommés
{{-- resources/views/components/card.blade.php --}}
@props(['title' => null, 'footer' => null])
<div class="card">
@if($title)
<div class="card-header">
{{ $title }}
</div>
@endif
<div class="card-body">
{{ $slot }}
</div>
@if(!$footer->isEmpty())
<div class="card-footer">
{{ $footer }}
</div>
@endif
</div>
{{-- Utilisation avec slots nommés --}}
<x-card>
<x-slot:title>
<h2>Mon Article</h2>
</x-slot:title>
<p>Contenu principal de la carte.</p>
<p>Peut contenir du HTML complexe.</p>
<x-slot:footer>
<a href="#">Lire la suite</a>
</x-slot:footer>
</x-card>
Stacks
{{-- Dans le layout : définir le point d'injection --}}
<head>
@stack('styles') {{-- les vues enfants pushent ici --}}
</head>
<body>
@yield('content')
@stack('scripts') {{-- les vues enfants pushent ici --}}
</body>
{{-- Dans une vue enfant : pousser du contenu --}}
@extends('layouts.app')
@push('styles')
<link rel="stylesheet" href="/css/chart.css">
@endpush
@section('content')
<canvas id="myChart"></canvas>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById('myChart'), { type: 'bar', data: @json($chartData) });
</script>
@endpush
{{-- prepend : insérer au début du stack --}}
@prepend('scripts')
<script>// Ce script sera avant les autres </script>
@endprepend
Formulaires & Validation
{{-- Formulaire de création --}}
<form action="{{ route('articles.store') }}" method="POST">
@csrf {{-- Token CSRF obligatoire pour POST/PUT/DELETE --}}
<div>
<label for="title">Titre</label>
<input
type="text"
id="title"
name="title"
value="{{ old('title') }}"
class="{{ $errors->has('title') ? 'is-invalid' : '' }}"
>
@error('title')
<span class="error">{{ $message }}</span>
@enderror
</div>
<div>
<label for="body">Contenu</label>
<textarea name="body">{{ old('body') }}</textarea>
@error('body')
<span class="error">{{ $message }}</span>
@enderror
</div>
<button type="submit">Créer</button>
</form>
{{-- Formulaire de modification (PUT) --}}
<form action="{{ route('articles.update', $article) }}" method="POST">
@csrf
@method('PUT') {{-- Simule la méthode PUT --}}
{{-- champs... --}}
</form>
{{-- Formulaire de suppression (DELETE) --}}
<form action="{{ route('articles.destroy', $article) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit" onclick="return confirm('Supprimer ?')">Supprimer</button>
</form>
old('field') re-remplit le formulaire après une erreur de validation. Sans ça, l'utilisateur perd tout ce qu'il a tapé.
Intro Livewire
composer require livewire/livewire
php artisan make:livewire SearchArticles
// app/Livewire/SearchArticles.php
namespace App\Livewire;
use App\Models\Article;
use Livewire\Component;
class SearchArticles extends Component
{
public string $search = '';
public function render()
{
return view('livewire.search-articles', [
'articles' => Article::where('title', 'like', "%{$this->search}%")->get(),
]);
}
}
{{-- resources/views/livewire/search-articles.blade.php --}}
<div>
<input wire:model.live="search" placeholder="Rechercher...">
@foreach($articles as $article)
<div>{{ $article->title }}</div>
@endforeach
</div>
{{-- Utilisation dans une vue Blade normale --}}
<livewire:search-articles />
{{-- ou --}}
@livewire('search-articles')
wire:model.live met à jour le composant à chaque frappe (live). wire:model sans .live met à jour à la perte de focus. Livewire gère automatiquement les requêtes HTTP entre le client et le serveur.