Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

727
docs/guides/controllers.md Normal file
View File

@@ -0,0 +1,727 @@
# 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.

370
docs/guides/routing.md Normal file
View File

@@ -0,0 +1,370 @@
# Routing-Anleitung
Diese Anleitung erklärt, wie das Routing-System des Frameworks funktioniert und wie Sie es effektiv nutzen können.
## Grundlagen des Routings
Das Routing-System ist verantwortlich für das Mapping von HTTP-Anfragen zu Controller-Aktionen. Es ermöglicht Ihnen, URLs zu definieren und festzulegen, welcher Code ausgeführt werden soll, wenn diese URLs aufgerufen werden.
### Routing-Konfiguration
Die Routing-Konfiguration befindet sich in der Datei `config/routes.php`. Diese Datei gibt eine Funktion zurück, die einen Router als Parameter erhält:
```php
<?php
use App\Framework\Routing\Router;
use App\Application\Controllers\HomeController;
use App\Application\Controllers\UserController;
return function (Router $router) {
// Hier werden Routen definiert
$router->get('/', [HomeController::class, 'index']);
$router->get('/users', [UserController::class, 'index']);
$router->get('/users/{id}', [UserController::class, 'show']);
};
```
## Definieren von Routen
### Grundlegende Routen
Sie können Routen für verschiedene HTTP-Methoden definieren:
```php
// GET-Route
$router->get('/users', [UserController::class, 'index']);
// POST-Route
$router->post('/users', [UserController::class, 'store']);
// PUT-Route
$router->put('/users/{id}', [UserController::class, 'update']);
// PATCH-Route
$router->patch('/users/{id}', [UserController::class, 'update']);
// DELETE-Route
$router->delete('/users/{id}', [UserController::class, 'destroy']);
// Mehrere HTTP-Methoden
$router->match(['GET', 'POST'], '/users/search', [UserController::class, 'search']);
// Alle HTTP-Methoden
$router->any('/users/all', [UserController::class, 'all']);
```
### Routenparameter
Sie können dynamische Segmente in Ihren Routen definieren, indem Sie geschweifte Klammern verwenden:
```php
$router->get('/users/{id}', [UserController::class, 'show']);
$router->get('/posts/{post}/comments/{comment}', [CommentController::class, 'show']);
```
Diese Parameter werden automatisch an die Controller-Methode übergeben:
```php
public function show(Request $request, int $id): Response
{
$user = User::find($id);
// ...
}
public function show(Request $request, int $post, int $comment): Response
{
$post = Post::find($post);
$comment = Comment::find($comment);
// ...
}
```
### Optionale Parameter
Sie können optionale Parameter definieren, indem Sie ein Fragezeichen nach dem Parameternamen hinzufügen:
```php
$router->get('/users/{id?}', [UserController::class, 'show']);
```
In diesem Fall müssen Sie in Ihrer Controller-Methode einen Standardwert für den Parameter angeben:
```php
public function show(Request $request, int $id = null): Response
{
if ($id === null) {
// Alle Benutzer anzeigen
} else {
// Einen bestimmten Benutzer anzeigen
}
// ...
}
```
### Parametereinschränkungen
Sie können Einschränkungen für Routenparameter definieren, um sicherzustellen, dass sie einem bestimmten Muster entsprechen:
```php
$router->get('/users/{id}', [UserController::class, 'show'])
->where('id', '[0-9]+');
$router->get('/posts/{slug}', [PostController::class, 'show'])
->where('slug', '[a-z0-9-]+');
$router->get('/categories/{category}/posts/{post}', [PostController::class, 'showInCategory'])
->where(['category' => '[a-z0-9-]+', 'post' => '[0-9]+']);
```
### Benannte Routen
Sie können Routen benennen, um später einfacher auf sie verweisen zu können:
```php
$router->get('/users/{id}', [UserController::class, 'show'])
->name('users.show');
```
Sie können dann URLs für benannte Routen generieren:
```php
$url = route('users.show', ['id' => 1]); // /users/1
```
## Routengruppen
Routengruppen ermöglichen es Ihnen, gemeinsame Attribute auf mehrere Routen anzuwenden, ohne sie für jede Route einzeln definieren zu müssen.
### Präfixe
Sie können ein Präfix für eine Gruppe von Routen definieren:
```php
$router->group('/admin', function (Router $router) {
$router->get('/dashboard', [AdminController::class, 'dashboard']);
$router->get('/users', [AdminController::class, 'users']);
$router->get('/settings', [AdminController::class, 'settings']);
});
```
Dies definiert die folgenden Routen:
- `/admin/dashboard`
- `/admin/users`
- `/admin/settings`
### Middleware
Sie können Middleware für eine Gruppe von Routen definieren:
```php
$router->group('', function (Router $router) {
$router->get('/dashboard', [AdminController::class, 'dashboard']);
$router->get('/users', [AdminController::class, 'users']);
$router->get('/settings', [AdminController::class, 'settings']);
})->middleware(AuthMiddleware::class);
```
Sie können auch mehrere Middleware-Klassen angeben:
```php
$router->group('', function (Router $router) {
// Routen
})->middleware([AuthMiddleware::class, AdminMiddleware::class]);
```
### Namensräume
Sie können einen Namensraum für eine Gruppe von Routen definieren:
```php
$router->group('', function (Router $router) {
$router->get('/users', ['UserController', 'index']);
$router->get('/posts', ['PostController', 'index']);
})->namespace('App\\Application\\Controllers\\Admin');
```
In diesem Fall werden die Controller-Klassen im Namensraum `App\Application\Controllers\Admin` gesucht, also `App\Application\Controllers\Admin\UserController` und `App\Application\Controllers\Admin\PostController`.
### Kombinierte Attribute
Sie können mehrere Attribute für eine Gruppe von Routen kombinieren:
```php
$router->group('/admin', function (Router $router) {
$router->get('/dashboard', ['DashboardController', 'index']);
$router->get('/users', ['UserController', 'index']);
})
->namespace('App\\Application\\Controllers\\Admin')
->middleware([AuthMiddleware::class, AdminMiddleware::class])
->name('admin.');
```
Dies definiert die folgenden Routen:
- `/admin/dashboard` mit dem Namen `admin.dashboard`
- `/admin/users` mit dem Namen `admin.users`
Beide Routen verwenden die Controller im Namensraum `App\Application\Controllers\Admin` und durchlaufen die `AuthMiddleware` und `AdminMiddleware`.
## Verschachtelte Gruppen
Sie können Routengruppen verschachteln, um komplexere Strukturen zu erstellen:
```php
$router->group('/admin', function (Router $router) {
$router->get('/dashboard', [AdminController::class, 'dashboard']);
$router->group('/users', function (Router $router) {
$router->get('/', [AdminUserController::class, 'index']);
$router->get('/{id}', [AdminUserController::class, 'show']);
$router->put('/{id}', [AdminUserController::class, 'update']);
$router->delete('/{id}', [AdminUserController::class, 'destroy']);
});
$router->group('/settings', function (Router $router) {
$router->get('/', [AdminSettingsController::class, 'index']);
$router->put('/general', [AdminSettingsController::class, 'updateGeneral']);
$router->put('/security', [AdminSettingsController::class, 'updateSecurity']);
});
});
```
## Fallback-Routen
Sie können eine Fallback-Route definieren, die verwendet wird, wenn keine andere Route übereinstimmt:
```php
$router->fallback([NotFoundController::class, 'index']);
```
## Middleware für einzelne Routen
Sie können Middleware auch für einzelne Routen definieren:
```php
$router->get('/profile', [ProfileController::class, 'index'])
->middleware(AuthMiddleware::class);
$router->get('/admin/dashboard', [AdminController::class, 'dashboard'])
->middleware([AuthMiddleware::class, AdminMiddleware::class]);
```
## Routen-Caching
Um die Leistung zu verbessern, können Sie die Routing-Konfiguration cachen:
```bash
php console.php route:cache
```
Dies erstellt eine Cache-Datei, die vom Framework verwendet wird, anstatt die Routen bei jeder Anfrage neu zu analysieren.
Um den Cache zu löschen:
```bash
php console.php route:clear
```
## Routen auflisten
Sie können alle registrierten Routen auflisten:
```bash
php console.php route:list
```
Dies zeigt eine Tabelle mit allen Routen, ihren HTTP-Methoden, URLs, Controller-Aktionen und Namen an.
## Routen-Modell-Binding
Das Framework unterstützt Routen-Modell-Binding, das automatisch Modellinstanzen aus Routenparametern auflöst:
```php
$router->get('/users/{user}', [UserController::class, 'show']);
```
In Ihrem Controller:
```php
public function show(Request $request, User $user): Response
{
// $user ist bereits eine Instanz des User-Modells
return $this->view('users.show', ['user' => $user]);
}
```
Standardmäßig wird der Parameter `{user}` als ID verwendet, um das Modell zu finden. Sie können dieses Verhalten anpassen, indem Sie die Methode `resolveRouteBinding` in Ihrem Modell überschreiben:
```php
public function resolveRouteBinding(string $value): ?static
{
return static::where('username', $value)->first();
}
```
## Ressourcen-Routen
Das Framework bietet eine Möglichkeit, schnell Ressourcen-Routen für CRUD-Operationen zu definieren:
```php
$router->resource('photos', PhotoController::class);
```
Dies erstellt die folgenden Routen:
| HTTP-Methode | URI | Aktion | Routenname |
|--------------|-------------------|----------|----------------|
| GET | /photos | index | photos.index |
| GET | /photos/create | create | photos.create |
| POST | /photos | store | photos.store |
| GET | /photos/{photo} | show | photos.show |
| GET | /photos/{photo}/edit | edit | photos.edit |
| PUT/PATCH | /photos/{photo} | update | photos.update |
| DELETE | /photos/{photo} | destroy | photos.destroy |
Sie können die generierten Routen einschränken:
```php
$router->resource('photos', PhotoController::class)
->only(['index', 'show']);
$router->resource('photos', PhotoController::class)
->except(['create', 'store', 'update', 'destroy']);
```
## API-Ressourcen-Routen
Für APIs können Sie API-Ressourcen-Routen definieren, die keine `create` und `edit` Routen enthalten:
```php
$router->apiResource('photos', PhotoApiController::class);
```
Dies erstellt die folgenden Routen:
| HTTP-Methode | URI | Aktion | Routenname |
|--------------|------------------|----------|----------------|
| GET | /photos | index | photos.index |
| POST | /photos | store | photos.store |
| GET | /photos/{photo} | show | photos.show |
| PUT/PATCH | /photos/{photo} | update | photos.update |
| DELETE | /photos/{photo} | destroy | photos.destroy |
## Verschachtelte Ressourcen
Sie können auch verschachtelte Ressourcen definieren:
```php
$router->resource('photos.comments', PhotoCommentController::class);
```
Dies erstellt Routen wie:
- `/photos/{photo}/comments`
- `/photos/{photo}/comments/{comment}`
## Weitere Informationen
- [Controller-Anleitung](controllers.md): Erfahren Sie mehr über Controller und wie sie mit Routen interagieren.
- [Middleware-Anleitung](middleware.md): Erfahren Sie mehr über Middleware und wie sie im Routing-System verwendet werden.
- [Validierungs-Anleitung](validation.md): Erfahren Sie, wie Sie Benutzereingaben validieren können.
- [Architekturübersicht](../architecture/overview.md): Überblick über die Architektur des Frameworks.

585
docs/guides/security.md Normal file
View File

@@ -0,0 +1,585 @@
# Sicherheits-Anleitung
Diese Anleitung erklärt die Sicherheitsfunktionen des Frameworks und wie Sie sie effektiv nutzen können, um Ihre Anwendung zu schützen.
## Überblick über die Sicherheitsarchitektur
Das Framework bietet eine mehrschichtige Sicherheitsarchitektur, die verschiedene Schutzmaßnahmen auf verschiedenen Ebenen implementiert:
1. **Eingabevalidierung**: Validierung aller Benutzereingaben, um Injection-Angriffe zu verhindern.
2. **Ausgabebereinigung**: Automatische Bereinigung von Ausgaben, um Cross-Site Scripting (XSS) zu verhindern.
3. **CSRF-Schutz**: Schutz vor Cross-Site Request Forgery.
4. **Authentifizierung**: Flexible Authentifizierungsmechanismen.
5. **Autorisierung**: Rollenbasierte und fähigkeitsbasierte Zugriffskontrolle.
6. **Web Application Firewall (WAF)**: Integrierte WAF zum Schutz vor gängigen Angriffen.
7. **Sicherheitsheader**: Automatische Konfiguration von Sicherheitsheadern.
8. **Sichere Konfiguration**: Sichere Standardeinstellungen und Best Practices.
## Eingabevalidierung
Die Eingabevalidierung ist die erste Verteidigungslinie gegen Injection-Angriffe. Das Framework bietet ein leistungsstarkes Validierungssystem, das in der [Validierungs-Anleitung](validation.md) ausführlich beschrieben wird.
### Beispiel für die Validierung von Benutzereingaben
```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',
'website' => 'nullable|url',
'age' => 'nullable|integer|min:18',
]);
// 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');
}
```
### Schutz vor SQL-Injection
Das Framework verwendet parametrisierte Abfragen und Prepared Statements, um SQL-Injection-Angriffe zu verhindern:
```php
// Sicher: Verwendung von parametrisierten Abfragen
$users = DB::table('users')
->where('email', $request->input('email'))
->get();
// Sicher: Verwendung des Query Builders
$users = User::where('email', $request->input('email'))->get();
// Sicher: Verwendung von Prepared Statements
$statement = DB::prepare('SELECT * FROM users WHERE email = ?');
$statement->execute([$request->input('email')]);
$users = $statement->fetchAll();
```
## Ausgabebereinigung
### Automatische Bereinigung in Views
Das Framework bereinigt automatisch alle Ausgaben in Views, um XSS-Angriffe zu verhindern:
```php
<!-- Sicher: Automatische Bereinigung -->
<div><?= $user->name ?></div>
<!-- Unsicher: Rohe Ausgabe ohne Bereinigung (nur verwenden, wenn Sie sicher sind, dass der Inhalt vertrauenswürdig ist) -->
<div><?= raw($user->html_content) ?></div>
```
### Manuelle Bereinigung
Sie können auch manuell Daten bereinigen:
```php
use App\Framework\Security\Sanitizer;
// Text bereinigen
$cleanText = Sanitizer::clean($input);
// HTML bereinigen (entfernt gefährliche Tags und Attribute)
$cleanHtml = Sanitizer::cleanHtml($input);
// URL bereinigen
$cleanUrl = Sanitizer::cleanUrl($input);
```
## CSRF-Schutz
Das Framework bietet automatischen Schutz vor Cross-Site Request Forgery (CSRF) für alle Formulare und AJAX-Anfragen.
### CSRF-Token in Formularen
```php
<form method="POST" action="/users">
<?= csrf_field() ?>
<input type="text" name="name">
<button type="submit">Speichern</button>
</form>
```
Die `csrf_field()`-Funktion generiert ein verstecktes Eingabefeld mit einem CSRF-Token:
```html
<input type="hidden" name="_token" value="random-token-here">
```
### CSRF-Token in AJAX-Anfragen
```javascript
// Mit jQuery
$.ajax({
url: '/users',
type: 'POST',
data: {
name: 'John Doe',
_token: '<?= csrf_token() ?>'
}
});
// Mit Fetch API
fetch('/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '<?= csrf_token() ?>'
},
body: JSON.stringify({
name: 'John Doe'
})
});
```
### CSRF-Schutz deaktivieren
In einigen Fällen, wie z.B. bei API-Endpunkten, möchten Sie möglicherweise den CSRF-Schutz deaktivieren:
```php
// In der Middleware-Konfiguration
$middleware->except([
CsrfMiddleware::class => [
'/api/*'
]
]);
```
## Authentifizierung
Das Framework bietet ein flexibles Authentifizierungssystem, das verschiedene Authentifizierungsmethoden unterstützt.
### Benutzerauthentifizierung
```php
use App\Framework\Auth\Auth;
class LoginController extends Controller
{
public function login(Request $request): Response
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|string',
]);
$credentials = $request->only(['email', 'password']);
$remember = $request->input('remember', false);
if (Auth::attempt($credentials, $remember)) {
return $this->redirect('/dashboard');
}
return $this->back()->withErrors([
'email' => 'Die angegebenen Anmeldeinformationen sind ungültig.',
]);
}
public function logout(): Response
{
Auth::logout();
return $this->redirect('/');
}
}
```
### Zugriff auf den authentifizierten Benutzer
```php
// Prüfen, ob ein Benutzer angemeldet ist
if (Auth::check()) {
// Benutzer ist angemeldet
}
// Aktuellen Benutzer abrufen
$user = Auth::user();
// Benutzer-ID abrufen
$userId = Auth::id();
```
### Passwort-Hashing
Das Framework verwendet automatisch sichere Passwort-Hashing-Algorithmen:
```php
use App\Framework\Security\Hash;
// Passwort hashen
$hashedPassword = Hash::make('password');
// Passwort überprüfen
if (Hash::check('password', $hashedPassword)) {
// Passwort ist korrekt
}
// Prüfen, ob ein Passwort neu gehasht werden muss
if (Hash::needsRehash($hashedPassword)) {
$hashedPassword = Hash::make('password');
}
```
### Passwort-Zurücksetzung
Das Framework bietet auch Unterstützung für die Passwort-Zurücksetzung:
```php
use App\Framework\Auth\Password;
// Token für die Passwort-Zurücksetzung erstellen
$token = Password::createToken($user);
// E-Mail mit dem Token senden
Mail::send('emails.reset-password', [
'token' => $token,
'user' => $user
], function ($message) use ($user) {
$message->to($user->email);
$message->subject('Passwort zurücksetzen');
});
// Token überprüfen und Passwort zurücksetzen
$status = Password::reset($request->only(
'email', 'password', 'password_confirmation', 'token'
), function ($user, $password) {
$user->password = Hash::make($password);
$user->save();
});
```
## Autorisierung
Das Framework bietet ein leistungsstarkes Autorisierungssystem, das rollenbasierte und fähigkeitsbasierte Zugriffskontrolle unterstützt.
### Rollenbasierte Zugriffskontrolle
```php
use App\Framework\Auth\Auth;
// Prüfen, ob der Benutzer eine bestimmte Rolle hat
if (Auth::user()->hasRole('admin')) {
// Benutzer ist ein Administrator
}
// Prüfen, ob der Benutzer eine von mehreren Rollen hat
if (Auth::user()->hasAnyRole(['admin', 'editor'])) {
// Benutzer ist entweder Administrator oder Editor
}
// Prüfen, ob der Benutzer alle angegebenen Rollen hat
if (Auth::user()->hasAllRoles(['admin', 'editor'])) {
// Benutzer ist sowohl Administrator als auch Editor
}
```
### Fähigkeitsbasierte Zugriffskontrolle
```php
use App\Framework\Auth\Auth;
// Prüfen, ob der Benutzer eine bestimmte Fähigkeit hat
if (Auth::user()->can('edit-post', $post)) {
// Benutzer kann den Beitrag bearbeiten
}
// Prüfen, ob der Benutzer eine Fähigkeit nicht hat
if (Auth::user()->cannot('delete-post', $post)) {
// Benutzer kann den Beitrag nicht löschen
}
// Autorisierung erzwingen (wirft eine Exception, wenn nicht autorisiert)
Auth::user()->authorize('edit-post', $post);
```
### Richtlinien definieren
Sie können Autorisierungsrichtlinien definieren, um komplexe Autorisierungslogik zu kapseln:
```php
use App\Framework\Auth\Policy;
class PostPolicy extends Policy
{
public function view(User $user, Post $post): bool
{
return true; // Jeder kann Beiträge sehen
}
public function create(User $user): bool
{
return $user->hasRole('writer');
}
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->hasRole('editor');
}
public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->hasRole('admin');
}
}
```
Registrieren Sie die Richtlinie:
```php
use App\Framework\Auth\Gate;
// In der Bootstrap-Datei
Gate::policy(Post::class, PostPolicy::class);
```
Verwenden Sie die Richtlinie:
```php
if (Auth::user()->can('update', $post)) {
// Benutzer kann den Beitrag aktualisieren
}
```
### Autorisierung in Controllern
Sie können die Autorisierung auch direkt in Controllern verwenden:
```php
public function update(Request $request, int $id): Response
{
$post = Post::find($id);
if (!$post) {
return $this->notFound('Beitrag nicht gefunden');
}
$this->authorize('update', $post);
// Beitrag aktualisieren
return $this->redirect('/posts')->with('success', 'Beitrag aktualisiert');
}
```
## Web Application Firewall (WAF)
Das Framework enthält eine integrierte Web Application Firewall (WAF), die Ihre Anwendung vor gängigen Angriffen schützt.
### WAF-Konfiguration
Die WAF ist standardmäßig aktiviert und kann in der Konfigurationsdatei `config/security.php` konfiguriert werden:
```php
return [
'waf' => [
'enabled' => true,
'rules' => [
'sql_injection' => true,
'xss' => true,
'command_injection' => true,
'path_traversal' => true,
'request_validation' => true,
],
'whitelist' => [
'ip' => [
'127.0.0.1',
'::1',
],
'paths' => [
'/api/webhook',
],
],
'blacklist' => [
'ip' => [
// Blockierte IP-Adressen
],
'user_agents' => [
'Bad Bot',
],
],
'rate_limiting' => [
'enabled' => true,
'max_requests' => 100,
'time_window' => 60, // Sekunden
],
],
];
```
### Maschinelles Lernen für die Angriffserkennung
Die WAF verwendet auch maschinelles Lernen, um Anomalien zu erkennen und potenzielle Angriffe zu identifizieren:
```php
use App\Framework\Waf\MachineLearning\MachineLearningEngine;
// In einem Controller oder Service
$mlEngine = $this->container->get(MachineLearningEngine::class);
$anomalyScore = $mlEngine->detectAnomalies($request);
if ($anomalyScore > 0.8) {
// Verdächtige Anfrage protokollieren oder blockieren
$this->logger->warning('Verdächtige Anfrage erkannt', [
'ip' => $request->ip(),
'path' => $request->path(),
'score' => $anomalyScore,
]);
}
```
## Sicherheitsheader
Das Framework konfiguriert automatisch wichtige Sicherheitsheader, um Ihre Anwendung zu schützen.
### Content Security Policy (CSP)
Die Content Security Policy schützt vor XSS-Angriffen, indem sie kontrolliert, welche Ressourcen geladen werden dürfen:
```php
use App\Framework\Security\ContentSecurityPolicy;
// In einem Middleware oder Controller
$csp = new ContentSecurityPolicy();
$csp->setDefaultSrc(['self'])
->setScriptSrc(['self', 'trusted-scripts.com'])
->setStyleSrc(['self', 'trusted-styles.com'])
->setImgSrc(['self', 'trusted-images.com'])
->setFontSrc(['self', 'trusted-fonts.com'])
->setConnectSrc(['self', 'api.trusted-domain.com'])
->setFrameSrc(['none'])
->setObjectSrc(['none'])
->setReportUri('/csp-report');
$response->headers->set('Content-Security-Policy', $csp->compile());
```
### Andere Sicherheitsheader
Das Framework setzt auch andere wichtige Sicherheitsheader:
```php
// In einem Middleware oder Controller
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
```
## Sichere Konfiguration
### HTTPS erzwingen
Sie können HTTPS für Ihre Anwendung erzwingen:
```php
// In der Middleware-Konfiguration
$middleware->add(HttpsMiddleware::class);
```
### Sichere Cookies
Das Framework konfiguriert automatisch sichere Cookies:
```php
// In der Konfigurationsdatei config/session.php
return [
'cookie' => [
'secure' => true,
'http_only' => true,
'same_site' => 'lax',
],
];
```
### Sichere Datei-Uploads
Das Framework bietet Funktionen für sichere Datei-Uploads:
```php
use App\Framework\Security\FileValidator;
// In einem Controller
$this->validate($request, [
'document' => 'required|file|mimes:pdf,doc,docx|max:10240',
]);
$file = $request->file('document');
// Zusätzliche Sicherheitsvalidierung
$validator = new FileValidator();
if (!$validator->isSafe($file)) {
return $this->back()->withErrors([
'document' => 'Die Datei ist möglicherweise schädlich.',
]);
}
// Datei speichern
$path = $file->store('documents');
```
## Sicherheitsprotokollierung und -überwachung
Das Framework bietet umfangreiche Protokollierung für Sicherheitsereignisse:
```php
use App\Framework\Security\SecurityLogger;
// In einem Controller oder Service
$securityLogger = $this->container->get(SecurityLogger::class);
// Sicherheitsereignis protokollieren
$securityLogger->log('authentication_failure', [
'user_id' => $user->id,
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
]);
```
### Sicherheitsbenachrichtigungen
Sie können auch Benachrichtigungen für wichtige Sicherheitsereignisse konfigurieren:
```php
use App\Framework\Security\SecurityNotifier;
// In einem Controller oder Service
$securityNotifier = $this->container->get(SecurityNotifier::class);
// Benachrichtigung senden
$securityNotifier->notify('suspicious_login', [
'user_id' => $user->id,
'ip' => $request->ip(),
'location' => $geoip->getLocation($request->ip()),
'time' => now(),
]);
```
## Sicherheits-Checkliste
Hier ist eine Checkliste, um sicherzustellen, dass Ihre Anwendung sicher ist:
1. **Eingabevalidierung**: Validieren Sie alle Benutzereingaben.
2. **Ausgabebereinigung**: Bereinigen Sie alle Ausgaben, um XSS zu verhindern.
3. **CSRF-Schutz**: Verwenden Sie CSRF-Token in allen Formularen und AJAX-Anfragen.
4. **Authentifizierung**: Implementieren Sie sichere Authentifizierungsmechanismen.
5. **Autorisierung**: Implementieren Sie rollenbasierte und fähigkeitsbasierte Zugriffskontrolle.
6. **Sichere Passwörter**: Verwenden Sie sichere Passwort-Hashing-Algorithmen.
7. **HTTPS**: Erzwingen Sie HTTPS für Ihre Anwendung.
8. **Sichere Cookies**: Konfigurieren Sie sichere Cookies.
9. **Sicherheitsheader**: Setzen Sie wichtige Sicherheitsheader.
10. **Datei-Uploads**: Validieren Sie Datei-Uploads sorgfältig.
11. **Fehlerbehandlung**: Zeigen Sie keine sensiblen Informationen in Fehlermeldungen an.
12. **Protokollierung**: Protokollieren Sie Sicherheitsereignisse.
13. **Aktualisierungen**: Halten Sie das Framework und alle Abhängigkeiten aktuell.
## Weitere Informationen
- [Authentifizierungs-Anleitung](authentication.md): Erfahren Sie mehr über die Authentifizierungsfunktionen des Frameworks.
- [Autorisierungs-Anleitung](authorization.md): Erfahren Sie mehr über die Autorisierungsfunktionen des Frameworks.
- [WAF-Anleitung](../components/waf/index.md): Erfahren Sie mehr über die Web Application Firewall des Frameworks.
- [Validierungs-Anleitung](validation.md): Erfahren Sie mehr über die Validierungsfunktionen des Frameworks.
- [Architekturübersicht](../architecture/overview.md): Überblick über die Architektur des Frameworks.

539
docs/guides/validation.md Normal file
View File

@@ -0,0 +1,539 @@
# Validierungs-Anleitung
Diese Anleitung erklärt, wie das Validierungssystem des Frameworks funktioniert und wie Sie es effektiv nutzen können.
## Grundlagen der Validierung
Die Validierung ist ein wichtiger Teil jeder Webanwendung, da sie sicherstellt, dass die Daten, die von Benutzern eingegeben werden, den erwarteten Regeln entsprechen. Das Framework bietet ein leistungsstarkes und flexibles Validierungssystem, das einfach zu verwenden ist.
### Validierung in Controllern
Die einfachste Möglichkeit, Daten zu validieren, ist die Verwendung der `validate`-Methode in Controllern:
```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',
'age' => 'integer|min:18',
]);
// 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, und die Validierungsfehler werden in der Session gespeichert.
- Bei einer AJAX-Anfrage wird eine JSON-Antwort mit den Validierungsfehlern und dem HTTP-Statuscode 422 zurückgegeben.
### Manuelle Validierung
Sie können auch den Validator direkt verwenden, um mehr Kontrolle über den Validierungsprozess zu haben:
```php
use App\Framework\Validation\Validator;
public function store(Request $request): Response
{
$validator = new Validator($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8|confirmed',
]);
if ($validator->fails()) {
$errors = $validator->errors();
// Fehler behandeln
return $this->back()->withErrors($errors);
}
// 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');
}
```
## Validierungsregeln
Das Framework bietet eine Vielzahl von Validierungsregeln, die Sie verwenden können, um Ihre Daten zu validieren.
### Grundlegende Regeln
- `required`: Das Feld muss vorhanden sein und darf nicht leer sein.
- `string`: Das Feld muss ein String sein.
- `integer`: Das Feld muss eine Ganzzahl sein.
- `numeric`: Das Feld muss eine Zahl sein (Ganzzahl oder Dezimalzahl).
- `boolean`: Das Feld muss ein Boolean sein (`true`, `false`, `1`, `0`, `"1"`, `"0"`).
- `array`: Das Feld muss ein Array sein.
- `date`: Das Feld muss ein gültiges Datum sein.
- `email`: Das Feld muss eine gültige E-Mail-Adresse sein.
- `url`: Das Feld muss eine gültige URL sein.
- `ip`: Das Feld muss eine gültige IP-Adresse sein.
- `json`: Das Feld muss ein gültiger JSON-String sein.
- `alpha`: Das Feld darf nur Buchstaben enthalten.
- `alpha_num`: Das Feld darf nur Buchstaben und Zahlen enthalten.
- `alpha_dash`: Das Feld darf nur Buchstaben, Zahlen, Bindestriche und Unterstriche enthalten.
### Größenregeln
- `min:value`: Das Feld muss mindestens `value` groß sein.
- Für Strings: Die Anzahl der Zeichen.
- Für Zahlen: Der Mindestwert.
- Für Arrays: Die Mindestanzahl der Elemente.
- Für Dateien: Die Mindestgröße in Kilobyte.
- `max:value`: Das Feld darf höchstens `value` groß sein.
- `size:value`: Das Feld muss genau `value` groß sein.
- `between:min,max`: Das Feld muss zwischen `min` und `max` liegen.
### Vergleichsregeln
- `same:field`: Das Feld muss den gleichen Wert wie `field` haben.
- `different:field`: Das Feld muss einen anderen Wert als `field` haben.
- `gt:field`: Das Feld muss größer als `field` sein.
- `gte:field`: Das Feld muss größer oder gleich `field` sein.
- `lt:field`: Das Feld muss kleiner als `field` sein.
- `lte:field`: Das Feld muss kleiner oder gleich `field` sein.
### Datenbankregeln
- `unique:table,column,except,idColumn`: Das Feld muss in der angegebenen Tabelle und Spalte einzigartig sein.
- `table`: Der Name der Tabelle.
- `column`: Der Name der Spalte (standardmäßig der Feldname).
- `except`: Die ID des Datensatzes, der bei der Überprüfung ignoriert werden soll (optional).
- `idColumn`: Der Name der ID-Spalte (standardmäßig `id`).
- `exists:table,column`: Das Feld muss in der angegebenen Tabelle und Spalte existieren.
### Dateiregeln
- `file`: Das Feld muss eine erfolgreich hochgeladene Datei sein.
- `image`: Das Feld muss ein Bild sein (jpeg, png, bmp, gif, svg).
- `mimes:jpeg,png,...`: Das Feld muss eine Datei mit einem der angegebenen MIME-Typen sein.
- `mimetypes:text/plain,...`: Das Feld muss eine Datei mit einem der angegebenen MIME-Typen sein.
- `dimensions:min_width=100,max_width=1000,min_height=100,max_height=1000,width=100,height=100,ratio=1/1`: Das Feld muss ein Bild sein, das den angegebenen Dimensionen entspricht.
### Bedingte Regeln
- `required_if:anotherfield,value,...`: Das Feld ist erforderlich, wenn `anotherfield` einen der angegebenen Werte hat.
- `required_unless:anotherfield,value,...`: Das Feld ist erforderlich, es sei denn, `anotherfield` hat einen der angegebenen Werte.
- `required_with:foo,bar,...`: Das Feld ist erforderlich, wenn eines der anderen angegebenen Felder vorhanden ist.
- `required_with_all:foo,bar,...`: Das Feld ist erforderlich, wenn alle anderen angegebenen Felder vorhanden sind.
- `required_without:foo,bar,...`: Das Feld ist erforderlich, wenn eines der anderen angegebenen Felder nicht vorhanden ist.
- `required_without_all:foo,bar,...`: Das Feld ist erforderlich, wenn alle anderen angegebenen Felder nicht vorhanden sind.
### Formatregeln
- `date_format:format`: Das Feld muss dem angegebenen Datumsformat entsprechen.
- `regex:pattern`: Das Feld muss dem angegebenen regulären Ausdruck entsprechen.
- `not_regex:pattern`: Das Feld darf nicht dem angegebenen regulären Ausdruck entsprechen.
### Bestätigungsregeln
- `confirmed`: Das Feld muss ein Bestätigungsfeld haben (z.B. `password_confirmation` für das Feld `password`).
- `accepted`: Das Feld muss akzeptiert werden (yes, on, 1, true).
### Spezielle Regeln
- `nullable`: Das Feld darf `null` sein.
- `present`: Das Feld muss im Eingabedaten vorhanden sein, kann aber leer sein.
- `sometimes`: Die Regel wird nur angewendet, wenn das Feld vorhanden ist.
## Benutzerdefinierte Validierungsnachrichten
Sie können benutzerdefinierte Validierungsnachrichten angeben, um die Standardnachrichten zu überschreiben:
```php
$this->validate($request, [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8|confirmed',
], [
'name.required' => 'Der Name ist erforderlich',
'email.unique' => 'Diese E-Mail-Adresse wird bereits verwendet',
'password.min' => 'Das Passwort muss mindestens 8 Zeichen lang sein',
]);
```
Sie können auch Platzhalter in Ihren Nachrichten verwenden:
- `:attribute`: Der Name des Feldes.
- `:min`: Der Mindestwert.
- `:max`: Der Maximalwert.
- `:size`: Die erforderliche Größe.
- `:values`: Die gültigen Werte.
- `:other`: Der Name des anderen Feldes.
```php
'min' => 'Das Feld :attribute muss mindestens :min Zeichen enthalten',
```
## Validierung von Arrays
Sie können auch verschachtelte Arrays validieren:
```php
$this->validate($request, [
'user.name' => 'required|string|max:255',
'user.email' => 'required|email|unique:users,email',
'addresses.*.street' => 'required|string|max:255',
'addresses.*.city' => 'required|string|max:255',
'addresses.*.country' => 'required|string|max:255',
]);
```
In diesem Beispiel wird erwartet, dass `user` ein Array mit den Schlüsseln `name` und `email` ist, und `addresses` ein Array von Arrays ist, wobei jedes innere Array die Schlüssel `street`, `city` und `country` hat.
## Benutzerdefinierte Validierungsregeln
### Erstellen einer benutzerdefinierten Regel
Sie können benutzerdefinierte Validierungsregeln erstellen, indem Sie die `Rule`-Schnittstelle implementieren:
```php
use App\Framework\Validation\Rule;
class StrongPassword implements Rule
{
public function passes($attribute, $value): bool
{
// Überprüfen, ob das Passwort stark ist
return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/', $value) === 1;
}
public function message(): string
{
return 'Das Passwort muss mindestens 8 Zeichen lang sein und mindestens einen Großbuchstaben, einen Kleinbuchstaben, eine Zahl und ein Sonderzeichen enthalten.';
}
}
```
### Verwenden einer benutzerdefinierten Regel
Sie können Ihre benutzerdefinierte Regel wie folgt verwenden:
```php
use App\Framework\Validation\Validator;
$validator = new Validator($request->all(), [
'password' => ['required', 'string', new StrongPassword()],
]);
```
Oder in einem Controller:
```php
$this->validate($request, [
'password' => ['required', 'string', new StrongPassword()],
]);
```
## Validierung mit Form Requests
Für komplexe Validierungslogik können Sie Form Request-Klassen erstellen:
```php
use App\Framework\Http\FormRequest;
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|confirmed',
];
}
public function messages(): array
{
return [
'name.required' => 'Der Name ist erforderlich',
'email.unique' => 'Diese E-Mail-Adresse wird bereits verwendet',
'password.min' => 'Das Passwort muss mindestens 8 Zeichen lang sein',
];
}
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');
}
```
Die `validated`-Methode gibt nur die validierten Felder zurück, was nützlich ist, um Massenänderungen zu verhindern.
## Validierung von Dateien
Das Framework bietet spezielle Regeln für die Validierung von Dateien:
```php
$this->validate($request, [
'avatar' => 'required|file|image|max:2048', // max in Kilobyte
'document' => 'required|file|mimes:pdf,doc,docx|max:10240',
]);
```
## Validierung von Daten außerhalb von Requests
Sie können den Validator auch verwenden, um Daten zu validieren, die nicht aus einer HTTP-Anfrage stammen:
```php
use App\Framework\Validation\Validator;
$data = [
'name' => 'John Doe',
'email' => 'john@example.com',
];
$validator = new Validator($data, [
'name' => 'required|string|max:255',
'email' => 'required|email',
]);
if ($validator->fails()) {
$errors = $validator->errors();
// Fehler behandeln
} else {
$validatedData = $validator->validated();
// Mit validierten Daten fortfahren
}
```
## Validierung in JavaScript
Das Framework bietet auch eine Möglichkeit, Validierungsregeln in JavaScript zu verwenden, um clientseitige Validierung zu ermöglichen:
```php
// In Ihrer View
<script>
const rules = <?= json_encode($validator->jsRules()) ?>;
const messages = <?= json_encode($validator->jsMessages()) ?>;
// Verwenden Sie die Regeln mit Ihrer bevorzugten JavaScript-Validierungsbibliothek
</script>
```
## Validierung von Abhängigkeiten
Manchmal hängt die Validierung eines Feldes vom Wert eines anderen Feldes ab. Sie können benutzerdefinierte Regeln verwenden, um diese Art von Validierung zu implementieren:
```php
$this->validate($request, [
'payment_type' => 'required|in:credit_card,paypal',
'card_number' => 'required_if:payment_type,credit_card|string|size:16',
'card_expiry' => 'required_if:payment_type,credit_card|date_format:m/y',
'card_cvv' => 'required_if:payment_type,credit_card|string|size:3',
'paypal_email' => 'required_if:payment_type,paypal|email',
]);
```
## Validierung mit Bedingungen
Sie können auch komplexere Bedingungen für die Validierung definieren:
```php
$validator = new Validator($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|email',
]);
$validator->sometimes('phone', 'required|string|size:10', function ($input) {
return $input->contact_method === 'phone';
});
$validator->sometimes('address', 'required|string|max:255', function ($input) {
return $input->requires_shipping === true;
});
```
## Validierung von Datenbanken
Das Framework bietet Regeln für die Validierung von Datenbanken:
```php
$this->validate($request, [
'email' => 'required|email|unique:users,email',
'username' => 'required|string|unique:users,username',
'category_id' => 'required|integer|exists:categories,id',
]);
```
Bei der Aktualisierung eines Datensatzes möchten Sie möglicherweise den aktuellen Datensatz von der Einzigartigkeit ausschließen:
```php
$this->validate($request, [
'email' => 'required|email|unique:users,email,' . $userId,
'username' => 'required|string|unique:users,username,' . $userId,
]);
```
## Fehlerbehandlung
### Zugriff auf Validierungsfehler
Wenn die Validierung fehlschlägt, können Sie auf die Fehler zugreifen:
```php
$validator = new Validator($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
]);
if ($validator->fails()) {
$errors = $validator->errors();
// Alle Fehler abrufen
$allErrors = $errors->all();
// Fehler für ein bestimmtes Feld abrufen
$nameErrors = $errors->get('name');
// Ersten Fehler für ein bestimmtes Feld abrufen
$firstNameError = $errors->first('name');
// Prüfen, ob ein Feld Fehler hat
$hasNameErrors = $errors->has('name');
}
```
### Anzeigen von Validierungsfehlern in Views
In Ihren Views können Sie die Validierungsfehler wie folgt anzeigen:
```php
<?php if ($errors->has('name')): ?>
<div class="error"><?= $errors->first('name') ?></div>
<?php endif; ?>
```
Oder alle Fehler anzeigen:
```php
<?php if ($errors->any()): ?>
<div class="alert alert-danger">
<ul>
<?php foreach ($errors->all() as $error): ?>
<li><?= $error ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
```
## Beste Praktiken
### 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|confirmed',
];
}
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));
// ...
}
```
### Validierung in Modellen
Sie können Validierungsregeln auch in Ihren Modellen definieren:
```php
class User extends Model
{
public static function validationRules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8',
];
}
public static function updateValidationRules(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, User::validationRules());
// ...
}
public function update(Request $request, int $id): Response
{
$this->validate($request, User::updateValidationRules($id));
// ...
}
```
## Weitere Informationen
- [Controller-Anleitung](controllers.md): Erfahren Sie mehr über Controller und wie sie mit der Validierung interagieren.
- [Form Request-Anleitung](form-requests.md): Erfahren Sie mehr über Form Requests und wie sie die Validierung vereinfachen können.
- [Datenbank-Anleitung](database.md): Erfahren Sie mehr über Datenbankoperationen und wie sie mit der Validierung interagieren.
- [Architekturübersicht](../architecture/overview.md): Überblick über die Architektur des Frameworks.