Novedades de Laravel 7

Laravel 7 incluye una nueva forma de escribir componentes utilizando una sintaxis similar a la de HTML. En el siguiente videotutorial exploraremos esta funcionalidad.

Repositorio

Ver el código de esta lección en GitHub

Nuestro primer «View Component»

Vamos a crear un componente de campo de texto.

Si en la vista resources/views/welcome.blade.php colocamos la siguiente línea:

<x-field></x-field>

Veremos el siguiente error:

InvalidArgumentException
Unable to locate class for component [field].

Esto sucede porque Laravel 7 está intentando encontrar un componente con el nombre «Field».

Vamos a ejecutar el comando php artisan make:component Field.

Este comando creará 2 archivos, una clase en app/View/Components/Field.php y una vista en resources/views/components/field.blade.php. Exploremos primero la clase.

La clase incluye un método render que por defecto retorna la vista generada previamente. También podemos retornar una cadena:

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class Field extends Component
{
    public function render()
    {
        return 'Field Component';
    }
}

Deberíamos ver el texto «Field Component» si recargamos la página en el navegador.

Vamos a deshacer este cambio, a abrir resources/views/components/field.blade.php y a copiar el siguiente HTML tomado de la documentación de Bootstrap:

<!-- resources/views/components/field.blade.php -->
<div class="form-group">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>

Para poder ver el HTML con los estilos correctos, coloquemos la siguiente hoja de estilo dentro de la etiqueta <head> de resources/views/welcome.blade.php.

<!-- resources/views/welcome.blade.php -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

Además encerraré el componente en algunas capas para mejorar los estilos solamente:

<div class="container">
    <div class="row">
        <div class="col col-8">
            <x-field></x-field>
        </div>
    </div>
</div>

No necesitas usar Bootstrap para probar esta característica, inténtalo con cualquier otro framework o con tu propio HTML y CSS.

Al recargar la página podemos ver el componente:

Blade Component

Para que nuestro componente sea útil deberíamos poder pasar el nombre del campo, además del tipo de campo y un mensaje de ayuda de forma opcional:

<!-- resources/views/welcome.blade.php -->
<x-field type="email" name="email" help="No compartiremos tu correo electrónico con terceros."></x-field>

Esto por sí solo no tendrá ningún efecto.

De vuelta a app/View/Components/Field.php vamos a declarar las propiedades públicas $name, $type y $help, y a iniciar sus valores a través del constructor de la clase.

Dentro de resources/views/components/field.blade.php agreguemos el nombre del campo usando la propiedad {{ $name }} y realicemos algunos cambios adicionales:

<!-- resources/views/components/field.blade.php -->
<div class="form-group">
    <label for="{{ $name }}">{{ ucfirst($name) }}</label>
    <input type="{{ $type }}" class="form-control" id="{{ $name }}" aria-describedby="emailHelp">
    @if ($help)
        <small id="{{ $name }}Help" class="form-text text-muted">{{ $help }}</small>
    @endif
</div>

Al recargar la página e inspeccionar el HTML podemos ver que ahora el componente tiene un nombre, label, tipo y mensaje de ayuda provenientes de la plantilla original.

Como puedes ver las propiedades públicas del componente son pasadas a la vista automáticamente, aunque también es posible pasar propiedades manualmente a través del método render, por ejemplo:

<?php
    // app/View/Components/Field.php

    public function render()
    {
        return view('components.field', [
            'label' => ucfirst(str_replace('_', ' ', $this->name)),
        ]);
    }

Además, los métodos públicos están disponibles también a través de la vista. Declaremos un nuevo método llamado label y coloquemos allí la lógica para retornar la etiqueta del campo:

<?php
    // app/View/Components/Field.php

    public function label()
    {
        return ucfirst(str_replace('_', ' ', $this->name));
    }

    public function render()
    {
        return view('components.field');
    }

Si recargamos el navegador una vez más veremos el resultado anterior. Por supuesto, podemos permitir que la etiqueta del campo sea pasada desde la vista inicial. Primero modifiquemos la clase del componente:

<?php

namespace App\View\Components;

use Illuminate\Support\Str;
use Illuminate\View\Component;

class Field extends Component
{
    //...

    /**
     * @var string
     */
    private $label;

    public function __construct(string $label = null)
    {
        //...
        $this->label = $label;
    }

    public function label()
    {
        return $this->label ?: ucfirst(str_replace('_', ' ', $this->name));
    }

    //...
}

Finalmente agreguemos la etiqueta en resources/views/welcome.blade.php:

<!-- resources/views/welcome.blade.php -->
<x-field type="email" name="email" label="Correo electrónico" help="No compartiremos tu correo electrónico con terceros."></x-field>

Si ejecutas estos dos pasos al revés, necesitarás ejecutar php artisan view:clear o volver a realizar cambios en welcome.blade.php para asegurarte que la vista sea recompilada.

Una manera alternativa de obtener la etiqueta del campo, sería utilizar el componente de idiomas de Laravel:

<?php
    // app/View/Components/Field.php
    use Illuminate\Support\Facades\Lang;

    public function label()
    {
        if ($this->label != null) {
            return $this->label;
        }

        if (Lang::has("validation.attributes.{$this->name}")) {
            return Lang::get("validation.attributes.{$this->name}");
        }

        return ucfirst(str_replace('_', ' ', $this->name));
    }

Esto es posible con el facade de Laravel, pero los nuevos View Components se instancian a través del Contenedor de Inyección de Dependencias de Laravel, por lo tanto esto también es posible:

<?php

namespace App\View\Components;

use Illuminate\Translation\Translator;

class Field extends Component
{
    /**
     * @var Translator
     */
    private $translator;

    public function __construct(Translator $translator)
    {
        $this->translator = $translator;

        //...
    }

    public function label()
    {
        //...

        if ($this->translator->has("validation.attributes.{$this->name}")) {
            return $this->translator->get("validation.attributes.{$this->name}");
        }

        //...
    }

    //...
}

Coloca las dependencias al principio y los atributos al final de los parámetros del constructor.

En las próximas lecciones continuaremos diseñando este componente y aprendiendo más sobre esta nueva característica de Blade disponible a partir de Laravel 7.

Utilizar una característica similar en versiones anteriores a Laravel 6

Si quieres utilizar una funcionalidad similar en versiones anteriores de Laravel, puedes revisar el componente Laravel Blade X de Spatie. Sin embargo, ten en cuenta que, aunque esta nueva característica de Blade fue inspirada en «Blade X», este paquete está escrito de una forma diferente y no es 100% compatible con la nueva característica de Laravel 7.

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Nuevos métodos id() y foreignId() para las migraciones de Laravel 7 Lección siguiente Creación de un componente de formulario con Blade y Laravel 7