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

# Anwendungsstruktur ab Laravel 11

> Das komplette Bild der mit Laravel 11 eingeführten Slim Application Skeleton sowie die interne Implementierung von Application::configure() bis zum ApplicationBuilder.

## Vergleich mit Laravel 10 und älter

Mit Laravel 11 wurde die Anwendungsstruktur als „Slim Application Skeleton" grundlegend überarbeitet. Die größte Änderung: verstreute Einstellungen werden nun an einer einzigen Stelle in `bootstrap/app.php` gebündelt.

| Aspekt            | Laravel 10 und älter                       | Laravel 11 und höher                          |
| ----------------- | ------------------------------------------ | --------------------------------------------- |
| HTTP-Kernel       | `app/Http/Kernel.php`                      | Entfällt (in das Framework integriert)        |
| Console-Kernel    | `app/Console/Kernel.php`                   | Entfällt (in `routes/console.php` verschoben) |
| Exception-Handler | `app/Exceptions/Handler.php`               | Entfällt (in `bootstrap/app.php` gebündelt)   |
| Service Provider  | 5 Dateien                                  | Nur noch `AppServiceProvider.php`             |
| Routen-Dateien    | `web.php` / `api.php` standardmäßig        | Nur `web.php` standard, `api.php` per Opt-in  |
| Bootstrap         | Einstellungen auf mehrere Dateien verteilt | In `bootstrap/app.php` gebündelt              |
| Standard-DB       | MySQL/PostgreSQL                           | SQLite                                        |

<Info>
  Diese Änderung betrifft **neue Projekte**. Bestehende Laravel-10-Anwendungen laufen nach einem Upgrade in ihrer alten Struktur weiter.
</Info>

## Neue Verzeichnis- und Dateistruktur

### Struktur des Skeletons

```
laravel-app/
├── app/
│   ├── Http/
│   │   └── Controllers/
│   ├── Models/
│   │   └── User.php
│   └── Providers/
│       └── AppServiceProvider.php
├── bootstrap/
│   ├── app.php          ← Zentraler Punkt der App-Konfiguration
│   ├── cache/
│   └── providers.php    ← Liste der Service Provider
├── config/
├── database/
├── public/
│   └── index.php        ← Einstiegspunkt
├── resources/
├── routes/
│   ├── web.php          ← Web-Routen (Standard)
│   └── console.php      ← Artisan-Commands und Scheduler
├── storage/
└── tests/
```

### `bootstrap/app.php` — Zentrum der Konfiguration

```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): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
```

Mit dieser einen Datei konfigurieren Sie Routing, Middleware und Exception-Handling. Was in Laravel 10 auf `app/Http/Kernel.php`, `app/Console/Kernel.php` und `app/Exceptions/Handler.php` verteilt war, ist hier zusammengefasst.

### `bootstrap/providers.php` — Liste der Service Provider

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

use App\Providers\AppServiceProvider;

return [
    AppServiceProvider::class,
];
```

Diese Datei registriert die Service Provider. In Laravel 10 stand das im `providers`-Array von `config/app.php`; jetzt ist es in `bootstrap/providers.php` getrennt. Standardmäßig ist nur der `AppServiceProvider` eingetragen.

<Tip>
  Beim `composer require` eines Pakets aktualisiert dieses ggf. `bootstrap/providers.php` automatisch. `config/app.php` wird zwar noch gelesen, aber Neuregistrierungen erfolgen bevorzugt in `bootstrap/providers.php`.
</Tip>

### Änderungen am `routes/`-Verzeichnis

```
routes/
├── web.php      ← Web-Routen (standardmäßig geladen)
└── console.php  ← Artisan-Commands und Scheduler-Definitionen
```

`api.php` und `channels.php` sind standardmäßig nicht vorhanden. Erzeugen Sie sie bei Bedarf per Artisan.

```shell theme={null}
# API-Routen ergänzen (api.php + Sanctum installieren)
php artisan install:api

# Broadcasting ergänzen (channels.php + Reverb installieren)
php artisan install:broadcasting
```

In `routes/console.php` können Sie auch Scheduler-Aufgaben definieren.

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

use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;

Artisan::command('inspire', function () {
    $this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');

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

### Entfallene Dateien

<AccordionGroup>
  <Accordion title="Wegfall von app/Http/Kernel.php">
    Der HTTP-Kernel ist in `Illuminate\Foundation\Http\Kernel` im Framework integriert. Middleware-Anpassungen erfolgen über `withMiddleware()` in `bootstrap/app.php`.

    ```php theme={null}
    // Laravel 10: app/Http/Kernel.php
    protected $middleware = [
        \Illuminate\Http\Middleware\TrustProxies::class,
        // ...
    ];

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            // ...
        ],
    ];
    ```

    ```php theme={null}
    // Laravel 11+: bootstrap/app.php
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->web(append: [
            EnsureUserIsSubscribed::class,
        ]);

        $middleware->validateCsrfTokens(except: ['stripe/*']);
    })
    ```
  </Accordion>

  <Accordion title="Wegfall von app/Console/Kernel.php">
    Die beiden Aufgaben des Console-Kernels sind getrennt: Artisan-Commands liegen in `app/Console/Commands/` und werden automatisch erkannt, Scheduler-Definitionen stehen in `routes/console.php`.

    ```php theme={null}
    // Laravel 10: app/Console/Kernel.php
    protected function schedule(Schedule $schedule): void
    {
        $schedule->command('emails:send')->daily();
    }
    ```

    ```php theme={null}
    // Laravel 11+: routes/console.php
    use Illuminate\Support\Facades\Schedule;

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

  <Accordion title="Wegfall von app/Exceptions/Handler.php">
    Der Exception-Handler ist in `Illuminate\Foundation\Exceptions\Handler` im Framework integriert. Anpassungen erfolgen über `withExceptions()` in `bootstrap/app.php`.

    ```php theme={null}
    // Laravel 10: app/Exceptions/Handler.php
    public function register(): void
    {
        $this->reportable(function (InvalidOrderException $e) {
            // ...
        });
    }
    ```

    ```php theme={null}
    // Laravel 11+: bootstrap/app.php
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->report(function (InvalidOrderException $e) {
            // ...
        });
    })
    ```
  </Accordion>
</AccordionGroup>

## Wie `Application::configure()` funktioniert

### Implementierung im Framework

`Application::configure()` ist eine statische Methode von `Illuminate\Foundation\Application`.

```php theme={null}
// Aus Illuminate\Foundation\Application
public static function configure(?string $basePath = null)
{
    $basePath = match (true) {
        is_string($basePath) => $basePath,
        default => static::inferBasePath(),
    };

    return (new Configuration\ApplicationBuilder(new static($basePath)))
        ->withKernels()
        ->withEvents()
        ->withCommands()
        ->withProviders();
}
```

Die Methode:

1. Bestimmt aus `basePath` das Wurzelverzeichnis der Anwendung.
2. Erzeugt eine `Application`-Instanz.
3. Verpackt sie in einem `ApplicationBuilder` und wendet Standardeinstellungen an.
4. Gibt den `ApplicationBuilder` zurück.

Wichtig: In `configure()` werden **bereits `withKernels()`, `withEvents()`, `withCommands()` und `withProviders()` aufgerufen** — in `bootstrap/app.php` müssen Sie sie nicht erneut aufrufen.

### Konfiguration per Method-Chain

```php theme={null}
Application::configure(basePath: dirname(__DIR__))  // liefert ApplicationBuilder
    ->withRouting(...)       // konfiguriert Routing, gibt $this zurück
    ->withMiddleware(...)    // konfiguriert Middleware, gibt $this zurück
    ->withExceptions(...)    // konfiguriert Exception-Handling, gibt $this zurück
    ->create();              // gibt die Application-Instanz zurück
```

`create()` extrahiert aus dem `ApplicationBuilder` die `Application`; genau diese Instanz gibt `bootstrap/app.php` per `return` zurück.

## Ablauf vom Request bis zum App-Start

```mermaid theme={null}
flowchart TD
    A["public/index.php<br>Einstiegspunkt"] --> B["bootstrap/app.php<br>requiren, Application erhalten"]
    B --> C["Application::configure()<br>erzeugt ApplicationBuilder"]
    C --> D["withKernels()<br>HTTP-/Console-Kernel registrieren"]
    D --> E["withEvents()<br>Event-Discovery einrichten"]
    E --> F["withCommands()<br>Pfad der Artisan-Commands setzen"]
    F --> G["withProviders()<br>bootstrap/providers.php laden"]
    G --> H["withRouting()<br>Routing konfigurieren"]
    H --> I["withMiddleware()<br>Middleware konfigurieren"]
    I --> J["withExceptions()<br>Exception-Handling konfigurieren"]
    J --> K["create()<br>Application-Instanz zurückgeben"]
    K --> L["HTTP-Kernel verarbeitet den Request<br>Middleware → Router → Controller"]
    L --> M["Response zurückgeben"]
```

`public/index.php` ist der Einstiegspunkt; die Datei lädt `bootstrap/app.php` und erhält die `Application`. Danach führt der HTTP-Kernel den Request durch die Middleware-Pipeline, der Router dispatched an den Controller.

## Wichtige Methoden des `ApplicationBuilder` im Detail

### `withRouting()` — interne Routen-Registrierung

```php theme={null}
public function withRouting(
    ?Closure $using = null,
    array|string|null $web = null,
    array|string|null $api = null,
    ?string $commands = null,
    ?string $channels = null,
    ?string $pages = null,
    ?string $health = null,
    string $apiPrefix = 'api',
    ?callable $then = null
)
```

Intern wird an `AppRouteServiceProvider::loadRoutesUsing()` ein Callback registriert; beim Booting wird der `AppRouteServiceProvider` eingehängt.

```php theme={null}
// Vereinfachte interne Verarbeitung von withRouting()
protected function buildRoutingCallback(...)
{
    return function () use ($web, $api, $pages, $health, $apiPrefix, $then) {
        if (is_string($api) || is_array($api)) {
            Route::middleware('api')->prefix($apiPrefix)->group($api);
        }

        if (is_string($health)) {
            Route::get($health, function () {
                Event::dispatch(new DiagnosingHealth);
                return response(View::file(...), status: 200);
            });
        }

        if (is_string($web) || is_array($web)) {
            Route::middleware('web')->group($web);
        }

        if (is_callable($then)) {
            $then($this->app);
        }
    };
}
```

**Wichtige Punkte:**

* Für `api`-Routen werden automatisch die Middleware-Gruppe `api` und der Prefix `/api` angewandt.
* Ein String an `health` registriert einen Health-Check-Endpoint (Default `/up`) automatisch.
* Der `health`-Pfad ist im Wartungsmodus ausgeschlossen (`PreventRequestsDuringMaintenance::except()`).
* `api` wird vor `web` registriert. Bei identischen Pfaden hat `api` Vorrang.
* Ein String an `pages` aktiviert das Routing von [Laravel Folio](https://github.com/laravel/folio).

### `withMiddleware()` — Middleware anpassen

```php theme={null}
public function withMiddleware(?callable $callback = null)
{
    $this->app->afterResolving(HttpKernel::class, function ($kernel) use ($callback) {
        $middleware = (new Middleware)
            ->redirectGuestsTo(fn () => route('login'));

        if (! is_null($callback)) {
            $callback($middleware);
        }

        $kernel->setGlobalMiddleware($middleware->getGlobalMiddleware());
        $kernel->setMiddlewareGroups($middleware->getMiddlewareGroups());
        $kernel->setMiddlewareAliases($middleware->getMiddlewareAliases());
        // ...
    });

    return $this;
}
```

`withMiddleware()` führt den Callback aus, **nachdem** der `HttpKernel` aufgelöst wurde — mittels `afterResolving()`. Dem Callback wird ein `Middleware`-Objekt mit vielen Anpassungsmethoden übergeben.

```php theme={null}
->withMiddleware(function (Middleware $middleware) {
    // Globale Middleware anhängen
    $middleware->append(MyGlobalMiddleware::class);

    // Middleware zur web-Gruppe hinzufügen
    $middleware->web(append: [EnsureUserIsSubscribed::class]);

    // Middleware in der api-Gruppe ersetzen
    $middleware->api(replace: [
        OldMiddleware::class => NewMiddleware::class,
    ]);

    // Pfade vom CSRF-Schutz ausnehmen
    $middleware->validateCsrfTokens(except: ['stripe/*', 'webhook/*']);

    // Weiterleitung für Gäste ändern
    $middleware->redirectGuestsTo('/custom-login');

    // Priorität setzen
    $middleware->priority([
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    ]);
})
```

### `withExceptions()` — Exception-Handling konfigurieren

```php theme={null}
public function withExceptions(?callable $using = null)
{
    $this->app->singleton(
        \Illuminate\Contracts\Debug\ExceptionHandler::class,
        \Illuminate\Foundation\Exceptions\Handler::class
    );

    if ($using !== null) {
        $this->app->afterResolving(
            \Illuminate\Foundation\Exceptions\Handler::class,
            fn ($handler) => $using(new Exceptions($handler)),
        );
    }

    return $this;
}
```

`withExceptions()` registriert die Handler-Klasse als Singleton und setzt den Callback per `afterResolving()`. Dem Callback wird ein `Exceptions`-Wrapper übergeben.

```php theme={null}
->withExceptions(function (Exceptions $exceptions) {
    // Bestimmte Exceptions nicht melden
    $exceptions->dontReport(MissedFlightException::class);

    // Eigenes Reporting für bestimmte Exceptions
    $exceptions->report(function (InvalidOrderException $e) {
        // z. B. Slack benachrichtigen
    });

    // HTTP-Response bestimmter Exceptions anpassen
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json(['message' => 'Not Found'], 404);
        }
    });

    // Throttling (nicht dieselbe Exception in Folge melden)
    $exceptions->throttle(function (Throwable $e) {
        return Limit::perMinute(20);
    });
})
```

### `withProviders()` — Service Provider registrieren

```php theme={null}
public function withProviders(array $providers = [], bool $withBootstrapProviders = true)
{
    RegisterProviders::merge(
        $providers,
        $withBootstrapProviders
            ? $this->app->getBootstrapProvidersPath()
            : null
    );

    return $this;
}
```

Da `withProviders()` innerhalb von `Application::configure()` automatisch aufgerufen wird, lädt Laravel `bootstrap/providers.php` von selbst. Für zusätzliche Provider rufen Sie es in `bootstrap/app.php` explizit auf.

```php theme={null}
Application::configure(basePath: dirname(__DIR__))
    ->withProviders([
        // Zusätzlich zur bootstrap/providers.php Provider hinzufügen
        App\Providers\CustomServiceProvider::class,
    ])
    ->withRouting(...)
    ->create();
```

<Warning>
  `withBootstrapProviders: false` überspringt `bootstrap/providers.php`. Nur mit gutem Grund weglassen.
</Warning>

### Weitere wichtige Methoden

| Methode                              | Beschreibung                                                                                   |
| ------------------------------------ | ---------------------------------------------------------------------------------------------- |
| `withKernels()`                      | Registriert HTTP-/Console-Kernel als Singleton. Wird von `configure()` automatisch aufgerufen. |
| `withEvents()`                       | Aktiviert Event-Discovery. Wird von `configure()` automatisch aufgerufen.                      |
| `withCommands(array $commands)`      | Fügt zusätzliche Artisan-Commands oder Command-Verzeichnisse hinzu.                            |
| `withSchedule(callable $callback)`   | Scheduler-Definitionen in `bootstrap/app.php` ablegen.                                         |
| `withBroadcasting(string $channels)` | Broadcasting-Channels-Datei registrieren.                                                      |
| `withBindings(array $bindings)`      | Container-Bindings registrieren.                                                               |
| `withSingletons(array $singletons)`  | Singleton-Bindings registrieren.                                                               |
| `registered(callable $callback)`     | Callback, nachdem Service Provider registriert wurden.                                         |
| `booting(callable $callback)`        | Callback während des Bootings.                                                                 |
| `booted(callable $callback)`         | Callback nach dem Booting.                                                                     |
| `create()`                           | Gibt die `Application`-Instanz zurück.                                                         |

## Designintention: warum es so ist

### „Code-First"-Konfiguration

In `app/Http/Kernel.php` (Laravel 10) wurde Middleware als Array aufgelistet — eher wie eine Config-Datei. Das erschwerte Typunterstützung und IDE-Feedback.

In Laravel 11 hat sich das zu einem Callback-Stil geändert: `withMiddleware(function (Middleware $middleware) { ... })`. So funktioniert Typvervollständigung, und Sie schreiben Verzweigungen oder Schleifen natürlich für dynamische Konfiguration.

### Von „Convention over Configuration" zu „Explicit Configuration"

`api.php` ist Opt-in geworden, damit Anwendungen ohne API-Routen nicht permanent die `api`-Middleware-Gruppe laden. Was nicht gebraucht wird, existiert per Default nicht.

### Nutzung von `afterResolving()`-Hooks

`withMiddleware()` und `withExceptions()` verwenden `afterResolving()`, um Konfigurationsreihenfolge-Probleme zu vermeiden. Die Methoden am `ApplicationBuilder` werden vor dem vollständigen Start aufgerufen; die tatsächliche Anwendung erfolgt verzögert beim ersten Auflösen des Kernels.

## Praktische Anpassungsbeispiele

### API und Web nebeneinander

```php theme={null}
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        apiPrefix: 'api/v1',  // Standard /api geändert
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
```

### Middleware anpassen

```php theme={null}
->withMiddleware(function (Middleware $middleware) {
    // Authentifizierungs-Check für Web-Routen
    $middleware->web(append: [
        \App\Http\Middleware\EnsureEmailIsVerified::class,
    ]);

    // Bestimmte Middleware für API-Routen entfernen
    $middleware->api(remove: [
        \Illuminate\Session\Middleware\StartSession::class,
    ]);

    // Webhook-Endpoints vom CSRF-Schutz ausnehmen
    $middleware->validateCsrfTokens(except: [
        'webhook/*',
        'stripe/webhook',
    ]);

    // Alias vergeben
    $middleware->alias([
        'subscribed' => \App\Http\Middleware\EnsureUserIsSubscribed::class,
    ]);
})
```

### Scheduler in `bootstrap/app.php` bündeln

Sie können den Scheduler in `routes/console.php` beschreiben — oder alles über `withSchedule()` in `bootstrap/app.php` bündeln.

```php theme={null}
use Illuminate\Console\Scheduling\Schedule;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withSchedule(function (Schedule $schedule) {
        $schedule->command('emails:send')->daily();
        $schedule->command('reports:generate')->weeklyOn(1, '8:00');
        $schedule->job(new PruneOldRecords)->daily();
    })
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
```

### Exception-Handling anpassen

```php theme={null}
->withExceptions(function (Exceptions $exceptions) {
    // API-Requests immer als JSON beantworten
    $exceptions->render(function (Throwable $e, Request $request) {
        if ($request->is('api/*') || $request->wantsJson()) {
            $status = match (true) {
                $e instanceof NotFoundHttpException => 404,
                $e instanceof AuthenticationException => 401,
                $e instanceof AuthorizationException => 403,
                $e instanceof ValidationException => 422,
                default => 500,
            };

            return response()->json([
                'message' => $e->getMessage(),
            ], $status);
        }
    });

    // Nur in Produktion Slack benachrichtigen
    if (app()->isProduction()) {
        $exceptions->report(function (Throwable $e) {
            app(SlackNotifier::class)->notify($e);
        })->stop();
    }
})
```

### Container-Bindings in `bootstrap/app.php` verwalten

In kleinen Anwendungen können Sie einfache Bindings statt im `AppServiceProvider` direkt in `bootstrap/app.php` beschreiben.

```php theme={null}
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withSingletons([
        \App\Contracts\PaymentGateway::class => \App\Services\StripeGateway::class,
        \App\Contracts\MailService::class => \App\Services\SendgridMailService::class,
    ])
    ->withMiddleware(...)
    ->withExceptions(...)
    ->create();
```

## Reihenfolge des Anwendungsstarts

Beim Start der Laravel-Anwendung laufen Service Provider und Application-Hooks in dieser Reihenfolge:

1. `register()` aller Service Provider.
2. `registered()` der Application.
3. `booting()` der Application.
4. `boot()` aller Service Provider.
5. `booted()` der Application.

Mit folgendem Code im `AppServiceProvider` lässt sich das nachverfolgen.

```php theme={null}
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        info('1. AppServiceProvider register');

        $this->booting(function () {
            info('4. AppServiceProvider booting');
        });

        $this->booted(function () {
            info('5. AppServiceProvider booted');
        });

        $this->app->registered(function () {
            info('2. app registered');
        });

        $this->app->booting(function () {
            info('3. app booting');
        });

        $this->app->booted(function () {
            info('6. AppServiceProvider@register app booted');
        });
    }

    public function boot(): void
    {
        $this->app->booted(function () {
            info('7. AppServiceProvider@boot app booted');
        });
    }
}
```

`registered()`, `booting()` und `booted()` am `ApplicationBuilder` registrieren nur Callbacks. Üblicherweise braucht man sie nicht, aber sie erlauben Sonderfälle — etwa Anpassungen am bereits gebooteten Kernel per `booted()`.

```php theme={null}
// bootstrap/app.php

use Illuminate\Contracts\Http\Kernel;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withMiddleware(...)
    ->withExceptions(...)
    ->booted(function (Application $app) {
        $kernel = $app->make(Kernel::class);

        //

        $app->instance(Kernel::class, $kernel);
    })
    ->create();
```

## Nächste Schritte

<Card title="Service Container" icon="box" href="/de/service-container">
  Verstehen Sie den Service Container, den der `ApplicationBuilder` intern nutzt.
</Card>

<Card title="FAQ zur neuen App-Struktur" icon="circle-question" href="/de/advanced/app-structure-faq">
  Häufige Fragen und Antworten zur neuen Anwendungsstruktur.
</Card>


## Related topics

- [FAQ zur neuen App-Struktur ab Laravel 11](/de/advanced/app-structure-faq.md)
- [Migrationsleitfaden von der alten zur neuen Struktur](/de/advanced/app-structure-migration.md)
- [Upgrade von Laravel 10 auf 11](/de/blog/upgrade-10-to-11.md)
- [Upgrade-Anleitung von Laravel 11 auf 12](/de/blog/upgrade-11-to-12.md)
- [Laravel-Tests mit Pest PHP starten](/de/blog/pest-introduction.md)
