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

# Guía de migración de la estructura antigua a la nueva

> Explica los pasos para migrar un proyecto desde la estructura antigua de aplicación de Laravel 10 al Slim Application Skeleton de Laravel 11 y posteriores.

## Introducción

Esta guía cubre los pasos para migrar la estructura antigua de aplicación (con clases `Kernel` y varios service providers) de Laravel 10 y anteriores al Slim Application Skeleton de Laravel 11 en adelante.

<Warning>
  **La documentación oficial no recomienda esta migración.**

  * La estructura antigua de Laravel 10 **sigue funcionando** en Laravel 11 y posteriores. No hay planes de retirar su soporte hasta la versión actual, incluida Laravel 13.
  * La migración es **completamente opcional**. Ni es obligatoria ni está recomendada.
  * Si no conoces en profundidad las diferencias entre la estructura antigua y la nueva, es muy recomendable no realizar la migración. Es un trabajo pensado para desarrolladores familiarizados con las tripas del framework.
  * Antes de migrar, **haz siempre una copia de seguridad** y comprueba que todos los tests pasan.
</Warning>

## Cuándo puede ser necesario migrar

Puedes plantearte la migración en casos como los siguientes:

* Quieres que la estructura del proyecto coincida con la estándar más reciente para que los nuevos miembros del equipo puedan contrastarla fácilmente con la documentación oficial.
* Quieres mantener la coherencia con paquetes o starter kits creados en Laravel 11 o posterior.
* Quieres reducir los archivos y clases de configuración heredados de la estructura antigua y simplificar tu código base.

## Requisitos previos

Esta guía asume lo siguiente:

* **La actualización de versión de Laravel ya se ha completado** (`laravel/framework ^11.0` o superior).
* Todos los tests existentes pasan.
* Conoces la estructura de aplicación de Laravel 11 en adelante (consulta [Estructura de aplicación en Laravel 11 y posteriores](/es/advanced/app-structure)).

## Ejemplo de migración

A continuación se muestran los pasos para migrar únicamente la estructura de la aplicación de un proyecto creado con Laravel 10 + Breeze (stack Blade), manteniendo Breeze intacto.

<Steps>
  <Step title="Sustituir bootstrap/app.php">
    El antiguo `bootstrap/app.php` creaba la instancia `$app` y registraba los kernels. Sustitúyelo por la cadena `Application::configure()`.

    **Antiguo (Laravel 10):**

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

    $app = new Illuminate\Foundation\Application(
        $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
    );

    $app->singleton(
        Illuminate\Contracts\Http\Kernel::class,
        App\Http\Kernel::class
    );

    $app->singleton(
        Illuminate\Contracts\Console\Kernel::class,
        App\Console\Kernel::class
    );

    $app->singleton(
        Illuminate\Contracts\Debug\ExceptionHandler::class,
        App\Exceptions\Handler::class
    );

    return $app;
    ```

    **Nuevo (Laravel 11 y posteriores):**

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

    use Illuminate\Foundation\Application;
    use Illuminate\Foundation\Configuration\Exceptions;
    use Illuminate\Foundation\Configuration\Middleware;

    return Application::configure(basePath: dirname(__DIR__))
        ->withRouting(
            web: __DIR__.'/../routes/web.php',
            commands: __DIR__.'/../routes/console.php',
            health: '/up',
        )
        ->withMiddleware(function (Middleware $middleware) {
            //
        })
        ->withExceptions(function (Exceptions $exceptions) {
            //
        })->create();
    ```

    <Info>
      Las callbacks de `withMiddleware()` y `withExceptions()` son el sitio donde deberás migrar la configuración de los archivos de kernel que eliminarás en el siguiente paso. De momento déjalas vacías; añadirás la configuración en pasos posteriores.
    </Info>
  </Step>

  <Step title="Eliminar el kernel HTTP (app/Http/Kernel.php)">
    `app/Http/Kernel.php` definía los middlewares globales, los grupos de middleware y los alias de middleware.

    **Antiguo (app/Http/Kernel.php):**

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

    namespace App\Http;

    use Illuminate\Foundation\Http\Kernel as HttpKernel;

    class Kernel extends HttpKernel
    {
        protected $middleware = [
            \App\Http\Middleware\TrustProxies::class,
            \Illuminate\Http\Middleware\HandleCors::class,
            \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
            \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
            \App\Http\Middleware\TrimStrings::class,
            \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        ];

        protected $middlewareGroups = [
            'web' => [
                \App\Http\Middleware\EncryptCookies::class,
                \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
                \Illuminate\Session\Middleware\StartSession::class,
                \Illuminate\View\Middleware\ShareErrorsFromSession::class,
                \App\Http\Middleware\VerifyCsrfToken::class,
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
            ],
            'api' => [
                \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
                \Illuminate\Routing\Middleware\SubstituteBindings::class,
            ],
        ];

        protected $middlewareAliases = [
            'auth' => \App\Http\Middleware\Authenticate::class,
            // ...
        ];
    }
    ```

    En Laravel 11 estos middlewares están integrados como valores por defecto dentro del framework. Si **no** los has personalizado, puedes eliminar `app/Http/Kernel.php` directamente.

    **Si tienes personalizaciones** (añades o excluyes middlewares propios), migra esa configuración a `withMiddleware()` en `bootstrap/app.php` antes de eliminarlo.

    ```php theme={null}
    ->withMiddleware(function (Middleware $middleware) {
        // Añadir un middleware global
        $middleware->append(\App\Http\Middleware\MyCustomMiddleware::class);

        // Añadir un middleware al grupo web
        $middleware->web(append: [
            \App\Http\Middleware\HandleInertiaRequests::class,
        ]);

        // Añadir alias de middleware
        $middleware->alias([
            'role' => \App\Http\Middleware\CheckRole::class,
        ]);
    })
    ```

    Cuando hayas terminado la migración, elimina `app/Http/Kernel.php`.

    <Info>
      En Laravel 11, las clases de middleware por defecto como `TrustProxies`, `EncryptCookies` o `VerifyCsrfToken` también se pueden eliminar de `app/Http/Middleware/`. Como el framework las incorpora internamente, si no necesitas personalizarlas, sus archivos ya no hacen falta.
    </Info>
  </Step>

  <Step title="Eliminar el kernel de consola (app/Console/Kernel.php)">
    `app/Console/Kernel.php` se encargaba de definir el schedule y de cargar automáticamente los comandos.

    **Antiguo (app/Console/Kernel.php):**

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

    namespace App\Console;

    use Illuminate\Console\Scheduling\Schedule;
    use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

    class Kernel extends ConsoleKernel
    {
        protected function schedule(Schedule $schedule): void
        {
            // $schedule->command('inspire')->hourly();
        }

        protected function commands(): void
        {
            $this->load(__DIR__.'/Commands');
            require base_path('routes/console.php');
        }
    }
    ```

    La **carga automática de comandos** en Laravel 11 y posteriores es automática: el directorio `app/Console/Commands/` se escanea solo, por lo que no necesitas `$this->load()`.

    La **definición del schedule** se migra a `routes/console.php` o al `withSchedule()` de `bootstrap/app.php`.

    ```php theme={null}
    // routes/console.php (Laravel 11 y posteriores)
    use Illuminate\Support\Facades\Schedule;

    Schedule::command('emails:send')->daily();
    ```

    Tras la migración, elimina `app/Console/Kernel.php`.

    <Info>
      No elimines los archivos de comandos Artisan personalizados de `app/Console/`. Deja los archivos de comandos tal cual y elimina únicamente el archivo de la clase Kernel.
    </Info>
  </Step>

  <Step title="Eliminar el handler de excepciones (app/Exceptions/Handler.php)">
    `app/Exceptions/Handler.php` se encargaba de la configuración del reporte y del renderizado de excepciones.

    **Antiguo (app/Exceptions/Handler.php):**

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

    namespace App\Exceptions;

    use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
    use Throwable;

    class Handler extends ExceptionHandler
    {
        protected $dontFlash = [
            'current_password',
            'password',
            'password_confirmation',
        ];

        public function register(): void
        {
            $this->reportable(function (Throwable $e) {
                //
            });
        }
    }
    ```

    Si tienes personalizaciones, migra su contenido a `withExceptions()` en `bootstrap/app.php` antes de eliminarlo.

    ```php theme={null}
    use Throwable;
    use Illuminate\Http\Request;

    ->withExceptions(function (Exceptions $exceptions) {
        // Personalizar el reporte de excepciones
        $exceptions->report(function (Throwable $e) {
            // ...
        });

        // Personalizar el renderizado de excepciones
        $exceptions->render(function (\App\Exceptions\InvalidOrderException $e, Request $request) {
            return response()->view('errors.invalid-order', status: 500);
        });
    })
    ```

    Si habías añadido elementos propios a `$dontFlash`, puedes migrarlos de la misma forma.

    ```php theme={null}
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->dontFlash([
            'my_sensitive_field',
        ]);
    })
    ```

    Cuando termine la migración, elimina `app/Exceptions/Handler.php`.
  </Step>

  <Step title="Eliminar RouteServiceProvider y migrar el registro de rutas">
    `app/Providers/RouteServiceProvider.php` cargaba los archivos de rutas y definía los rate limits.

    **Antiguo (app/Providers/RouteServiceProvider.php):**

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

    namespace App\Providers;

    use Illuminate\Cache\RateLimiting\Limit;
    use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\RateLimiter;
    use Illuminate\Support\Facades\Route;

    class RouteServiceProvider extends ServiceProvider
    {
        public const HOME = '/home';

        public function boot(): void
        {
            RateLimiter::for('api', function (Request $request) {
                return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
            });

            $this->routes(function () {
                Route::middleware('api')
                    ->prefix('api')
                    ->group(base_path('routes/api.php'));

                Route::middleware('web')
                    ->group(base_path('routes/web.php'));
            });
        }
    }
    ```

    Migra el **registro de los archivos de rutas** a `withRouting()` en `bootstrap/app.php`.

    ```php theme={null}
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php', // si utilizas rutas de API
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ```

    Migra los **rate limits** a `AppServiceProvider::boot()`.

    ```php theme={null}
    // app/Providers/AppServiceProvider.php
    use Illuminate\Cache\RateLimiting\Limit;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\RateLimiter;

    public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
    ```

    Si usas la constante `HOME` en algún sitio, reemplázala por la URL directamente en forma de cadena o mueve la constante a `AppServiceProvider`.

    Cuando termines la migración, elimina `app/Providers/RouteServiceProvider.php`.
  </Step>

  <Step title="Consolidar los service providers">
    Laravel 10 traía por defecto 5 service providers. Los consolidaremos en un único `AppServiceProvider.php`.

    **Providers a eliminar (migra su contenido a AppServiceProvider antes de borrarlos):**

    | Archivo                                      | Destino de la migración                                           |
    | -------------------------------------------- | ----------------------------------------------------------------- |
    | `app/Providers/AuthServiceProvider.php`      | `Gate::policy()`, etc., en `AppServiceProvider::boot()`           |
    | `app/Providers/BroadcastServiceProvider.php` | Si usas broadcasting, `withBroadcasting()` de `bootstrap/app.php` |
    | `app/Providers/EventServiceProvider.php`     | `Event::listen()`, etc., en `AppServiceProvider::boot()`          |
    | `app/Providers/RouteServiceProvider.php`     | Ya tratado en el paso anterior                                    |

    **Ejemplo de migración del contenido antiguo de app/Providers/AuthServiceProvider.php:**

    ```php theme={null}
    // Antiguo: app/Providers/AuthServiceProvider.php
    use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

    class AuthServiceProvider extends ServiceProvider
    {
        protected $policies = [
            Post::class => PostPolicy::class,
        ];

        public function boot(): void
        {
            $this->registerPolicies();
        }
    }
    ```

    ```php theme={null}
    // Nuevo: app/Providers/AppServiceProvider.php
    // Si sigues las convenciones de nomenclatura estándar de Laravel, se detecta automáticamente y no es necesario
    use Illuminate\Support\Facades\Gate;

    class AppServiceProvider extends ServiceProvider
    {
        public function boot(): void
        {
            Gate::policy(Post::class, PostPolicy::class);
        }
    }
    ```

    Cuando hayas eliminado los archivos de providers innecesarios, elimina también el array `providers` de `config/app.php`.

    ```php theme={null}
    // Array providers en config/app.php
    // Se puede eliminar completo
    'providers' => ServiceProvider::defaultProviders()->merge([
    App\Providers\AppServiceProvider::class,
    // Elimina también las entradas de los providers que has borrado
    // App\Providers\AuthServiceProvider::class, // eliminar
    // App\Providers\EventServiceProvider::class, // eliminar
    // App\Providers\RouteServiceProvider::class, // eliminar
    ])->toArray(),
    ```

    Además, crea `bootstrap/providers.php` para adaptarte a la nueva estructura.

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

    // bootstrap/providers.php
    return [
        App\Providers\AppServiceProvider::class,
    ];
    ```

    <Info>
      Si existe `bootstrap/providers.php`, Laravel dará prioridad a este archivo como lista de providers al cargarlos.
    </Info>
  </Step>

  <Step title="Actualizar la clase base de controlador">
    La clase base `Controller` de Laravel 10 usaba los traits `AuthorizesRequests` y `ValidatesRequests`. La nueva clase base de Laravel 11 es una clase abstracta simple sin esos traits.

    **Antiguo (Laravel 10):**

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

    namespace App\Http\Controllers;

    use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
    use Illuminate\Foundation\Validation\ValidatesRequests;
    use Illuminate\Routing\Controller as BaseController;

    class Controller extends BaseController
    {
        use AuthorizesRequests, ValidatesRequests;
    }
    ```

    **Nuevo (Laravel 11 y posteriores):**

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

    namespace App\Http\Controllers;

    abstract class Controller
    {
        //
    }
    ```

    Sustituye las funcionalidades que aportaban los traits del siguiente modo.

    | Método antiguo (vía trait)              | Método nuevo                                                                                                                         |
    | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
    | `$this->validate($request, $rules)`     | `$request->validate($rules)`                                                                                                         |
    | `$this->authorize('update', $post)`     | `Gate::authorize('update', $post)`                                                                                                   |
    | `$this->authorizeResource(Post::class)` | En Laravel 11 no hay una alternativa sencilla, así que deja `App\Http\Controllers\Controller` con las especificaciones de Laravel 10 |

    Si tus controladores existentes usan los métodos de esos traits, puedes elegir entre modificar cada controlador o dejar los traits en la clase base `Controller`. No es necesario cambiarlo todo a la vez para que funcione.

    <Warning>
      Si los controladores de autenticación generados por Breeze o Jetstream llaman a `$this->validate()` o `$this->authorize()`, comprueba siempre su funcionamiento antes de cambiar la clase base.
    </Warning>
  </Step>

  <Step title="Eliminar archivos config innecesarios">
    Los archivos como `config/cors.php`, `config/hashing.php` o `config/view.php` que no hayas modificado respecto al valor por defecto se pueden eliminar. Mantén los que hayas cambiado.
  </Step>

  <Step title="Actualizar public/index.php">
    Ha cambiado para adaptarse a la nueva estructura, así que reescríbelo por completo.

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

    use Illuminate\Foundation\Application;
    use Illuminate\Http\Request;

    define('LARAVEL_START', microtime(true));

    // Determine if the application is in maintenance mode...
    if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
    require $maintenance;
    }

    // Register the Composer autoloader...
    require __DIR__.'/../vendor/autoload.php';

    // Bootstrap Laravel and handle the request...
    /** @var Application $app */
    $app = require_once __DIR__.'/../bootstrap/app.php';

    $app->handleRequest(Request::capture());
    ```
  </Step>

  <Step title="Actualizar artisan">
    Reescribe también el archivo artisan por completo del mismo modo.

    ```php theme={null}
    #!/usr/bin/env php
    <?php

    use Illuminate\Foundation\Application;
    use Symfony\Component\Console\Input\ArgvInput;

    define('LARAVEL_START', microtime(true));

    // Register the Composer autoloader...
    require __DIR__.'/vendor/autoload.php';

    // Bootstrap Laravel and handle the command...
    /** @var Application $app */
    $app = require_once __DIR__.'/bootstrap/app.php';

    $status = $app->handleCommand(new ArgvInput);

    exit($status);
    ```
  </Step>

  <Step title="Actualizar tests/TestCase.php">
    El trait `CreatesApplication` ya no es necesario, así que ajústalo. Puedes borrar `tests/CreatesApplication.php`.

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

    namespace Tests;

    use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

    abstract class TestCase extends BaseTestCase
    {
        //
    }
    ```
  </Step>

  <Step title="Actualizar .env, .env.example y phpunit.xml">
    Se ha renombrado `CACHE_DRIVER` a `CACHE_STORE` y se han añadido algunas entradas, así que ajústalos si es necesario.
    Estos cambios pueden afectar a los archivos de config y al entorno de producción, así que hazlos con cuidado.
    No es imprescindible seguir todos los cambios al pie de la letra.
  </Step>

  <Step title="Comprobación de funcionamiento">
    Cuando termine la migración, comprueba el funcionamiento en este orden.

    ```shell theme={null}
    # Limpiar la caché de configuración
    php artisan config:clear
    php artisan route:clear
    php artisan cache:clear

    # Verificar que las rutas se han registrado correctamente
    php artisan route:list

    # Ejecutar los tests
    php artisan test
    ```

    Si aparecen problemas, restaura los archivos eliminados desde la copia de seguridad y revisa los mensajes de error.
  </Step>
</Steps>

## Estructura de archivos tras la migración

Tras migrar, el proyecto tendrá una estructura similar a esta (se indican las diferencias frente a la estructura antigua).

```
app/
├── Console/
│   └── Commands/          ← mantén tus comandos personalizados
│   (Kernel.php eliminado)
├── Exceptions/
│   (Handler.php eliminado)
├── Http/
│   ├── Controllers/
│   │   └── Controller.php ← cambiado a una clase abstracta simple
│   └── Middleware/        ← mantén tus middlewares personalizados
│   (Kernel.php eliminado)
├── Models/
└── Providers/
    └── AppServiceProvider.php ← consolidado en un único archivo
    (AuthServiceProvider.php eliminado)
    (BroadcastServiceProvider.php eliminado)
    (EventServiceProvider.php eliminado)
    (RouteServiceProvider.php eliminado)
bootstrap/
├── app.php                ← sustituido por la forma Application::configure()
└── providers.php          ← creado
routes/
├── web.php
├── api.php                ← solo si es necesario
└── console.php            ← las definiciones del schedule van aquí
```

## Resumen

| Elemento migrado           | Ubicación antigua                        | Ubicación nueva                                                      |
| -------------------------- | ---------------------------------------- | -------------------------------------------------------------------- |
| Middleware HTTP            | `app/Http/Kernel.php`                    | `withMiddleware()` en `bootstrap/app.php`                            |
| Manejo de excepciones      | `app/Exceptions/Handler.php`             | `withExceptions()` en `bootstrap/app.php`                            |
| Definición del schedule    | `app/Console/Kernel.php`                 | `routes/console.php`                                                 |
| Registro de rutas          | `app/Providers/RouteServiceProvider.php` | `withRouting()` en `bootstrap/app.php`                               |
| Lista de service providers | Array `providers` en `config/app.php`    | `bootstrap/providers.php`                                            |
| Registro de policies       | `app/Providers/AuthServiceProvider.php`  | Registro automático. Registro manual en `AppServiceProvider::boot()` |
| Event listeners            | `app/Providers/EventServiceProvider.php` | Registro automático. Registro manual en `AppServiceProvider::boot()` |

## Enlaces de referencia

* [Estructura de aplicación en Laravel 11 y posteriores](/es/advanced/app-structure)
* [FAQ de la nueva estructura de aplicación](/es/advanced/app-structure-faq)
* [Guía oficial de actualización (11.x)](https://github.com/laravel/docs/blob/11.x/upgrade.md)
* [Diferencias entre laravel/laravel 10.x y 11.x](https://github.com/laravel/laravel/compare/10.x...11.x)
* [Controller.php de Laravel 10](https://github.com/laravel/laravel/blob/10.x/app/Http/Controllers/Controller.php)
* [Controller.php de Laravel 11](https://github.com/laravel/laravel/blob/11.x/app/Http/Controllers/Controller.php)


## Related topics

- [FAQ de la nueva estructura de aplicación de Laravel 11 y posteriores](/es/advanced/app-structure-faq.md)
- [Gestión de la compatibilidad de versiones de paquetes](/es/advanced/package-versioning.md)
- [Guía de migración de laravel/ui a Fortify](/es/blog/ui-to-fortify.md)
- [Actualización de Laravel 10 a 11](/es/blog/upgrade-10-to-11.md)
- [Guía de desarrollo de apps con integración de la Engine API - VOICEVOX for Laravel](/es/packages/laravel-voicevox/app-guide.md)
