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

# Hooks de resolución de servicios en paquetes

> Analiza callAfterResolving() de Laravel 13 y explica cómo extender servicios resueltos y no resueltos, junto con las precauciones para singletons, bindings normales y registros repetidos.

Cuando un paquete añade funcionalidad a un servicio de Laravel, a veces quieres no crear la instancia hasta que se use y, al mismo tiempo, aplicar la configuración si ya se ha creado. El método protegido `callAfterResolving()` del service provider sirve para combinar ambas cosas.

En esta página se revisa la implementación de Laravel Framework `v13.35.0` y se profundiza en la extensión desde `boot()` que aparece en [Desarrollo de paquetes para Laravel](/es/advanced/package-development). No es un hook que espere a que termine el arranque de toda la aplicación, sino un hook sobre la resolución del servicio indicado.

## Esperar si no está resuelto y ejecutar ya si está resuelto

`ServiceProvider::callAfterResolving()` realiza el siguiente proceso en dos pasos.

1. Registra el callback en `afterResolving()` del contenedor.
2. Si `resolved()` es true, obtiene el servicio con `make()` y llama también al callback en ese momento.

Si no está resuelto, el método en sí no ejecuta `make()` sobre el destino. En una resolución normal, el contenedor construye el objeto, aplica los extenders, llama a los callbacks de `resolving` y después llama a los callbacks de `afterResolving`.

```mermaid theme={null}
flowchart TD
    A["callAfterResolving() en boot()"] --> B["Registrar en afterResolving"]
    B --> C{"¿El destino está resolved()?"}
    C -->|No| D["No se crea el destino ahora"]
    D --> E["El destino se resuelve más tarde"]
    E --> F["Se ejecuta el callback registrado"]
    C -->|Sí| G["Obtener el destino con make()"]
    G --> H["Ejecutar también el callback en ese momento"]
```

| Método | Sin resolver al registrar | Singleton ya resuelto al registrar |
| - | - | - |
| Configurar directamente tras `$this->app->make()` | Lo crea en ese momento | Configura la instancia existente |
| `$this->app->afterResolving()` | Espera a resoluciones futuras | El registro por sí solo no configura la instancia existente |
| `$this->callAfterResolving()` | Espera a resoluciones futuras | Configura también la instancia existente en ese momento |

Al obtener un singleton de forma normal, el contenedor devuelve antes la instancia guardada, por lo que `afterResolving` no se dispara en cada obtención. Lo importante es que, si solo registras `afterResolving()`, la configuración no se aplicará a un singleton creado antes del registro.

## Añadir una regla al Factory de validación

Como ejemplo de un paquete que ofrece su propia regla de cadena, se registra la regla después de resolver `validator`. El `ValidationServiceProvider` oficial registra esta clave como singleton, y el propio provider admite carga diferida.

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Factory;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->callAfterResolving('validator', function (Factory $factory): void {
            $factory->extend(
                'courier_tracking_code',
                static function ($attribute, $value, $parameters, $validator): bool {
                    return is_string($value)
                        && preg_match('/\ACR-[0-9]{8}\z/', $value) === 1;
                },
                'The :attribute must be a valid courier tracking code.',
            );
        });
    }
}
```

En la aplicación que lo usa, se combina con las reglas normales.

```php theme={null}
$request->validate([
    'tracking_code' => ['required', 'string', 'courier_tracking_code'],
]);
```

El `extend()` de este ejemplo es la API de registro de reglas de `Illuminate\Validation\Factory`. Es un método distinto del `extend()` del contenedor que se describe más adelante. Las reglas personalizadas normales pueden no ejecutarse con valores vacíos, así que la obligatoriedad se indica con `required`. Para el diseño detallado de la regla en sí, consulta [Reglas de validación personalizadas](/es/advanced/custom-validation-rules).

`Factory::extend()` sobrescribe el elemento del array con el mismo nombre de regla, así que, como en este ejemplo, volver a registrar el mismo proceso no añade más entradas. Aun así, para no sobrescribir reglas de otros paquetes, añade un prefijo propio del paquete a los nombres de las reglas públicas.

<Info>
  El callback se ejecuta cuando se resuelve el Factory. La closure de validación de la regla se ejecuta más tarde, cuando el Validator valida el valor. No se validan valores de entrada al registrar el hook.
</Info>

## Hacer coincidir la clave destino y la comprobación de resolución

En los eventos de resolución normales del contenedor, no solo se seleccionan los callbacks que coinciden con la clave registrada, sino también los que coinciden con el tipo del objeto obtenido. En cambio, `resolved($name)`, que `callAfterResolving()` usa en ese momento, comprueba si existe una marca de resuelto o una instancia guardada para la clave normalizada a partir del alias. No es un proceso que busque entre todos los objetos ya creados.

Por eso, que una interfaz o clase esté resuelta no garantiza que `resolved()` devuelva true para otra clave. Revisa el binding real y los alias del servicio destino, y prueba también el caso ya resuelto con la misma clave. En el ejemplo anterior se indica `validator`, que es la clave registrada por el provider oficial.

Además, `resolved()` también incluye el criterio de «se resolvió en el pasado». No significa que la instancia actual siga existiendo necesariamente.

## No es una API que garantice «ejecutar solo una vez»

`callAfterResolving()` deja el callback registrado. Incluso después de ejecutarse en ese momento, el callback registrado se ejecutará si el objeto se vuelve a resolver en el futuro. Además, si vuelves a llamar al método, se añade otro callback.

Ten especial cuidado si ya has resuelto un binding normal no compartido. En la implementación revisada, ocurre lo siguiente.

1. Se registra un nuevo callback.
2. Como `resolved()` es true, se llama a `make()`.
3. `make()` crea un nuevo objeto y el callback se ejecuta en su evento de resolución.
4. El callback inmediato también se ejecuta sobre el mismo objeto devuelto por `make()`.

En este camino, el callback registrado se aplica dos veces al mismo objeto. No reutilices tal cual con bindings normales un diseño pensado solo para obtener singletons.

<Warning>
  No envíes correos, llames a APIs externas, realices cobros ni añadas listeners incondicionalmente dentro del callback. Limita su uso a aplicar configuración idempotente al servicio y diséñalo para que los efectos secundarios no se dupliquen al volver a registrar o resolver.
</Warning>

## Usar otra API para reemplazar el objeto

El valor de retorno de un callback de `afterResolving` no se usa para reemplazar el objeto que devuelve el contenedor. Si quieres devolver un decorador para sustituir el propio servicio, considera el `extend()` del contenedor. El contrato de la closure de esta API es devolver el servicio modificado.

Del mismo modo, `callAfterResolving()` tampoco es un mecanismo que actualice todas las dependencias ya guardadas en otros objetos. Si el objetivo es actualizar dependencias al volver a hacer un binding, revisa `rebinding()` en la documentación oficial y el diseño de la clase destino.

Para los requisitos de un provider que realmente cargue servicios de forma diferida, consulta [DeferrableProvider](/es/advanced/deferred-provider). Usar el hook y hacer que el propio provider que lo registra se cargue de forma diferida son cosas distintas.

## Combinaciones que comprobar al actualizar

Antes de publicar el paquete, verifica no solo el orden de arranque normal, sino también el caso en que otro provider haya usado el servicio antes.

| Caso | Qué comprobar |
| - | - |
| Registrar el hook con el destino sin resolver | El registro por sí solo no crea el destino, y la configuración se aplica en la primera resolución |
| Resolver primero el singleton y después registrar | La configuración se aplica en ese momento a la misma instancia |
| Volver a obtener con `make()` normal un singleton ya configurado | Se devuelve la misma instancia y los callbacks de resolución no se ejecutan de nuevo |
| Registrar sobre un binding normal resuelto anteriormente | No se rompe aunque se aplique dos veces: por el `make()` del registro y por la llamada explícita |
| Registrar el hook varias veces o regenerar instancias | No se acumulan sin querer las mismas reglas, listeners, rutas, etc. |
| Actualizar las versiones de Laravel soportadas | Volver a revisar la clave del binding, si es compartido y el camino de ejecución del hook |

Si registras con una bandera estática que algo ya se usó una vez en toda la aplicación, la configuración puede no aplicarse a servicios recreados en un nuevo contenedor o en las pruebas. En la medida de lo posible, garantiza la idempotencia de la configuración a nivel del objeto destino o de la clave registrada.

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Sobrescritura y actualización de vistas de paquetes" icon="eye" href="/es/advanced/package-views">
    Revisa un ejemplo en el que loadViewsFrom() usa un hook de resolución para registrar el espacio de nombres de las vistas.
  </Card>

  <Card title="Sobrescritura y actualización de traducciones de paquetes" icon="language" href="/es/advanced/package-translations">
    Revisa el registro de espacios de nombres en el Translator y el tratamiento de las traducciones ya cargadas.
  </Card>
</Columns>

## Fuentes primarias consultadas

* [Documentación oficial de Laravel: desarrollo de paquetes](https://github.com/laravel/docs/blob/13.x/packages.md)
* [Documentación oficial de Laravel: Container events, Extending bindings y Rebinding](https://github.com/laravel/docs/blob/13.x/container.md#container-events)
* [Documentación oficial de Laravel: reglas de validación personalizadas](https://github.com/laravel/docs/blob/13.x/validation.md#custom-validation-rules)
* [ServiceProvider: callAfterResolving() y su uso en el registro de recursos](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [Container: resolved(), resolve() y afterResolving()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Container/Container.php)
* [ValidationServiceProvider: registro de validator como singleton](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/ValidationServiceProvider.php)
* [Validation Factory: registro de reglas con extend()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/Factory.php)


## Related topics

- [Desarrollo de paquetes para Laravel](/es/advanced/package-development.md)
- [Temas avanzados](/es/advanced/index.md)
- [Registro de rutas y caché en paquetes](/es/advanced/package-routes.md)
- [Guía de actualización de Laravel 11 a 12](/es/blog/upgrade-11-to-12.md)
- [Hooks de sesión](/es/packages/laravel-copilot-sdk/hooks.md)


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