chore: complete update
This commit is contained in:
116
src/Framework/Validation/Exceptions/ValidationException.php
Normal file
116
src/Framework/Validation/Exceptions/ValidationException.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Exceptions;
|
||||
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
use App\Framework\Validation\ValidationResult;
|
||||
|
||||
final class ValidationException extends FrameworkException
|
||||
{
|
||||
public readonly ValidationResult $validationResult;
|
||||
public readonly array $errors;
|
||||
public readonly string $field;
|
||||
|
||||
/**
|
||||
* @param ValidationResult $validationResult Das Validierungsergebnis mit allen Fehlern
|
||||
* @param string|null $field Optionaler einzelner Feldname für Rückwärtskompatibilität
|
||||
*/
|
||||
public function __construct(
|
||||
ValidationResult $validationResult,
|
||||
?string $field = null
|
||||
) {
|
||||
$this->validationResult = $validationResult;
|
||||
|
||||
// Für Rückwärtskompatibilität: Wenn nur ein Feld angegeben wurde, verwende dessen Fehler
|
||||
if ($field !== null && $validationResult->getFieldErrors($field)) {
|
||||
$this->field = $field;
|
||||
$this->errors = $validationResult->getFieldErrors($field);
|
||||
} else {
|
||||
// Andernfalls verwende das erste Feld oder einen Standard
|
||||
$allErrors = $validationResult->getAll();
|
||||
$firstField = array_key_first($allErrors);
|
||||
$this->field = $firstField ?? 'unknown';
|
||||
$this->errors = $firstField ? $allErrors[$firstField] : [];
|
||||
}
|
||||
|
||||
// Erstelle eine aussagekräftige Fehlernachricht aus allen Fehlern
|
||||
$message = $this->createErrorMessage();
|
||||
|
||||
parent::__construct(message: $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine strukturierte Fehlernachricht aus allen Validierungsfehlern
|
||||
*/
|
||||
private function createErrorMessage(): string
|
||||
{
|
||||
$allErrors = $this->validationResult->getAll();
|
||||
|
||||
if (empty($allErrors)) {
|
||||
return 'Unbekannter Validierungsfehler.';
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
foreach ($allErrors as $field => $fieldErrors) {
|
||||
$fieldMessage = $field . ': ' . implode(', ', $fieldErrors);
|
||||
$messages[] = $fieldMessage;
|
||||
}
|
||||
|
||||
return implode('; ', $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Fehlermeldungen für ein bestimmtes Feld zurück
|
||||
*
|
||||
* @param string $field Feldname
|
||||
* @return array<string> Liste der Fehlermeldungen für das Feld
|
||||
*/
|
||||
public function getFieldErrors(string $field): array
|
||||
{
|
||||
return $this->validationResult->getFieldErrors($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Fehlermeldungen als Array zurück
|
||||
*
|
||||
* @return array<string, string[]> Alle Fehlermeldungen gruppiert nach Feldern
|
||||
*/
|
||||
public function getAllErrors(): array
|
||||
{
|
||||
return $this->validationResult->getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Fehlermeldungen als flache Liste zurück
|
||||
*
|
||||
* @return array<string> Liste aller Fehlermeldungen
|
||||
*/
|
||||
public function getAllErrorMessages(): array
|
||||
{
|
||||
return $this->validationResult->getAllErrorMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob ein bestimmtes Feld Fehler hat
|
||||
*/
|
||||
public function hasFieldErrors(string $field): bool
|
||||
{
|
||||
return !empty($this->validationResult->getFieldErrors($field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Statische Factory-Methode für einfache Einzelfeld-Fehler (Rückwärtskompatibilität)
|
||||
*
|
||||
* @param array<string> $errors Liste der Fehlermeldungen
|
||||
* @param string $field Feldname
|
||||
* @return self
|
||||
*/
|
||||
public static function forField(array $errors, string $field): self
|
||||
{
|
||||
$validationResult = new ValidationResult();
|
||||
$validationResult->addErrors($field, $errors);
|
||||
|
||||
return new self($validationResult, $field);
|
||||
}
|
||||
}
|
||||
15
src/Framework/Validation/GroupAware.php
Normal file
15
src/Framework/Validation/GroupAware.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation;
|
||||
|
||||
/**
|
||||
* Interface für Validierungsregeln, die zu bestimmten Gruppen gehören können
|
||||
*/
|
||||
interface GroupAware
|
||||
{
|
||||
/**
|
||||
* Prüft, ob das Attribut zu einer bestimmten Validierungsgruppe gehört
|
||||
*/
|
||||
public function belongsToGroup(string $group): bool;
|
||||
}
|
||||
35
src/Framework/Validation/Rules/Custom.php
Normal file
35
src/Framework/Validation/Rules/Custom.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
use Closure;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final readonly class Custom implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param callable $validator Funktion zur Validierung (erhält den Wert und gibt bool zurück)
|
||||
* @param array<string> $messages Fehlermeldungen
|
||||
*/
|
||||
public function __construct(
|
||||
private Closure $validator,
|
||||
private array $messages = ['Der angegebene Wert ist ungültig.']
|
||||
) {
|
||||
if (!is_callable($validator)) {
|
||||
throw new \InvalidArgumentException('Der Validator muss eine aufrufbare Funktion sein.');
|
||||
}
|
||||
}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
return ($this->validator)($value);
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
}
|
||||
32
src/Framework/Validation/Rules/Email.php
Normal file
32
src/Framework/Validation/Rules/Email.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final class Email implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
private ?string $message = null
|
||||
) {}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return true; // Leere Werte werden von Required-Regel behandelt
|
||||
}
|
||||
|
||||
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
return [$this->message ?? 'Bitte geben Sie eine gültige E-Mail-Adresse ein.'];
|
||||
}
|
||||
}
|
||||
46
src/Framework/Validation/Rules/In.php
Normal file
46
src/Framework/Validation/Rules/In.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final class In implements ValidationRule
|
||||
{
|
||||
private array $values;
|
||||
|
||||
/**
|
||||
* @param array $values Erlaubte Werte
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
array $values,
|
||||
private ?string $message = null
|
||||
) {
|
||||
if (empty($values)) {
|
||||
throw new \InvalidArgumentException('Die Liste der erlaubten Werte darf nicht leer sein.');
|
||||
}
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return true; // Leere Werte werden von Required-Regel behandelt
|
||||
}
|
||||
|
||||
return in_array($value, $this->values, true);
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
if ($this->message !== null) {
|
||||
return [$this->message];
|
||||
}
|
||||
|
||||
$valuesList = implode(', ', array_map(fn($v) => "'$v'", $this->values));
|
||||
return ["Dieser Wert muss einer der folgenden sein: $valuesList."];
|
||||
}
|
||||
}
|
||||
26
src/Framework/Validation/Rules/IsTrue.php
Normal file
26
src/Framework/Validation/Rules/IsTrue.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final class IsTrue implements ValidationRule
|
||||
{
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
return ['Dieser Wert muss "true" sein.'];
|
||||
}
|
||||
}
|
||||
32
src/Framework/Validation/Rules/Numeric.php
Normal file
32
src/Framework/Validation/Rules/Numeric.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final readonly class Numeric implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
private ?string $message = null
|
||||
) {}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return true; // Leere Werte werden von Required-Regel behandelt
|
||||
}
|
||||
|
||||
return is_numeric($value);
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
return [$this->message ?? 'Dieser Wert muss numerisch sein.'];
|
||||
}
|
||||
}
|
||||
38
src/Framework/Validation/Rules/Pattern.php
Normal file
38
src/Framework/Validation/Rules/Pattern.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final readonly class Pattern implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param string $pattern Regulärer Ausdruck
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
private string $pattern,
|
||||
private ?string $message = null
|
||||
) {}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return true; // Leere Werte werden von Required-Regel behandelt
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return preg_match($this->pattern, $value) === 1;
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
return [$this->message ?? 'Dieser Wert entspricht nicht dem erforderlichen Format.'];
|
||||
}
|
||||
}
|
||||
62
src/Framework/Validation/Rules/Range.php
Normal file
62
src/Framework/Validation/Rules/Range.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final readonly class Range implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param float|null $min Minimaler Wert
|
||||
* @param float|null $max Maximaler Wert
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
private ?float $min = null,
|
||||
private ?float $max = null,
|
||||
private ?string $message = null
|
||||
) {
|
||||
if ($min === null && $max === null) {
|
||||
throw new \InvalidArgumentException('Mindestens einer der Parameter min oder max muss gesetzt sein.');
|
||||
}
|
||||
}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return true; // Leere Werte werden von Required-Regel behandelt
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->min !== null && $value < $this->min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->max !== null && $value > $this->max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
if ($this->message !== null) {
|
||||
return [$this->message];
|
||||
}
|
||||
|
||||
if ($this->min !== null && $this->max !== null) {
|
||||
return ["Dieser Wert muss zwischen {$this->min} und {$this->max} liegen."];
|
||||
} elseif ($this->min !== null) {
|
||||
return ["Dieser Wert muss mindestens {$this->min} sein."];
|
||||
} else {
|
||||
return ["Dieser Wert darf maximal {$this->max} sein."];
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/Framework/Validation/Rules/Required.php
Normal file
28
src/Framework/Validation/Rules/Required.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final readonly class Required implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
private ?string $message = null
|
||||
) {}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
return $value !== null && $value !== '';
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
return [$this->message ?? 'Dieser Wert ist erforderlich.'];
|
||||
}
|
||||
}
|
||||
64
src/Framework/Validation/Rules/StringLength.php
Normal file
64
src/Framework/Validation/Rules/StringLength.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final readonly class StringLength implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* @param int|null $min Minimale Länge des Strings
|
||||
* @param int|null $max Maximale Länge des Strings
|
||||
* @param string|null $message Benutzerdefinierte Fehlermeldung
|
||||
*/
|
||||
public function __construct(
|
||||
private ?int $min = null,
|
||||
private ?int $max = null,
|
||||
private ?string $message = null
|
||||
) {
|
||||
if ($min === null && $max === null) {
|
||||
throw new \InvalidArgumentException('Mindestens einer der Parameter min oder max muss gesetzt sein.');
|
||||
}
|
||||
}
|
||||
|
||||
public function validate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return true; // Leere Werte werden von Required-Regel behandelt
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$length = mb_strlen($value);
|
||||
|
||||
if ($this->min !== null && $length < $this->min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->max !== null && $length > $this->max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getErrorMessages(): array
|
||||
{
|
||||
if ($this->message !== null) {
|
||||
return [$this->message];
|
||||
}
|
||||
|
||||
if ($this->min !== null && $this->max !== null) {
|
||||
return ["Dieser Wert muss zwischen {$this->min} und {$this->max} Zeichen lang sein."];
|
||||
} elseif ($this->min !== null) {
|
||||
return ["Dieser Wert muss mindestens {$this->min} Zeichen lang sein."];
|
||||
} else {
|
||||
return ["Dieser Wert darf maximal {$this->max} Zeichen lang sein."];
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Framework/Validation/Rules/ValidationGroup.php
Normal file
31
src/Framework/Validation/Rules/ValidationGroup.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation\Rules;
|
||||
|
||||
use App\Framework\Validation\GroupAware;
|
||||
use App\Framework\Validation\ValidationRule;
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
|
||||
final class ValidationGroup implements GroupAware
|
||||
{
|
||||
/** @var array<string> */
|
||||
private array $groups;
|
||||
|
||||
/**
|
||||
* @param string|array<string> $groups Name der Gruppe oder Gruppen
|
||||
*/
|
||||
public function __construct(string|array $groups)
|
||||
{
|
||||
$this->groups = is_array($groups) ? $groups : [$groups];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob das Attribut zu einer bestimmten Gruppe gehört
|
||||
*/
|
||||
public function belongsToGroup(string $group): bool
|
||||
{
|
||||
return in_array($group, $this->groups, true);
|
||||
}
|
||||
}
|
||||
72
src/Framework/Validation/ValidationErrorMiddleware.php
Normal file
72
src/Framework/Validation/ValidationErrorMiddleware.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation;
|
||||
|
||||
use App\Framework\Http\Headers;
|
||||
use App\Framework\Http\HttpMiddleware;
|
||||
use App\Framework\Http\HttpResponse;
|
||||
use App\Framework\Http\MiddlewareContext;
|
||||
use App\Framework\Http\MiddlewarePriority;
|
||||
use App\Framework\Http\MiddlewarePriorityAttribute;
|
||||
use App\Framework\Http\RequestStateManager;
|
||||
use App\Framework\Http\Responses\JsonResponse;
|
||||
use App\Framework\Http\Responses\RedirectResponse;
|
||||
use App\Framework\Http\ServerKey;
|
||||
use App\Framework\Http\Session\Session;
|
||||
use App\Framework\Http\Status;
|
||||
use App\Framework\Validation\Exceptions\ValidationException;
|
||||
|
||||
#[MiddlewarePriorityAttribute(MiddlewarePriority::SESSION, -50)]
|
||||
final readonly class ValidationErrorMiddleware implements HttpMiddleware
|
||||
{
|
||||
public function __construct(
|
||||
private Session $session
|
||||
) {}
|
||||
public function __invoke(MiddlewareContext $context, callable $next, RequestStateManager $stateManager): MiddlewareContext
|
||||
{
|
||||
try {
|
||||
return $next($context);
|
||||
} catch (ValidationException $e) {
|
||||
|
||||
// Speichern des Formulars in der Session
|
||||
$this->session->form->store('form', $context->request->parsedBody->data);
|
||||
|
||||
// Speichern der Validierungsfehler in der Session
|
||||
$this->session->validation->add('form', $e->getAllErrors());;
|
||||
|
||||
return $this->createValidationErrorResponse($context, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine Fehlerantwort für Validierungsfehler
|
||||
*/
|
||||
private function createValidationErrorResponse(MiddlewareContext $context, ValidationException $e): MiddlewareContext
|
||||
{
|
||||
$acceptHeader = $context->request->server->get(ServerKey::HTTP_ACCEPT, '');
|
||||
|
||||
// Formatierung der Fehlerantwort je nach Content-Type
|
||||
//$acceptHeader = $_SERVER['HTTP_ACCEPT'] ?? '';
|
||||
|
||||
if (str_contains($acceptHeader, 'application/json')) {
|
||||
// JSON-Antwort für API-Anfragen
|
||||
|
||||
return $context->withResponse(
|
||||
new JsonResponse(
|
||||
body: [
|
||||
'error' => 'Validation Error',
|
||||
'message' => $e->getMessage(),
|
||||
'errors' => $e->getAllErrors(),//$this->formatErrors($e),
|
||||
],
|
||||
status: Status::UNPROCESSABLE_ENTITY,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
||||
$uri = $context->request->server->getRefererUri();
|
||||
|
||||
return $context->withResponse(new RedirectResponse($uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/Framework/Validation/ValidationResult.php
Normal file
84
src/Framework/Validation/ValidationResult.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation;
|
||||
|
||||
final class ValidationResult
|
||||
{
|
||||
/** @var array<string, string[]> */
|
||||
private array $errors = [];
|
||||
|
||||
/**
|
||||
* Fügt eine Fehlermeldung für ein Feld hinzu
|
||||
*/
|
||||
public function addError(string $field, string $message): void
|
||||
{
|
||||
$this->errors[$field][] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt mehrere Fehlermeldungen für ein Feld hinzu
|
||||
*
|
||||
* @param string $field Feldname
|
||||
* @param array<string> $messages Liste von Fehlermeldungen
|
||||
*/
|
||||
public function addErrors(string $field, array $messages): void
|
||||
{
|
||||
foreach ($messages as $message) {
|
||||
$this->addError($field, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob Fehler vorhanden sind
|
||||
*/
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return !empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Fehlermeldungen für ein bestimmtes Feld zurück
|
||||
*
|
||||
* @param string $field Feldname
|
||||
* @return array<string> Liste der Fehlermeldungen für das Feld
|
||||
*/
|
||||
public function getFieldErrors(string $field): array
|
||||
{
|
||||
return $this->errors[$field] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Fehlermeldungen als flache Liste zurück
|
||||
*
|
||||
* @return array<string> Liste aller Fehlermeldungen
|
||||
*/
|
||||
public function getAllErrorMessages(): array
|
||||
{
|
||||
$messages = [];
|
||||
foreach ($this->errors as $fieldErrors) {
|
||||
foreach ($fieldErrors as $error) {
|
||||
$messages[] = $error;
|
||||
}
|
||||
}
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kombiniert zwei Validierungsergebnisse
|
||||
*/
|
||||
public function merge(ValidationResult $other): self
|
||||
{
|
||||
foreach ($other->errors as $field => $messages) {
|
||||
foreach ($messages as $message) {
|
||||
$this->addError($field, $message);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAll():array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
18
src/Framework/Validation/ValidationRule.php
Normal file
18
src/Framework/Validation/ValidationRule.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Framework\Validation;
|
||||
|
||||
interface ValidationRule
|
||||
{
|
||||
/**
|
||||
* Validiert einen Wert anhand der Regel
|
||||
*/
|
||||
public function validate(mixed $value): bool;
|
||||
|
||||
/**
|
||||
* Gibt alle Fehlermeldungen für diese Regel zurück
|
||||
*
|
||||
* @return array<string> Liste der Fehlermeldungen
|
||||
*/
|
||||
public function getErrorMessages(): array;
|
||||
}
|
||||
71
src/Framework/Validation/Validator.php
Normal file
71
src/Framework/Validation/Validator.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Validation;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
final class Validator
|
||||
{
|
||||
/**
|
||||
* Validiert ein Objekt und gibt das Ergebnis zurück
|
||||
*
|
||||
* @param object $object Das zu validierende Objekt
|
||||
* @param string|null $group Optionale Validierungsgruppe
|
||||
* @return ValidationResult
|
||||
*/
|
||||
public function validate(object $object, ?string $group = null): ValidationResult
|
||||
{
|
||||
$result = new ValidationResult();
|
||||
$refClass = new ReflectionClass($object);
|
||||
|
||||
// Eigenschaften validieren
|
||||
foreach ($refClass->getProperties() as $property) {
|
||||
|
||||
try {
|
||||
|
||||
if(!$property?->isInitialized($object) && !$property->getType()?->allowsNull()){
|
||||
|
||||
$result->addError(
|
||||
$property->getName(),
|
||||
sprintf("Feld '%s' darf nicht null sein.", $property->getName())
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$value = $property->getValue($object);
|
||||
} catch (\Throwable) {
|
||||
|
||||
//$result->addError($property->getName(), 'Fehler beim Lesen des Werts');
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
foreach ($property->getAttributes() as $attribute) {
|
||||
$attrInstance = $attribute->newInstance();
|
||||
|
||||
|
||||
if ($attrInstance instanceof ValidationRule) {
|
||||
$shouldValidate = false;
|
||||
|
||||
if ($group === null) {
|
||||
$shouldValidate = true;
|
||||
} elseif ($attrInstance instanceof GroupAware) {
|
||||
$shouldValidate = $attrInstance->belongsToGroup($group);
|
||||
} else {
|
||||
$shouldValidate = true;
|
||||
}
|
||||
|
||||
if ($shouldValidate && !$attrInstance->validate($value)) {
|
||||
$result->addErrors($property->getName(), $attrInstance->getErrorMessages());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user