# 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 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.