Eigene Validatoren
In diesem Artikel wird erklärt, wie du eigene Validatoren erstellen kannst. Du solltest hierzu bereits die Artikel Formulare und Eigenschafts-Typen gelesen haben.
Allgemeins
Um dem Entwickler die Autovervollständigung zu erleichtern, sollten die Namen aller Validatoren-Klassen mit dem Prefix "Val" beginnen.
Eigenschafts-Validatoren ignorieren null
-Werte. Ist die Eigenschaft obligatorisch, soll zusätzlich ValNotEmpty
oder ValIsset
angewendet werden.
Bevor du mit diesem Artikel fortfährst, solltest du ein geringes Wissen über den Typ ManagedProperty
haben. Lies hierzu das Kapitel Eigene Eigenschaften.
Allgemeiner Validator
Für einen Validator, der allgemein auf ein Formular angewendet werden soll, erstellst du eine Klasse, die n2n\dispatch\map\val\Validator
implementiert. Das folgende Beispiel zeigt einen Validator, der prüft, ob die Eigenschaft $foo
grösser als $bar
ist:
class ValFooBar implements Validator { public function validate(MappingResult $mappingResult, N2nContext $n2nContext) { if ($mappingResult->foo > $mappingResult->bar) return; $mappingResult->getBindingErrors()->addError('foo', 'Foo must be greater than Bar.'); } }
validate()
übergibt dir das MappingResult
des aktuellen Form Models, über welches du auf die zu validierenden Werte zugreifen kannst. Zudem liefert dir MappingResult::getBindingErrors()
die BindingErrors
, über welche du allfällige Validierungs-Fehler registrieren kannst.
Allgemeine Validatoren lassen sich über BindingDefinition::valo()
registrieren.
private function _validation(BindingDefinition $bd) { $bd->valo(new ValFooBar()); }
Eigenschafts-Validator
Eigenschafts-Validatoren implementieren n2n\dispatch\map\val\PropertyValidator
und können über BindingDefinition::val()
auf beliebige Eigenschaften angewendet werden.
Zum Beispiel können wir einen Validator ValEquals
erstellen, der überprüfen soll, ob die Werte aller Eigenschaften, auf die er angewendet wird, gleich sind. Um die Entwicklung eines Validators zu vereinfachen, empfiehlt es sich n2n\dispatch\map\val\impl\PropertyValidatorAdapter
zu erweitern.
class ValEquals extends PropertyValidatorAdapter { public function __construct() { $this->restrictType(array('n2n\dispatch\property\impl\ScalarProperty'), false); } public function validate(MappingResult $mappingResult, N2nContext $n2nContext) { $firstPropertyName = null; $firstMapValue = null; foreach ($this->getManagedProperties() as $managedProperty) { $propertyName = $managedProperty->getName(); if ($firstPropertyName === null) { $firstPropertyName = $propertyName; $firstMapValue = $mappingResult->__get($propertyName); continue; } if ($firstMapValue === $mappingResult->__get($propertyName)) { continue; } $mappingResult->getBindingErrors()->addError($propertyName, $mappingResult->getLabel($propertyName) . ' does not equal ' . $mappingResult->getLabel($firstPropertyName)); } } }
PropertyValidatorAdapter::restrictType()
gibt dir die Möglichkeit deinen Validator auf bestimmte Eigenschafts-Typen einzuschränken. Der erste Parameter erwartet ein array
von Namen der kompatiblen Eigenschafts-Typen. Über den zweiten Parameter bestimmst du, ob der Validator auf Array-Eigenschaften angewendet werden darf. In diesem Beispiel erlaubt ValEquals
nur Eigenschaften vom Typ n2n\dispatch\property\impl\ScalarProperty
, die keine Arrays sind. Wendet der Entwickler ValEquals
auf einen anderen Eigenschafts-Typ an, wird eine entsprechende Exception
geworfen.
PropertyValidatorAdapter::getManagedProperties()
gibt uns ein Array vonManagedProperty
-Objekten zurück, die alle Eigenschaften repräsentieren, auf welche der Validator angewendet werden soll.
Möchten wir nun prüfen, ob die Eigenschaften $foo
und $bar
identisch sind, müssen wir ValEquals
wie folgt anwenden:
private function _validation(BindingDefinition $bd) { $bd->val(array('additionalQuestions', 'mainQuestion'), new ValEquals()); }
SinglePropertyValidator
Erweitere n2n\dispatch\map\val\SinglePropertyValidator
, um Eigenschaften unabhängig voneinander zu validieren. SinglePropertyValidator
erweitert PropertyValidatorAdapter
und lässt dich die Methode validateProperty($mapValue)
implementieren, die für jede Eigenschaft ausgeführt wird und deren Wert als Parameter übergibt. Nutze SinglePropertyValidator::failed()
oder SinglePropertyValidator::failedCode()
, um Fehler zu registrieren und SinglePropertyValidator::getLabel()
, um das Label der aktuell zu validierenden Eigenschaft zu erhalten. failed()
erlaubt als Fehlermeldung einen string
oder ein Objekt vom Typ n2n\l10n\Message
(auch n2n\l10n\MessageCode
). Informationen über weitere Hilfs-Methoden findest du in der PHPDoc.
class ValArraySize extends SinglePropertyValidator { private $minSize; private $maxSize; public function __construct($minSize, $maxSize) { $this->minSize = (int) $minSize; $this->maxSize = (int) $maxSize; $this->restrictType(null, true); } protected function validateProperty($mapValue) { $size = count($mapValue); if ($size < $this->minSize) { $this->failed('At least ' . $this->minSize . ' of ' . $this->getLabel() . ' are required.'); } if ($size > $this->maxSize) { $this->failed('At least ' . $this->minSize . ' of ' . $this->getLabel() . ' are required.'); } } }
ValArraySize
prüft die Grösse eines Arrays. Mit $this->restrictType(null, true);
erreichen wir, dass dieser Validator nur auf Array-Eigenschaften angewendet werden kann.
Dieser Validator könnte zum Beispiel folgendermassen angewendet werden:
private function _validation(BindingDefinition $bd) { $bd->val(array('foos', 'bars'), new ValArraySize(1, 5)); }
In diesem Beispiel überprüfen wir, ob die zwei Eigenschaften $foos
und $bars
beide Arrays mit mindestens einem und maximal 5 Feldern als Wert haben.
SimplePropertyValidator
Erweitere n2n\dispatch\map\val\SimplePropertyValidator
, wenn es für deinen Validator zu vernachlässigen ist, ob er auf eine Array-Eigenschaft angewendet wird oder nicht.SimplePropertyValidator
lässt dich die Methode validateValue()
implementieren. Ähnlich wie beim SinglePropertyValidator
wird diese Methode für jede Eigenschaft aufgerufen. Bei Array-Eigenschaften wird sie allerdings für jedes Array-Feld separat mit dessen Wert als Parameter aufgerufen. SimplePropertyValidator
erweitert SinglePropertyValidator
, so sind dieselben Hilfs-Methoden verfügbar.
class ValSuffix extends SimplePropertyValidator { private $suffix; public function __construct($suffix) { $this->suffix = $suffix; $this->restrictType(array('n2n\dispatch\property\SimpleProperty')); } public function validateValue($mapValue) { if ($mapValue === null) return; $managedProperty = $this->getManagedProperty(); ArgumentUtils::assertTrue($managedProperty instanceof SimpleProperty); $scalarValue = $managedProperty->convertMapValueToScalar($mapValue, $this->getN2nContext()); if (!StringUtils::endsWith($this->suffix, $scalarValue)) { $this->failed($this->getLabel() . ' must end with ' . $this->suffix); } } }
ValSuffix
prüft, ob die Werte aller Eigenschaften, auf die der Validator angewendet wird, den jeweiligen Suffix haben. Ist die Eigenschaft ein Array, so wird überprüft, ob die Werte aller Array-Felder diesen Suffix haben. Wir beschränken diesen Validator auf Eigenschafts-Typen, die n2n\dispatch\property\SimpleProperty
implementieren. Somit müssen wir SimpleProperty::convertMapValueToScalar()
verwenden, um den Eigenschafts-Wert in einen skalaren Wert umzuwandeln.
In folgendem Beispiel gehen wir davon aus, dass $additionalQuestions
eine Array-Eigenschaft ist und $mainQuestion
nicht.
private function _validation(BindingDefinition $bd) { $bd->val(array('foo', 'bars'), new ValSuffix('?')); }
In diesem Validierungs-Beispiel üperprüfen wir, ob die Eigenschaft $mainQuestion
und alle Array-Felder der Eigenschaft $additionalQuestion
einen Wert mit Suffix "?" haben.
Wiederverwendbarkeit
Um die Wiederverwendbarkeit von Validatoren zu erhöhen, sollte der Entwickler die Möglichkeit haben, optional eine eigene Fehlermeldung zu übergeben. Sollte er dies nicht tun, muss im Fehlerfall eine Standard-Fehlermeldung verwendet werden. Hierzu solltest du auf einen [link zu text-collection doku]Text-Code[/[link zu text-collection doku] zurückgreifen. Dieser Text-Code kann bei der Ausgabe in einer View, von der [link zu View Text-Collection Info]aktuellen Text-Datei[/link zu View Text-Collection Info] überschrieben werden. Damit dies nicht unabsichtlich passiert, sollte der Text-Code den Namen (mit Namespace) des Validators enthalten.
class ValSuffix extends SimplePropertyValidator { const DEFAULT_ERROR_TEXT_CODE = 'atusch.form.ValSuffix'; private $suffix; private $errorMessage; public function __construct($suffix, $errorMessage = null) { $this->suffix = $suffix; $this->errorMessage = $errorMessage; $this->restrictType(array('n2n\dispatch\property\SimpleProperty')); } public function validateValue($mapValue) { if ($mapValue === null) return; ArgumentUtils::assertTrue($managedProperty instanceof SimpleProperty); $scalarValue = $managedProperty->convertMapValueToScalar($mapValue, $this->getN2nContext()); if (!StringUtils::endsWith($this->suffix, $scalarValue)) { $this->failed($this->errorMessage, self::DEFAULT_ERROR_TEXT_CODE, array('suffix' => $this->suffix), 'atusch'); } } }
Den Text-Code der Standard-Fehlermeldung sowie allfällige Argumente kannst du SinglePropertyValidator::failed()
als zweiten und dritten Parameter übergeben. failed()
fügt bei den Argumenten des Text-Codes zusätzlich das Feld "field" hinzu, welches das Label der aktuellen Eigenschaft enthält. Dies geschieht auch für übergebene Objekte von MessageCode
. Als vierten Parameter wird der Namespace des Moduls erwartet, in welchem die Text-Datei gesucht werden soll. In der Text-Datei atusch\lang\en.ini
könnte zum Beispiel Folgendes stehen:
atusch.form.ValSuffix = "{field} requires suffix '{suffix}'.";