Saltar al contenido principal
En Laravel 11 la estructura de la aplicación cambió notablemente con el llamado «Slim Application Skeleton». Este documento reúne los puntos que suelen resultar confusos cuando te incorporas a un proyecto que se actualizó conservando la estructura antigua o cuando aprendiste con libros y tutoriales de Laravel 10 o anteriores. Son preguntas frecuentes que aparecían en los foros de Laracasts y en Stack Overflow cuando se lanzó Laravel 11.
Este FAQ se centra en proyectos nuevos (Laravel 11 o posterior). Si necesitas actualizar un proyecto existente, consulta la guía de migración.
En la nueva estructura, se han eliminado del proyecto los archivos de config que rara vez se modifican. Para esos archivos se usa el config/ incluido en el framework.Los config/ del proyecto y del framework se fusionan, y el del proyecto tiene prioridad. Cuando necesites personalizar algo, crea el archivo y los cambios se aplicarán.
# Consulta el config del framework
cat vendor/laravel/framework/config/cors.php

# Cópialo al proyecto y personalízalo
php artisan config:publish cors
En la nueva estructura de Laravel 11, App\Http\Controllers\Controller es una clase vacía, no extiende Illuminate\Routing\Controller y no usa los traits ValidatesRequests ni AuthorizesRequests.
Laravel 10Laravel 11
Extiende Illuminate\Routing\ControllerClase vacía
Usa el trait ValidatesRequestsNo
Usa el trait AuthorizesRequestsNo
Referencia: Controller de Laravel 10 vs Controller de Laravel 11Alternativas:
// Usa $request->validate()
public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|string|max:255',
    ]);
}

// Usa Gate::authorize()
use Illuminate\Support\Facades\Gate;

public function update(Request $request, Post $post)
{
    Gate::authorize('update', $post);
}
Si los usas con frecuencia, también puedes restaurar App\Http\Controllers\Controller como en Laravel 10.
<?php

namespace App\Http\Controllers;

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

abstract class Controller extends BaseController
{
    use AuthorizesRequests, ValidatesRequests;
}
En la nueva estructura, app/Http/Kernel.php ha desaparecido. La configuración del middleware se hace mediante withMiddleware() en bootstrap/app.php.
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    // Todo lo que antes se configuraba en middlewares como TrustProxies ahora se hace con métodos de la clase Middleware
    $middleware->trustProxies(at: '*');

    // Añadir middleware global
    $middleware->append(\App\Http\Middleware\MyMiddleware::class);

    // Configurar alias para grupos de rutas
    $middleware->alias([
        'my-middleware' => \App\Http\Middleware\MyMiddleware::class,
    ]);

    // Personalizar los grupos de middleware Web / API
    $middleware->web(append: [
        \App\Http\Middleware\HandleInertiaRequests::class,
    ]);
})
Los archivos de los middlewares personalizados siguen creándose en app/Http/Middleware/.
$this->middleware() era una funcionalidad de Illuminate\Routing\Controller y no está disponible en la nueva clase base vacía.En su lugar, implementa la interfaz HasMiddleware y define el método middleware().
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;

class UserController extends Controller implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('log', only: ['index']),
            new Middleware('subscribed', except: ['index']),
        ];
    }
}
Desde Laravel 13, también puedes utilizar el atributo #[Middleware].
use Illuminate\Routing\Controllers\HasMiddleware;
use App\Http\Middleware\EnsureTokenIsValid;

#[Middleware(EnsureTokenIsValid::class)]
class UserController extends Controller
{
    // ...
}
Referencia: Middleware de controlador
authorizeResource() depende del trait AuthorizesRequests, por lo que no está disponible en la clase base vacía de la nueva estructura.Hay varias formas de resolverlo.1. Devolver la clase base al estilo Laravel 10 (la opción más sencilla)
// app/Http/Controllers/Controller.php
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Routing\Controller as BaseController;

abstract class Controller extends BaseController
{
    use AuthorizesRequests;
}
Así podrás volver a usar $this->authorizeResource().2. Aplicar el atributo #[Authorize] de Laravel 13 a cada método
use Illuminate\Routing\Attributes\Controllers\Authorize;
use App\Models\Post;

class PostController extends Controller
{
    #[Authorize('view', 'post')]
    public function show(Post $post) { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post) { /* ... */ }
}
En la nueva estructura, app/Console/Kernel.php ha desaparecido y el sitio para configurar el schedule ha cambiado.Definirlo en routes/console.php (recomendado):
// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily();
Schedule::job(new SendEmails)->everyFiveMinutes();
Definirlo en bootstrap/app.php:
->withSchedule(function (Schedule $schedule) {
    $schedule->command('emails:send')->daily();
})
En la nueva estructura, EventServiceProvider ha desaparecido y ya no es necesario registrar manualmente los eventos y sus listeners.Cómo funciona el registro automático: basta con declarar el tipo del evento en el argumento del método handle() del listener para que se registre automáticamente.
namespace App\Listeners;

use App\Events\OrderShipped;

class SendShipmentNotification
{
    // Con solo declarar la clase del evento como argumento de handle() se registra automáticamente
    public function handle(OrderShipped $event): void
    {
        // ...
    }
}
Si necesitas registrarlo manualmente, hazlo en AppServiceProvider::boot().
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(
        OrderShipped::class,
        SendShipmentNotification::class,
    );
}
En la nueva estructura, la lista de service providers se gestiona desde bootstrap/providers.php en lugar de config/app.php.
// bootstrap/providers.php
return [
    App\Providers\AppServiceProvider::class,
    App\Providers\MyCustomServiceProvider::class, // ← añádelo aquí
];
Los providers creados con artisan make:provider se añaden aquí automáticamente.
En la nueva estructura, app/Exceptions/Handler.php ha desaparecido y el manejo de excepciones se configura con withExceptions() en bootstrap/app.php.
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    // No reportar una excepción concreta
    $exceptions->dontReport(InvalidOrderException::class);

    // Renderizado personalizado
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });

    // Cambiar el destino de la redirección cuando no está autenticado
    $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
        return $request->is('api/*');
    });
})
La personalización de las rutas se hace con withRouting() en bootstrap/app.php.Añadir archivos de rutas:
->withRouting(
    web: __DIR__.'/../routes/web.php',
    then: function () {
        Route::middleware('web')
            ->prefix('admin')
            ->group(base_path('routes/admin.php'));
    },
)
Control completo (equivalente al RouteServiceProvider de Laravel 10):Si especificas using, el registro de rutas por defecto de Laravel queda completamente desactivado y podrás controlar todo por tu cuenta.
->withRouting(
    using: function () {
        Route::middleware('web')
            ->group(base_path('routes/web.php'));

        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));
    },
)
Si añades una clave providers en config/app.php, se fusionará con el config/app.php interno del framework y surtirá efecto.
// config/app.php
use Illuminate\Support\ServiceProvider;

return [
    // ...
    'providers' => ServiceProvider::defaultProviders()->replace([
        Illuminate\Foo\FooServiceProvider::class => Bar\BarServiceProvider::class,
    ])->toArray(),
];
Con el método replace() puedes sustituir un provider por defecto por tu propia implementación.
Desde Laravel 11, las funciones de API no están incluidas por defecto; se instalan de forma individual cuando se necesitan.
php artisan install:api
Este comando crea y configura lo siguiente:
  • routes/api.php
  • Configuración de Laravel Sanctum para la autenticación de la API.
  • Registro de las rutas de API en bootstrap/app.php.
Desde Laravel 11, la funcionalidad de broadcasting tampoco viene incluida por defecto; se instala aparte cuando se necesita.
php artisan install:broadcasting
Este comando crea y configura lo siguiente:
  • routes/channels.php
  • Configuración de instalación de Laravel Reverb.
  • Archivo de configuración de broadcasting.
Revisa el contenido de bootstrap/app.php.Nueva estructura de Laravel 11 (o ya migrada):
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withMiddleware(...)
    ->withExceptions(...)
    ->create();
Proyecto de Laravel 10 o anterior actualizado sin migrar la estructura:
// bootstrap/app.php
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
// ...
Si tiene el formato return Application::configure(..., es la nueva estructura; en caso contrario, es un proyecto actualizado manteniendo la estructura antigua. Para migrar un proyecto de la estructura antigua a la nueva, consulta la guía de migración.

Páginas relacionadas

Estructura de aplicación en Laravel 11 y posteriores

Explica en detalle la nueva estructura de la aplicación y la implementación interna de ApplicationBuilder.

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

Cubre los pasos para migrar un proyecto desde la estructura antigua de Laravel 10 a la nueva estructura de Laravel 11 y posteriores.
Última modificación el 13 de julio de 2026