# Controller-Anleitung

Diese Anleitung erklärt, wie Controller im Framework funktionieren und wie Sie sie effektiv nutzen können.

## Was sind Controller?

Controller sind Klassen, die für die Verarbeitung von HTTP-Anfragen verantwortlich sind. Sie nehmen Anfragen entgegen, führen die erforderliche Geschäftslogik aus und geben Antworten zurück. Controller dienen als Vermittler zwischen den Modellen (Daten und Geschäftslogik) und den Views (Präsentationsschicht).

## Grundlegende Controller

### Erstellen eines Controllers

Controller werden im Verzeichnis `src/Application/Controllers` gespeichert. Ein einfacher Controller sieht wie folgt aus:

```php
<?php

namespace App\Application\Controllers;

use App\Framework\Http\Controller;
use App\Framework\Http\Request;
use App\Framework\Http\Response;

class UserController extends Controller
{
    public function index(Request $request): Response
    {
        return $this->view('users.index', [
            'users' => User::all()
        ]);
    }
    
    public function show(Request $request, int $id): Response
    {
        $user = User::find($id);
        
        if (!$user) {
            return $this->notFound('Benutzer nicht gefunden');
        }
        
        return $this->view('users.show', [
            'user' => $user
        ]);
    }
}
```

### Controller-Basisklasse

Alle Controller sollten von der `App\Framework\Http\Controller`-Klasse erben, die nützliche Methoden für die Erstellung von Antworten bereitstellt:

```php
// Gibt eine View zurück
$this->view('users.index', ['users' => $users]);

// Gibt JSON zurück
$this->json(['name' => 'John', 'email' => 'john@example.com']);

// Leitet zu einer anderen URL um
$this->redirect('/users');

// Leitet zurück zur vorherigen Seite um
$this->back();

// Gibt einen Fehler zurück
$this->error('Ein Fehler ist aufgetreten', 500);

// Gibt einen 404-Fehler zurück
$this->notFound('Seite nicht gefunden');

// Gibt einen 403-Fehler zurück
$this->forbidden('Zugriff verweigert');

// Gibt eine leere Antwort zurück
$this->noContent();
```

## Anfragen verarbeiten

### Zugriff auf Anfragedaten

Sie können auf die Daten einer Anfrage über das `Request`-Objekt zugreifen:

```php
public function store(Request $request): Response
{
    // Zugriff auf Formulardaten
    $name = $request->input('name');
    $email = $request->input('email');
    
    // Zugriff auf Formulardaten mit Standardwert
    $age = $request->input('age', 18);
    
    // Prüfen, ob ein Feld vorhanden ist
    if ($request->has('address')) {
        // ...
    }
    
    // Zugriff auf alle Formulardaten
    $data = $request->all();
    
    // Zugriff auf bestimmte Formulardaten
    $userData = $request->only(['name', 'email']);
    $nonUserData = $request->except(['name', 'email']);
    
    // Zugriff auf URL-Parameter
    $page = $request->query('page', 1);
    
    // Zugriff auf Dateien
    $file = $request->file('avatar');
    
    // Zugriff auf Header
    $token = $request->header('Authorization');
    
    // Zugriff auf Cookies
    $remember = $request->cookie('remember', false);
    
    // Zugriff auf die Anfragemethode
    $method = $request->method();
    
    // Prüfen, ob die Anfrage eine AJAX-Anfrage ist
    if ($request->isAjax()) {
        // ...
    }
    
    // Prüfen, ob die Anfrage eine bestimmte Methode verwendet
    if ($request->isMethod('POST')) {
        // ...
    }
    
    // Zugriff auf die Anfrage-URL
    $url = $request->url();
    
    // Zugriff auf die vollständige Anfrage-URL mit Abfrageparametern
    $fullUrl = $request->fullUrl();
    
    // Zugriff auf den Pfad der Anfrage
    $path = $request->path();
}
```

### Validierung von Anfragedaten

Sie können Anfragedaten direkt im Controller validieren:

```php
public function store(Request $request): Response
{
    $this->validate($request, [
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users,email',
        'password' => 'required|string|min:8|confirmed',
    ]);
    
    // Die Validierung wurde bestanden, fahren Sie mit der Speicherung fort
    $user = User::create($request->only(['name', 'email', 'password']));
    
    return $this->redirect('/users')->with('success', 'Benutzer erstellt');
}
```

Wenn die Validierung fehlschlägt, wird automatisch eine Antwort mit den Validierungsfehlern zurückgegeben. Bei einer regulären Anfrage wird der Benutzer zur vorherigen Seite umgeleitet, bei einer AJAX-Anfrage wird eine JSON-Antwort mit den Fehlern zurückgegeben.

Weitere Informationen zur Validierung finden Sie in der [Validierungs-Anleitung](validation.md).

## Antworten erstellen

### HTML-Antworten

Um eine HTML-Antwort zu erstellen, verwenden Sie die `view`-Methode:

```php
public function index(): Response
{
    $users = User::all();
    
    return $this->view('users.index', [
        'users' => $users
    ]);
}
```

Die `view`-Methode akzeptiert den Namen der View und ein Array mit Daten, die an die View übergeben werden sollen. Der Name der View entspricht dem Pfad der View-Datei relativ zum Verzeichnis `resources/views`, wobei Punkte als Verzeichnistrennzeichen verwendet werden. Im obigen Beispiel wird die Datei `resources/views/users/index.php` gerendert.

### JSON-Antworten

Um eine JSON-Antwort zu erstellen, verwenden Sie die `json`-Methode:

```php
public function index(): Response
{
    $users = User::all();
    
    return $this->json([
        'users' => $users
    ]);
}
```

Die `json`-Methode akzeptiert ein Array oder ein Objekt, das in JSON konvertiert werden soll, und einen optionalen HTTP-Statuscode:

```php
return $this->json(['error' => 'Nicht gefunden'], 404);
```

### Weiterleitungen

Um eine Weiterleitung zu erstellen, verwenden Sie die `redirect`-Methode:

```php
public function store(Request $request): Response
{
    // Benutzer erstellen
    
    return $this->redirect('/users');
}
```

Sie können auch zur vorherigen Seite umleiten:

```php
public function store(Request $request): Response
{
    // Validierungsfehler
    
    return $this->back();
}
```

Sie können Flash-Daten mit der Weiterleitung senden:

```php
return $this->redirect('/users')
    ->with('success', 'Benutzer erstellt');
```

Diese Flash-Daten sind in der nächsten Anfrage über die Session verfügbar:

```php
$message = $request->session()->get('success');
```

### Fehlerantworten

Um eine Fehlerantwort zu erstellen, verwenden Sie eine der Fehlermethoden:

```php
// 404 Not Found
return $this->notFound('Benutzer nicht gefunden');

// 403 Forbidden
return $this->forbidden('Sie haben keine Berechtigung, diesen Benutzer zu bearbeiten');

// 401 Unauthorized
return $this->unauthorized('Bitte melden Sie sich an');

// 400 Bad Request
return $this->badRequest('Ungültige Anfrage');

// 500 Internal Server Error
return $this->error('Ein Fehler ist aufgetreten');
```

### Datei-Downloads

Um eine Datei zum Download anzubieten, verwenden Sie die `download`-Methode:

```php
public function download(int $id): Response
{
    $file = File::find($id);
    
    return $this->download($file->path, $file->name);
}
```

### Datei-Streams

Um eine Datei zu streamen, verwenden Sie die `stream`-Methode:

```php
public function stream(int $id): Response
{
    $video = Video::find($id);
    
    return $this->stream($video->path, $video->mime_type);
}
```

## Dependency Injection

Das Framework unterstützt Dependency Injection in Controller-Konstruktoren und -Methoden:

```php
class UserController extends Controller
{
    private UserRepository $userRepository;
    
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }
    
    public function index(Request $request, Logger $logger): Response
    {
        $logger->info('Benutzerindex aufgerufen');
        
        $users = $this->userRepository->all();
        
        return $this->view('users.index', [
            'users' => $users
        ]);
    }
}
```

## Middleware

Sie können Middleware auf Controller-Ebene anwenden, indem Sie die `middleware`-Methode im Konstruktor aufrufen:

```php
class AdminController extends Controller
{
    public function __construct()
    {
        $this->middleware(AuthMiddleware::class);
        $this->middleware(AdminMiddleware::class);
    }
    
    // ...
}
```

Sie können die Middleware auch auf bestimmte Methoden beschränken:

```php
public function __construct()
{
    $this->middleware(AuthMiddleware::class)->only(['index', 'show']);
    $this->middleware(AdminMiddleware::class)->except(['index', 'show']);
}
```

## Ressourcen-Controller

Ressourcen-Controller bieten eine bequeme Möglichkeit, CRUD-Operationen für eine Ressource zu implementieren. Ein Ressourcen-Controller enthält die folgenden Methoden:

- `index`: Zeigt eine Liste aller Ressourcen an
- `create`: Zeigt ein Formular zum Erstellen einer neuen Ressource an
- `store`: Speichert eine neu erstellte Ressource
- `show`: Zeigt eine bestimmte Ressource an
- `edit`: Zeigt ein Formular zum Bearbeiten einer Ressource an
- `update`: Aktualisiert eine bestimmte Ressource
- `destroy`: Löscht eine bestimmte Ressource

Hier ist ein Beispiel für einen Ressourcen-Controller:

```php
class PhotoController extends Controller
{
    public function index(): Response
    {
        $photos = Photo::all();
        
        return $this->view('photos.index', [
            'photos' => $photos
        ]);
    }
    
    public function create(): Response
    {
        return $this->view('photos.create');
    }
    
    public function store(Request $request): Response
    {
        $this->validate($request, [
            'title' => 'required|string|max:255',
            'image' => 'required|image|max:2048',
        ]);
        
        $photo = new Photo();
        $photo->title = $request->input('title');
        $photo->path = $request->file('image')->store('photos');
        $photo->save();
        
        return $this->redirect('/photos')->with('success', 'Foto hochgeladen');
    }
    
    public function show(int $id): Response
    {
        $photo = Photo::find($id);
        
        if (!$photo) {
            return $this->notFound('Foto nicht gefunden');
        }
        
        return $this->view('photos.show', [
            'photo' => $photo
        ]);
    }
    
    public function edit(int $id): Response
    {
        $photo = Photo::find($id);
        
        if (!$photo) {
            return $this->notFound('Foto nicht gefunden');
        }
        
        return $this->view('photos.edit', [
            'photo' => $photo
        ]);
    }
    
    public function update(Request $request, int $id): Response
    {
        $photo = Photo::find($id);
        
        if (!$photo) {
            return $this->notFound('Foto nicht gefunden');
        }
        
        $this->validate($request, [
            'title' => 'required|string|max:255',
        ]);
        
        $photo->title = $request->input('title');
        $photo->save();
        
        return $this->redirect('/photos')->with('success', 'Foto aktualisiert');
    }
    
    public function destroy(int $id): Response
    {
        $photo = Photo::find($id);
        
        if (!$photo) {
            return $this->notFound('Foto nicht gefunden');
        }
        
        $photo->delete();
        
        return $this->redirect('/photos')->with('success', 'Foto gelöscht');
    }
}
```

Um einen Ressourcen-Controller zu registrieren, verwenden Sie die `resource`-Methode des Routers:

```php
$router->resource('photos', PhotoController::class);
```

## API-Controller

API-Controller sind ähnlich wie reguläre Controller, geben jedoch in der Regel JSON-Antworten zurück:

```php
class ApiUserController extends Controller
{
    public function index(): Response
    {
        $users = User::all();
        
        return $this->json([
            'data' => $users
        ]);
    }
    
    public function show(int $id): Response
    {
        $user = User::find($id);
        
        if (!$user) {
            return $this->json([
                'error' => 'Benutzer nicht gefunden'
            ], 404);
        }
        
        return $this->json([
            'data' => $user
        ]);
    }
    
    public function store(Request $request): Response
    {
        $this->validate($request, [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
        ]);
        
        $user = User::create($request->only(['name', 'email', 'password']));
        
        return $this->json([
            'data' => $user,
            'message' => 'Benutzer erstellt'
        ], 201);
    }
    
    public function update(Request $request, int $id): Response
    {
        $user = User::find($id);
        
        if (!$user) {
            return $this->json([
                'error' => 'Benutzer nicht gefunden'
            ], 404);
        }
        
        $this->validate($request, [
            'name' => 'string|max:255',
            'email' => 'email|unique:users,email,' . $id,
        ]);
        
        $user->fill($request->only(['name', 'email']));
        $user->save();
        
        return $this->json([
            'data' => $user,
            'message' => 'Benutzer aktualisiert'
        ]);
    }
    
    public function destroy(int $id): Response
    {
        $user = User::find($id);
        
        if (!$user) {
            return $this->json([
                'error' => 'Benutzer nicht gefunden'
            ], 404);
        }
        
        $user->delete();
        
        return $this->json([
            'message' => 'Benutzer gelöscht'
        ]);
    }
}
```

## Single Action Controller

Wenn ein Controller nur eine einzige Aktion ausführt, können Sie einen Single Action Controller erstellen, der die `__invoke`-Methode implementiert:

```php
class ShowDashboardController extends Controller
{
    public function __invoke(Request $request): Response
    {
        $stats = [
            'users' => User::count(),
            'posts' => Post::count(),
            'comments' => Comment::count(),
        ];
        
        return $this->view('dashboard', [
            'stats' => $stats
        ]);
    }
}
```

Um einen Single Action Controller zu registrieren, geben Sie einfach die Controller-Klasse an:

```php
$router->get('/dashboard', ShowDashboardController::class);
```

## Controller-Organisation

### Namensräume

Sie können Controller in Namensräumen organisieren, um sie besser zu strukturieren:

```php
namespace App\Application\Controllers\Admin;

class UserController extends Controller
{
    // ...
}
```

### Controller-Gruppen

Sie können Controller in Gruppen organisieren, indem Sie Routengruppen mit einem Namensraum verwenden:

```php
$router->group('/admin', function (Router $router) {
    $router->get('/users', [UserController::class, 'index']);
    $router->get('/posts', [PostController::class, 'index']);
})->namespace('App\\Application\\Controllers\\Admin');
```

## Beste Praktiken

### Schlanke Controller

Controller sollten schlank sein und sich auf die Verarbeitung von HTTP-Anfragen konzentrieren. Komplexe Geschäftslogik sollte in Service-Klassen oder Modelle ausgelagert werden:

```php
class UserController extends Controller
{
    private UserService $userService;
    
    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }
    
    public function store(Request $request): Response
    {
        $this->validate($request, [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
        ]);
        
        $user = $this->userService->createUser(
            $request->input('name'),
            $request->input('email'),
            $request->input('password')
        );
        
        return $this->redirect('/users')->with('success', 'Benutzer erstellt');
    }
}
```

### Wiederverwendung von Validierungsregeln

Wenn Sie dieselben Validierungsregeln in mehreren Controllern verwenden, sollten Sie sie in eine separate Klasse auslagern:

```php
class UserValidationRules
{
    public static function forCreation(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
        ];
    }
    
    public static function forUpdate(int $userId): array
    {
        return [
            'name' => 'string|max:255',
            'email' => 'email|unique:users,email,' . $userId,
        ];
    }
}
```

Dann können Sie diese Regeln in Ihren Controllern verwenden:

```php
public function store(Request $request): Response
{
    $this->validate($request, UserValidationRules::forCreation());
    
    // ...
}

public function update(Request $request, int $id): Response
{
    $this->validate($request, UserValidationRules::forUpdate($id));
    
    // ...
}
```

### Verwendung von Form Requests

Für komplexe Validierungslogik können Sie Form Request-Klassen erstellen:

```php
class CreateUserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
        ];
    }
    
    public function messages(): array
    {
        return [
            'name.required' => 'Der Name ist erforderlich',
            'email.unique' => 'Diese E-Mail-Adresse wird bereits verwendet',
        ];
    }
    
    public function authorize(): bool
    {
        return $this->user()->hasPermission('create-users');
    }
}
```

Dann können Sie die Form Request-Klasse in Ihrem Controller verwenden:

```php
public function store(CreateUserRequest $request): Response
{
    // Die Validierung wurde bereits durchgeführt
    
    $user = User::create($request->validated());
    
    return $this->redirect('/users')->with('success', 'Benutzer erstellt');
}
```

## Weitere Informationen

- [Routing-Anleitung](routing.md): Erfahren Sie mehr über das Routing-System und wie es mit Controllern interagiert.
- [Validierungs-Anleitung](validation.md): Erfahren Sie mehr über die Validierung von Benutzereingaben.
- [Middleware-Anleitung](middleware.md): Erfahren Sie mehr über Middleware und wie sie mit Controllern interagiert.
- [Architekturübersicht](../architecture/overview.md): Überblick über die Architektur des Frameworks.
