5. Vistas con Blade en Laravel
Vistas con Blade en Laravel
Section titled “Vistas con Blade en Laravel”Blade es el motor de plantillas incluido con Laravel. A diferencia de otros motores de plantillas PHP, Blade no te restringe de usar código PHP plano en tus vistas. De hecho, todas las vistas Blade se compilan en código PHP plano y se almacenan en caché hasta que sean modificadas, lo que significa que Blade añade esencialmente cero sobrecarga a tu aplicación.
Las vistas Blade utilizan la extensión de archivo
.blade.phpresources/viewsCreación de vistas y componentes
Section titled “Creación de vistas y componentes”Laravel proporciona varios comandos Artisan para crear vistas y componentes Blade rápidamente:
Crear vistas
Section titled “Crear vistas”Aunque puedes crear archivos de vista manualmente, Laravel ofrece un comando para generarlas:
php artisan make:view nombre-vistaEsto creará un archivo
nombre-vista.blade.phpresources/viewsPara crear una vista en un subdirectorio:
php artisan make:view carpeta.nombre-vistaEsto generará
resources/views/carpeta/nombre-vista.blade.phpCrear componentes
Section titled “Crear componentes”Para crear un componente Blade de clase:
php artisan make:component NombreComponenteEste comando genera:
- Una clase PHP en app/View/Components/NombreComponente.php
- Una plantilla Blade en resources/views/components/nombre-componente.blade.php
Para crear un componente en un subdirectorio:
php artisan make:component Admin/AlertaAdminEsto generará el componente en
app/View/Components/Admin/AlertaAdmin.phpresources/views/components/admin/alerta-admin.blade.phpComponentes anónimos
Section titled “Componentes anónimos”Si solo necesitas la plantilla sin la clase PHP:
php artisan make:component forms.input --viewEsto creará únicamente
resources/views/components/forms/input.blade.phpComponentes en línea
Section titled “Componentes en línea”Para crear un componente que renderiza su contenido directamente desde el método render() sin una vista separada:
php artisan make:component Alert --inlineEsto generará solo la clase PHP con un método render() que devuelve una cadena HTML.
Sintaxis Blade básica
Section titled “Sintaxis Blade básica”Blade proporciona una sintaxis limpia y elegante para trabajar con datos y estructuras de control en tus vistas. Vamos a explorar las directivas básicas de Blade.
Mostrar datos
Section titled “Mostrar datos”Puedes mostrar datos pasados a tu vista Blade utilizando llaves dobles:
<!-- Mostrar una variable -->{{ $variable }}
<!-- Mostrar el resultado de una función -->{{ time() }}
<!-- Mostrar una variable con escape HTML desactivado (cuidado con XSS) -->{!! $contenidoHtml !!}Comentarios
Section titled “Comentarios”Puedes añadir comentarios en tus plantillas Blade que no serán visibles en el HTML resultante:
{{-- Este comentario no aparecerá en el HTML --}}Estructuras de control
Section titled “Estructuras de control”Blade proporciona directivas convenientes para las estructuras de control comunes de PHP:
Condicionales
Section titled “Condicionales”<!-- Directiva @if -->@if($usuario->tipo === 'admin') <p>Este usuario es administrador</p>@elseif($usuario->tipo === 'editor') <p>Este usuario es editor</p>@else <p>Este usuario es miembro regular</p>@endif
<!-- Directiva @unless (inverso de @if) -->@unless($usuarios->isEmpty()) <p>Hay usuarios disponibles</p>@endunless
<!-- Directiva @isset -->@isset($usuario) <p>La variable usuario está definida</p>@endisset
<!-- Directiva @empty -->@empty($usuarios) <p>No hay usuarios disponibles</p>@endemptyOperador ternario
Section titled “Operador ternario”<!-- Sintaxis estándar -->{{ $esActivo ? 'Activo' : 'Inactivo' }}
<!-- Sintaxis abreviada -->{{ $nombre ?? 'Invitado' }}Bucles
Section titled “Bucles”<!-- Bucle @foreach -->@foreach($usuarios as $usuario) <p>{{ $usuario->nombre }}</p>@endforeach
<!-- Bucle @forelse (con caso vacío) -->@forelse($usuarios as $usuario) <p>{{ $usuario->nombre }}</p>@empty <p>No hay usuarios registrados</p>@endforelse
<!-- Bucle @for -->@for($i = 0; $i < 10; $i++) <p>Valor: {{ $i }}</p>@endfor
<!-- Bucle @while -->@while($condicion) <p>Bucle while en ejecución</p>@endwhileVariables de bucle
Section titled “Variables de bucle”Dentro de un bucle
@foreach@forelse@foreach($usuarios as $usuario) @if($loop->first) <p>Este es el primer usuario</p> @endif
<p>{{ $loop->iteration }} / {{ $loop->count }}</p>
@if($loop->last) <p>Este es el último usuario</p> @endif@endforeachLas propiedades disponibles en la variable
$loop- : Índice del bucle actual (comienza en 0)$loop->index
- : Iteración actual (comienza en 1)$loop->iteration
- : Iteraciones restantes$loop->remaining
- : Total de elementos$loop->count
- : Si es la primera iteración$loop->first
- : Si es la última iteración$loop->last
- : Si es una iteración par$loop->even
- : Si es una iteración impar$loop->odd
- : Nivel de anidamiento del bucle$loop->depth
- : En bucles anidados, accede a la variable loop del padre$loop->parent
Directivas de autenticación
Section titled “Directivas de autenticación”Blade proporciona directivas convenientes para verificar el estado de autenticación del usuario:
@auth <!-- El usuario está autenticado --> <p>Bienvenido, {{ Auth::user()->name }}</p>@endauth
@guest <!-- El usuario NO está autenticado --> <p>Por favor inicia sesión</p>@endguest
<!-- Verificar un guard específico -->@auth('admin') <!-- El usuario está autenticado en el guard 'admin' -->@endauthDirectivas de verificación de roles
Section titled “Directivas de verificación de roles”Puedes crear directivas personalizadas para verificar roles o permisos:
@can('editar', $post) <a href="/posts/{{ $post->id }}/edit">Editar post</a>@elsecan('ver', $post) <a href="/posts/{{ $post->id }}">Ver post</a>@endcan
@cannot('eliminar', $post) <p>No tienes permiso para eliminar este post</p>@endcannotHerencia de plantillas
Section titled “Herencia de plantillas”Blade proporciona un poderoso sistema de herencia de plantillas que te permite crear un diseño base que contiene todas las secciones comunes de tu sitio y definir “bloques” que las plantillas hijas pueden sobrescribir.
@extends
Section titled “@extends”La directiva
@extends@extends('layouts.app')Esto indica que la vista actual extiende la plantilla ubicada en
resources/views/layouts/app.blade.php@section y @yield
Section titled “@section y @yield”Las directivas
@section@yield<!DOCTYPE html><html><head> <title>@yield('title', 'Mi Sitio')</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/css/app.css"> @yield('styles')</head><body> <header> @include('partials.nav') </header>
<main> @yield('content') </main>
<footer> @include('partials.footer') </footer>
<script src="/js/app.js"></script> @yield('scripts')</body></html>@extends('layouts.app')
@section('title', 'Página de inicio')
@section('styles') <link rel="stylesheet" href="/css/home.css">@endsection
@section('content') <div class="container"> <h1>Bienvenido a mi sitio</h1> <p>Este es el contenido de la página de inicio.</p> </div>@endsection
@section('scripts') <script src="/js/home.js"></script>@endsectionEn este ejemplo:
- define un espacio llamado “title” con un valor predeterminado “Mi Sitio”.@yield('title', 'Mi Sitio')
- en la vista hija proporciona contenido para ese espacio.@section('title', 'Página de inicio')
- Para secciones más grandes, usamos .@section('content') ... @endsection
@show y @parent
Section titled “@show y @parent”La directiva
@show@endsection<!-- Layout -->@section('sidebar') <p>Contenido del sidebar principal</p>@show
<!-- Vista hija -->@section('sidebar') @parent <p>Contenido adicional del sidebar</p>@endsectionLa directiva
@parent@include
Section titled “@include”La directiva
@include<!-- Incluir una vista simple -->@include('partials.header')
<!-- Incluir una vista y pasar datos -->@include('partials.error', ['mensaje' => 'Error en el formulario'])
<!-- Incluir una vista si existe -->@includeIf('partials.sidebar', ['algunaVariable' => 'valor'])
<!-- Incluir una vista si una condición es verdadera -->@includeWhen($usuario->esAdmin, 'partials.admin-panel')
<!-- Incluir una vista si una condición es falsa -->@includeUnless($usuario->esAdmin, 'partials.user-panel')
<!-- Incluir la primera vista que exista de un array -->@includeFirst(['custom.admin', 'admin'], ['algunaVariable' => 'valor'])La directiva
@each<!-- Sintaxis: @each('vista.a.renderizar', $datos, 'variable', 'vista.si.vacio') -->@each('partials.usuario', $usuarios, 'usuario', 'partials.no-usuarios')Esto renderizará la vista
partials.usuario$usuarios$usuario$usuariospartials.no-usuariosComponentes y layouts
Section titled “Componentes y layouts”Los componentes Blade proporcionan un enfoque modular para construir interfaces de usuario. Laravel ofrece dos tipos de componentes: componentes de clase y componentes anónimos.
Componentes de clase
Section titled “Componentes de clase”Los componentes de clase son componentes Blade respaldados por una clase PHP. Para crear un componente de clase, puedes usar el comando Artisan:
php artisan make:component AlertEste comando creará dos archivos:
- Una clase de componente en app/View/Components/Alert.php
- Una plantilla Blade en resources/views/components/alert.blade.php
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component{ public $type; public $message;
/** * Crear una nueva instancia del componente. * * @param string $type * @param string $message * @return void */ public function __construct($type = 'info', $message = null) { $this->type = $type; $this->message = $message; }
/** * Métodos adicionales disponibles en la plantilla. */ public function classes() { return ["alert", "alert-{$this->type}"]; }
/** * Obtener la vista / contenido que representa el componente. * * @return \Illuminate\Contracts\View\View|\Closure|string */ public function render() { return view('components.alert'); }}<div {{ $attributes->merge(['class' => implode(' ', $classes())]) }}> <div class="alert-icon"> @if($type === 'success') <svg><!-- Ícono de éxito --></svg> @elseif($type === 'warning') <svg><!-- Ícono de advertencia --></svg> @elseif($type === 'error') <svg><!-- Ícono de error --></svg> @else <svg><!-- Ícono de info --></svg> @endif </div>
<div class="alert-content"> @if($title) <div class="alert-title">{{ $title }}</div> @endif
<div class="alert-body"> {{ $message ?? $slot }} </div> </div>
@if($dismissible) <button type="button" class="alert-close" aria-label="Cerrar"> × </button> @endif</div><!-- Uso básico --><x-alert type="success" message="Operación completada con éxito" />
<!-- Con contenido en slot --><x-alert type="error"> <strong>Error:</strong> No se pudo procesar la solicitud.</x-alert>
<!-- Con atributos adicionales --><x-alert type="warning" class="mt-4 mb-4" id="my-alert" data-auto-close="5000"> Esta alerta se cerrará automáticamente.</x-alert>Componentes anónimos
Section titled “Componentes anónimos”Los componentes anónimos son componentes Blade que no tienen una clase asociada. Son útiles para componentes simples que no requieren lógica adicional:
<div class="form-group"> <label for="{{ $id }}" class="form-label">{{ $label }}</label> <input type="{{ $type ?? 'text' }}" id="{{ $id }}" name="{{ $name ?? $id }}" value="{{ $value ?? old($name ?? $id) }}" {{ $attributes->merge(['class' => 'form-control']) }} > @error($name ?? $id) <div class="invalid-feedback">{{ $message }}</div> @enderror</div>Para usar este componente:
<x-input id="email" label="Correo electrónico" type="email" required placeholder="ejemplo@correo.com"/>Atributos y slots
Section titled “Atributos y slots”Los componentes Blade pueden recibir atributos y contenido a través de slots:
Atributos
Section titled “Atributos”Puedes acceder a los atributos pasados al componente mediante la variable
$attributes<!-- En el componente --><div {{ $attributes }}> <!-- Contenido --></div>
<!-- Fusionar clases --><div {{ $attributes->merge(['class' => 'default-class']) }}> <!-- Contenido --></div>
<!-- Filtrar atributos --><div {{ $attributes->filter(fn ($value, $key) => $key === 'class') }}> <!-- Contenido --></div>Los componentes pueden tener un slot principal y slots con nombre:
<!-- En el componente --><div class="card"> <div class="card-header"> {{ $header ?? 'Encabezado predeterminado' }} </div> <div class="card-body"> {{ $slot }} </div> <div class="card-footer"> {{ $footer ?? '' }} </div></div>
<!-- Uso del componente --><x-card> <x-slot:header> Mi encabezado personalizado </x-slot:header>
Contenido principal de la tarjeta
<x-slot:footer> <button>Guardar</button> </x-slot:footer></x-card>Layouts usando componentes
Section titled “Layouts usando componentes”Puedes crear layouts utilizando componentes, lo que proporciona una alternativa moderna a
@extends<!DOCTYPE html><html lang="es"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ $title ?? 'Mi aplicación' }}</title> <link rel="stylesheet" href="/css/app.css"> {{ $styles ?? '' }}</head><body> <header> <x-navigation /> </header>
<main> {{ $slot }} </main>
<footer> <x-footer /> </footer>
<script src="/js/app.js"></script> {{ $scripts ?? '' }}</body></html><x-layouts.app> <x-slot:title>Panel de control</x-slot:title>
<x-slot:styles> <link rel="stylesheet" href="/css/dashboard.css"> </x-slot:styles>
<div class="dashboard"> <h1>Panel de control</h1> <p>Bienvenido a tu panel de control</p>
<x-stats-card :stats="$stats" /> </div>
<x-slot:scripts> <script src="/js/dashboard.js"></script> </x-slot:scripts></x-layouts.app>Componentes dinámicos
Section titled “Componentes dinámicos”Puedes renderizar componentes dinámicamente utilizando una variable para el nombre del componente:
@php $componentName = $tipo === 'admin' ? 'admin-panel' : 'user-panel';@endphp
<x-dynamic-component :component="$componentName" :data="$data" />Uso de stacks (@push, @stack)
Section titled “Uso de stacks (@push, @stack)”Las stacks en Blade te permiten insertar contenido en secciones específicas de tus plantillas desde cualquier vista hija. Esto es especialmente útil para recursos como scripts o estilos CSS que necesitas incluir desde diferentes vistas pero quieres renderizarlos en un lugar específico de tu layout.
@stack y @push
Section titled “@stack y @push”La directiva
@stack@push<!DOCTYPE html><html><head> <title>@yield('title')</title> <link rel="stylesheet" href="/css/app.css"> @stack('styles')</head><body> <main> @yield('content') </main>
<script src="/js/app.js"></script> @stack('scripts')</body></html>@extends('layouts.app')
@section('title', 'Página de inicio')
@section('content') <h1>Bienvenido</h1> <p>Contenido de la página de inicio</p>@endsection
@push('styles') <link rel="stylesheet" href="/css/home.css">@endpush
@push('scripts') <script src="/js/home.js"></script>@endpush@extends('layouts.app')
@section('title', 'Panel de control')
@section('content') <h1>Panel de control</h1> <div id="chart"></div>@endsection
@push('styles') <link rel="stylesheet" href="/css/dashboard.css"> <link rel="stylesheet" href="/css/charts.css">@endpush
@push('scripts') <script src="/js/dashboard.js"></script> <script src="/js/charts.js"></script>@endpushEn este ejemplo, cada vista hija “empuja” sus propios estilos y scripts a las stacks definidas en el layout principal. Cuando se renderiza la vista, todo el contenido empujado a cada stack se incluirá en el orden en que fue empujado.
@prepend
Section titled “@prepend”Si necesitas añadir contenido al principio de una stack en lugar de al final, puedes usar
@prepend@prepend('scripts') <script src="/js/first-script.js"></script>@endprependPara asegurarte de que un bloque de código solo se incluya una vez, incluso si la vista que lo contiene se incluye múltiples veces, puedes usar
@once@once @push('scripts') <script> // Este script solo se incluirá una vez console.log('Cargado una sola vez'); </script> @endpush@endonceInyección de servicios en vistas (@inject)
Section titled “Inyección de servicios en vistas (@inject)”La directiva
@injectSintaxis básica
Section titled “Sintaxis básica”La sintaxis de
@inject@inject('nombreVariable', 'NombreClaseServicio')Donde:
- es el nombre de la variable que contendrá la instancia del servicio en tu vista.nombreVariable
- es el nombre de la clase o el alias del servicio que quieres inyectar.NombreClaseServicio
Ejemplo práctico
Section titled “Ejemplo práctico”Supongamos que tienes un servicio
MenuService<?php
namespace App\Services;
class MenuService{ public function getMainMenu() { return [ ['title' => 'Inicio', 'url' => '/'], ['title' => 'Productos', 'url' => '/productos'], ['title' => 'Servicios', 'url' => '/servicios'], ['title' => 'Contacto', 'url' => '/contacto'], ]; }
public function getUserMenu($user) { $menu = [ ['title' => 'Mi perfil', 'url' => '/perfil'], ];
if ($user->isAdmin()) { $menu[] = ['title' => 'Panel de administración', 'url' => '/admin']; }
$menu[] = ['title' => 'Cerrar sesión', 'url' => '/logout'];
return $menu; }}// En un Service Providerpublic function register(){ $this->app->singleton(MenuService::class);}@inject('menu', 'App\Services\MenuService')
<nav class="main-nav"> <ul> @foreach($menu->getMainMenu() as $item) <li><a href="{{ $item['url'] }}">{{ $item['title'] }}</a></li> @endforeach </ul></nav>
@auth <nav class="user-nav"> <ul> @foreach($menu->getUserMenu(Auth::user()) as $item) <li><a href="{{ $item['url'] }}">{{ $item['title'] }}</a></li> @endforeach </ul> </nav>@endauthConsideraciones sobre el uso de @inject
Section titled “Consideraciones sobre el uso de @inject”En general, es mejor pasar datos a las vistas desde los controladores cuando sea posible. Sin embargo,
@inject- Son utilizados en múltiples vistas (como navegación, configuración, etc.)
- No dependen del contexto específico de la solicitud actual
- Son ligeros y no realizan operaciones pesadas
Alternativas a @inject
Section titled “Alternativas a @inject”Una alternativa a
@inject<?php
namespace AppProviders;
use AppServicesMenuService;use IlluminateSupportFacadesView;use IlluminateSupportServiceProvider;
class ViewServiceProvider extends ServiceProvider{ public function boot() { // Compartir con todas las vistas View::share('siteName', 'Mi Sitio Web');
// Compartir solo con vistas específicas View::composer( ['layouts.app', 'partials.navigation'], function ($view) { $view->with('menu', app(MenuService::class)); } ); }}Conclusión
Section titled “Conclusión”Blade es un motor de plantillas potente y flexible que ofrece numerosas características para crear vistas dinámicas y reutilizables en Laravel. Desde la sintaxis básica hasta componentes avanzados, herencia de plantillas, stacks e inyección de servicios, Blade proporciona todas las herramientas necesarias para construir interfaces de usuario modernas y mantenibles.
Recuerda que las mejores prácticas incluyen:
- Mantener las vistas simples y enfocadas en la presentación
- Utilizar componentes para elementos de UI reutilizables
- Aprovechar la herencia de plantillas para mantener un diseño consistente
- Usar stacks para organizar recursos como scripts y estilos
- Limitar el uso de a casos específicos donde realmente sea necesario@inject