> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Extensiones de Blade en paquetes y caché de compilación

> A partir del BladeCompiler de Laravel 13, explica cómo registrar directivas y condicionales propios, separar el momento de compilación del de ejecución y verificar la caché de vistas al actualizar un paquete.

Para ofrecer sintaxis de Blade desde un paquete, separa **el proceso que convierte la plantilla en PHP** del **proceso que ejecuta el PHP generado**. Si incrustas el valor actual de la configuración en tiempo de compilación, las vistas en caché pueden seguir mostrando valores obsoletos.

Esta página toma como referencia la documentación oficial de Laravel `13.x` y la implementación de Framework `v13.37.0`, y profundiza en los [fundamentos del desarrollo de paquetes](/es/advanced/package-development). Para la sintaxis habitual de las plantillas, consulta [Blade](/es/blade); para los espacios de nombres y la publicación de vistas, consulta [Sobrescritura y actualización de vistas de paquetes](/es/advanced/package-views).

## Distinguir registro, compilación y ejecución

El callback que se pasa a `Blade::directive()` no devuelve el valor que se muestra, sino **una cadena con el código PHP que se genera**. El argumento `$expression` tampoco es un valor evaluado, sino la expresión PHP escrita en la plantilla.

```mermaid theme={null}
flowchart TD
    A["Registrar la extensión<br>en el boot del proveedor"] --> B["Renderizar la vista"]
    B --> C{"¿Hace falta compilar?"}
    C -->|Sí| D["El callback de directive<br>genera código PHP"]
    D --> E["Guardar el resultado compilado"]
    C -->|No| F["Usar el PHP guardado"]
    E --> G["Ejecutar el PHP y generar HTML"]
    F --> G
    G --> H["Consultar la configuración<br>y los argumentos en ejecución"]
```

| API | Función del callback registrado | Momento en que se ejecuta |
| - | - | - |
| `Blade::directive()` | Genera una cadena de código PHP | Al compilar la directiva correspondiente |
| `Blade::if()` | Evalúa la condición | Cuando el PHP generado llama a `Blade::check()` |

`view:cache` también realiza la compilación. Diseña el callback que genera la directiva para que no obtenga el usuario actual, no escriba en la base de datos ni llame a APIs externas.

## Registrar con nombres propios del paquete

Como ejemplo, se ofrece `@acmeCourierLabel($code)`, que muestra la etiqueta configurada delante del código de envío. Se asume que el PSR-4 de Composer asigna `Acme\Courier\` a `src/` y que el proveedor se registra mediante el [descubrimiento automático](/es/advanced/package-discovery) o manualmente.

```php src/CourierServiceProvider.php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Blade::directive('acmeCourierLabel', function (string $expression): string {
            return "<?php echo e((string) config('courier.label', 'Tracking').': '.({$expression})); ?>";
        });

        Blade::if('acmeCourierEnabled', function (): bool {
            return (bool) config('courier.enabled', false);
        });
    }
}
```

En este ejemplo, el contrato de configuración establece que `courier.label` es una cadena y `courier.enabled` es un booleano. Para distribuir y fusionar el archivo de configuración, consulta [Fusión y caché de la configuración de paquetes](/es/advanced/package-config-merging).

El registro se hace en el `boot()` habitual del proveedor, sin envolverlo en una condición `runningInConsole()`. La misma extensión es necesaria tanto al renderizar por HTTP como al ejecutar `view:cache` de Artisan. Evita también convertir en proveedor diferido un proveedor que solo registra extensiones de Blade, ya que no se iniciaría hasta que se resolviera otro servicio.

### Recibir los argumentos como expresiones, no como valores

```blade theme={null}
@acmeCourierLabel($code)
```

En esta plantilla, el callback recibe la cadena `$code`. El PHP generado tiene la siguiente forma.

```php theme={null}
<?php echo e((string) config('courier.label', 'Tracking').': '.($code)); ?>
```

`config()` y `$code` se evalúan en el momento en que se ejecuta este PHP. Es distinto de una implementación en la que el propio callback lee `config()` y devuelve una cadena fija.

El `callCustomDirective()` revisado elimina los paréntesis exteriores, aplica trim a la expresión y la pasa al callback. No convierte el argumento en un valor PHP ni lo divide en argumentos individuales. Este ejemplo se publica como **una sintaxis que recibe una única expresión obligatoria**.

Si necesitas varios argumentos, no los separes simplemente con `explode()` por comas. Los arrays, las llamadas a métodos y los literales de cadena también pueden contener comas. Si quieres manejar atributos o HTML complejo, es más fácil separar responsabilidades ofreciéndolo como un [componente de Blade](/es/blade).

<Warning>
  Las expresiones de la plantilla son PHP escrito por desarrolladores de confianza. No construyas el código fuente de una plantilla a partir de datos introducidos por el usuario. Además, el `echo` de una directiva propia no recibe automáticamente el escape habitual de `{{ }}`. En este ejemplo, el PHP generado llama explícitamente a `e()`.
</Warning>

### Las colisiones de nombres también son un problema de compatibilidad

`directive()` guarda el handler en un array usando el nombre como clave. Si el nombre coincide, sobrescribe el registro anterior, y `compileStatement()` prioriza las directivas propias frente a los métodos de conversión integrados. Las pruebas oficiales incluyen incluso un caso en el que un registro propio sobrescribe `foreach`.

Evita nombres genéricos como `label` o `enabled` y los nombres integrados, y añade un prefijo propio del paquete. Trata los nombres y argumentos que los usuarios escriben en sus plantillas como una API pública y proporciona un procedimiento de migración cuando los cambies.

## Evaluar las condiciones en ejecución con Blade::if

La condición registrada en el proveedor anterior se puede usar así.

```blade theme={null}
@acmeCourierEnabled
    @acmeCourierLabel($code)
@else
    El seguimiento de envíos está desactivado.
@endacmeCourierEnabled
```

`Blade::if()` guarda el callback de la condición y registra las directivas de inicio, `unless`, `else` y cierre. En este ejemplo, la parte de inicio se convierte en el siguiente PHP.

```php theme={null}
<?php if (\Illuminate\Support\Facades\Blade::check('acmeCourierEnabled')): ?>
```

El callback que consulta la configuración se llama desde `check()` en tiempo de ejecución. No fija el resultado true o false en tiempo de compilación. Por ello, el proceso HTTP que usa las vistas compiladas también necesita que el callback de la condición esté registrado.

Esta sintaxis sirve para alternar lo que se muestra, no para autorizar operaciones de envío. Aplica también el control de acceso en las rutas o en las operaciones.

## Actualizar el paquete no siempre regenera las vistas automáticamente

El `Compiler::isExpired()` revisado comprueba si existe la caché y, según la configuración, compara la fecha de modificación de la vista original con la del archivo compilado. **No hace seguimiento de los cambios en las directivas escritas en el proveedor como archivos dependientes.**

| Cambio | Efecto en el PHP compilado |
| - | - |
| Cambiar el código que devuelve `directive()` | El PHP antiguo permanece, por lo que hay que recompilar |
| Cambiar solo la lógica del callback de la condición de `Blade::if()` | El `check()` ya generado puede llamar a la nueva lógica, pero hay que cargar el código nuevo |
| Cambiar el nombre de la directiva o el contrato de sus argumentos | Hay que modificar las plantillas de los usuarios y recompilar |
| Vistas ya publicadas en la aplicación | Actualizar las vistas del paquete no actualiza las copias publicadas |

La documentación oficial también indica que, después de cambiar la lógica de una directiva, se borre la caché con `view:clear`. Ejecútalo en el proceso de despliegue de la aplicación que lo usa, después de alinear el paquete actualizado con las vistas del usuario.

```bash theme={null}
php artisan view:clear
php artisan view:cache
```

Ejecuta `view:cache` cuando quieras precompilar en producción. No borres la caché en cada `boot()` del paquete ni modifiques sin permiso el proceso de despliegue de la aplicación durante la instalación. La actualización de la caché de configuración y la recarga de procesos persistentes también deben seguir el procedimiento de despliegue habitual del entorno.

## Verificar por separado el resultado compilado y el renderizado

Primero, verifica el PHP generado en una aplicación Laravel de pruebas que tenga registrado el proveedor anterior. El siguiente método se añade a una prueba Feature que hereda de `Tests\TestCase`.

```php theme={null}
public function test_label_directive_compiles_to_runtime_lookup(): void
{
    $compiled = \Illuminate\Support\Facades\Blade::compileString(
        '@acmeCourierLabel($code)',
    );

    $this->assertSame(
        "<?php echo e((string) config('courier.label', 'Tracking').': '.(\$code)); ?>",
        $compiled,
    );
}
```

Esta prueba confirma el contrato de generación de PHP, pero no verifica el escape ni las bifurcaciones en tiempo de ejecución. En un paquete real, añade también verificaciones del renderizado de vistas y de las actualizaciones.

| Caso de verificación | Resultado esperado |
| - | - |
| Pasar en el código de envío caracteres que se interpretarían como HTML | Se escapan mediante el `e()` generado |
| Usar la misma vista compilada y cambiar la etiqueta configurada antes de renderizar | El siguiente renderizado muestra el nuevo valor de configuración |
| Renderizar con `courier.enabled` en true y en false | Solo se muestra la rama correspondiente |
| Arrancar una aplicación nueva después de `view:cache` | La extensión está registrada y el resultado es el mismo que sin precompilar |
| Desplegar tras cambiar el código que genera la directiva | Tras regenerar la caché, se usa el nuevo PHP |
| Actualizar la versión de Laravel compatible | Pasan las pruebas de código generado, renderizado y uso de la caché |

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Sobrescritura y actualización de vistas de paquetes" icon="eye" href="/es/advanced/package-views">
    Revisa el orden de búsqueda de los espacios de nombres y cómo mantener las plantillas publicadas.
  </Card>

  <Card title="Caché de paquetes e integración con optimize" icon="gears" href="/es/advanced/package-optimization">
    Revisa cómo integrar la generación y eliminación de cachés propias en el proceso de despliegue.
  </Card>
</Columns>

## Fuentes primarias consultadas

* [Documentación oficial de Laravel: Extending Blade y Custom if statements](https://github.com/laravel/docs/blob/12080c72699b99ec6573f7e79ff4834d9001b6bc/blade.md#extending-blade)
* [Documentación oficial de Laravel: vistas y componentes de paquetes](https://github.com/laravel/docs/blob/12080c72699b99ec6573f7e79ff4834d9001b6bc/packages.md#views)
* [BladeCompiler: directive(), callCustomDirective(), compileStatement(), if() y check()](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/View/Compilers/BladeCompiler.php)
* [Compiler: evaluación de la caché con isExpired()](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/View/Compilers/Compiler.php)
* [BladeCustomTest: directivas y condiciones propias, y sobrescritura de sintaxis integrada](https://github.com/laravel/framework/blob/v13.37.0/tests/View/Blade/BladeCustomTest.php)


## Related topics

- [Temas avanzados](/es/advanced/index.md)
- [Registro de rutas y caché en paquetes](/es/advanced/package-routes.md)
- [Registro y caché de listeners de eventos en paquetes](/es/advanced/package-events.md)
- [Sobrescritura y actualización de vistas de paquetes](/es/advanced/package-views.md)
- [Plantillas Blade](/es/blade.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.