Wie füge ich einem vorhandenen Formular / Feld einen benutzerdefinierten Validierungshandler hinzu?

21

Wie füge ich einem vorhandenen Formular (oder Formularfeld) in Drupal 8 einen benutzerdefinierten Validierungshandler hinzu?

Ich habe ein Formular, das ich nicht erstellt habe. Ich möchte meine eigenen Validierungsregeln für einige Felder hinzufügen, wenn das Formular gesendet wird.

Für Drupal 7 Benutzerdefinierte Validierung für ein Formular? erklärt, hook_form_alter()wie Sie Ihren Validierungs-Handler] [1] implementieren und dann zum $form['#validate']Array hinzufügen , aber in Drupal 8 sind Formulare Klassen. Die Validierung erfolgt über die validateForm()Methode und ich weiß nicht, wie ich meinen Code in diese einbinden soll.

AngularChef
quelle
4
Möglicherweise ein Duplikat der benutzerdefinierten Validierung für ein Formular?
Gummi
1
Es ist nicht gerade ein Duplikat. Meine Frage ist für D8, Ihr Link ist für D7.
AngularChef
Ich bin heute auf dieses Problem gestoßen und wollte es nur anderen mitteilen, wenn Sie POST nicht verwenden (ich wollte eine URL-Übermittlung an eine vorhandene Ansichtsseite), und zwar weder beim Ausführen von validateForm noch beim Ausführen von submitForm. Im Nachhinein ist das offensichtlich ... aber ich habe 30 Minuten damit verbracht, es herauszufinden, bevor ich merkte ...: /
ben.hamelin

Antworten:

19

Die #validateEigenschaft wird weiterhin in Drupal 8 verwendet. (Mit der Lösung von Adi überschreiben Sie den vorhandenen Validator.)

Wenn Sie Ihren benutzerdefinierten Validator zusätzlich zum Standard hinzufügen möchten, müssen Sie in hook_form_FORM_ID_alter (oder ähnlichem) Folgendes hinzufügen :

$form['#validate'][] = 'my_test_validate';
Shabir A.
quelle
Danke, Shabir. Das Hinzufügen eines benutzerdefinierten Validators funktioniert also in D7 und D8 genauso. ;)
AngularChef
Konsultieren Sie genau den Code des Knotenmoduls. Es gibt dort viele Beispiele
Shabir A.
2
Ich habe es gerade ausprobiert und es hat perfekt funktioniert, danke. Beachten Sie bitte, dass Sie entgegen den Angaben in der D8-Formular-API-Referenz #validate(Ihrem Link) kein $form_stateArray (D7-Methode), sondern ein Objekt zur Implementierung FormStateInterface(D8-Methode) verwenden sollten. Mit anderen Worten, der Code in Ihrem benutzerdefinierten Validator sollte dem Code einer ursprünglichen validateForm()Methode entsprechen.
AngularChef
25

Berdir gab die richtige Antwort, dass eine Einschränkung der richtige Weg ist, um einem Feld in Drupal 8 eine Validierung hinzuzufügen. Hier ist ein Beispiel.

Im folgenden Beispiel werde ich mit einem Knoten des Typs arbeiten podcast, der das Einzelwertfeld hat field_podcast_duration. Der Wert für dieses Feld muss als HH: MM: SS (Stunden, Minuten und Sekunden) formatiert sein.

Um eine Einschränkung zu erstellen, müssen zwei Klassen hinzugefügt werden. Die erste ist die Einschränkungsdefinition, und die zweite ist die Einschränkungsüberprüfung. Beide sind Plugins im Namespace von Drupal\[MODULENAME]\Plugin\Validation\Constraint.

Erstens die Einschränkungsdefinition. Beachten Sie, dass die Plugin-ID in der Anmerkung (Kommentar) der Klasse als 'PodcastDuration' angegeben ist. Dies wird weiter unten verwendet.

namespace Drupal\[MODULENAME]\Plugin\Validation\Constraint;

use Symfony\Component\Validator\Constraint;

/**
 * Checks that the submitted duration is of the format HH:MM:SS
 *
 * @Constraint(
 *   id = "PodcastDuration",
 *   label = @Translation("Podcast Duration", context = "Validation"),
 * )
 */
class PodcastDurationConstraint extends Constraint {

  // The message that will be shown if the format is incorrect.
  public $incorrectDurationFormat = 'The duration must be in the format HH:MM:SS or HHH:MM:SS. You provided %duration';
}

Als nächstes müssen wir den Constraint Validator bereitstellen. Dieser Name dieser Klasse ist der Klassenname von oben, an den ValidatorFolgendes angehängt ist:

namespace Drupal\[MODULENAME]\Plugin\Validation\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
 * Validates the PodcastDuration constraint.
 */
class PodcastDurationConstraintValidator extends ConstraintValidator {

  /**
   * {@inheritdoc}
   */
  public function validate($items, Constraint $constraint) {
    // This is a single-item field so we only need to
    // validate the first item
    $item = $items->first();

    // If there is no value we don't need to validate anything
    if (!isset($item)) {
      return NULL;
    }

    // Check that the value is in the format HH:MM:SS
    if (!preg_match('/^[0-9]{1,2}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}$/', $item->value)) {
      // The value is an incorrect format, so we set a 'violation'
      // aka error. The key we use for the constraint is the key
      // we set in the constraint, in this case $incorrectDurationFormat.
      $this->context->addViolation($constraint->incorrectDurationFormat, ['%duration' => $item->value]);
    }
  }
}

Schließlich müssen wir Drupal anweisen, unsere Einschränkung field_podcast_durationfür den podcastKnotentyp zu verwenden. Wir machen das in hook_entity_bundle_field_info_alter():

use Drupal\Core\Entity\EntityTypeInterface;

function HOOK_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if (!empty($fields['field_podcast_duration'])) {
    $fields['field_podcast_duration']->addConstraint('PodcastDuration');
  }
}
Jaypan
quelle
Wenn Sie letztendlich andere Feldwerte benötigen, um Ihr Feld zu validieren, können Sie dem Inhaltstyp eine Einschränkung hinzufügen. Siehe diesen Blog-Beitrag: lakshminp.com/entity-validation-drupal-8-part-2
ummdorian
1
D8 Form Validation API wurde hier auf Drupal.org ausführlich erklärt. Bereitstellung einer benutzerdefinierten Validierungsbeschränkung
Sukhjinder Singh
1
Da sich diese Frage speziell auf die Formular-API und nicht auf die Feld-API bezieht, wie hängt man diese Einschränkung an ein Formularelement (kein Entitätsfeld) an?
Aaronbauman
Formularelemente können keine Einschränkungen haben. Sie können einem bestimmten Formularelement mit #element_validate eine Validierung hinzufügen. Sehen Sie sich die Top-Antwort auf diesen Thread - es ist das gleiche in D8 arbeitet als D7 drupal.stackexchange.com/questions/86990/...
Jaypan
Stellen Sie sicher, dass Sie $item = $items->first();NULL überprüfen und zurückgeben, wenn nichts vorhanden ist. Andernfalls wird beim Bearbeiten des Felds ein schwerwiegender Fehler angezeigt: TypeError: Argument 2, das an Drupal \ Component \ Utility \ NestedArray :: getValue () übergeben wird, muss vom Typ Array sein. null angegeben, aufgerufen in /data/app/core/lib/Drupal/Core/Field/WidgetBase.php in Zeile 407 in Drupal \ Component \ Utility \ NestedArray :: getValue () (Zeile 69 von core / lib / Drupal / Component /Utility/NestedArray.php).
Ivan Zugec
16

Der richtige Weg, dies für eine Inhaltsentität wie einen Knoten zu tun, besteht darin, ihn als Einschränkung zu registrieren.

Siehe forum_entity_bundle_field_info_alter()und die entsprechenden? ForumLeafValidierungsbeschränkung (beachten Sie, dass zwei Klassen erforderlich sind).

Das ist zunächst etwas komplizierter, hat aber den Vorteil, dass es in die Validierungs-API integriert ist, sodass Ihre Validierung nicht auf das Formularsystem beschränkt ist, sondern beispielsweise auch mit über die REST-API gesendeten Knoten funktionieren kann.

Berdir
quelle
Ein guter Punkt: Dies ist etwas Neues für diejenigen, die früher Code für Drupal 7 geschrieben haben. Ich bin sicher, dass es viele Benutzer gibt, die versuchen werden, Validierungshandler hinzuzufügen, wenn eine Einschränkung angemessener ist.
kiamlaluno
Berdir: Ich habe versucht, diese Option zu implementieren hook_entity_bundle_field_info_alter()(wie hier beschrieben ), aber es hat nie funktioniert ... Es scheint ein dokumentiertes Problem mit diesem Hook zu geben: drupal.org/node/2346347 .
AngularChef
Es gibt einige Probleme, aber ich glaube nicht, dass sie mit Ihrem Problem zusammenhängen. forum.module zeigt, dass es funktioniert. Geben Sie Ihren Code frei. Andernfalls können Sie nicht auf mögliche Probleme in Ihrer Implementierung hinweisen.
Berdir
1
Ich würde gerne mit dieser Methode weitermachen, aber bis es ein gutes Beispiel dafür gibt, wie man sie mit Datentypen verwendet (dh nicht feldspezifisch), um zu prüfen, ob eine externe Bedingung erfüllt ist, bleibt ich bei den Formularänderungen. Der Artikel hat sich nicht damit befasst. Würde mich jemand freundlich auf etwas Nützliches hinweisen oder es hier posten? Vielen Dank.
Colan
Was ist, wenn wir eine vorhandene Validierung wie \ Drupal \ user \ Form \ UserLoginForm :: validateName () hinzufügen müssen? In d7 war es einfach wie $ form ['# validate'] = array ('user_login_name_validate', 'myother_validaion',); Aber es scheint, als hätte das Modul ohne
Beitrag
8

Ich möchte etwas mehr Licht in diese Angelegenheit bringen. Das Hinzufügen der Validierung ist genauso wie zuvor: in hook_form_alter:

$form['#validate'][] = '_form_validation_number_title_validate';

Die Verwendung des values-Objekts innerhalb des $ form_state in der validate-Funktion ist jedoch etwas anders. z.B:

function _form_validation_number_title_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {

  if ($form_state->hasValue('title')) {
     $title = $form_state->getValue('title');

     if (!is_numeric($title[0]['value'])) {
        $form_state->setErrorByName('title', t('Your title should be number'));
     }

  }
}

Also nicht mit einem direkten Zugriff auf das private Variablenobjekt, sondern mit einer Getter-Funktion.

Weitere Informationen finden Sie in meinem Github: https://github.com/flesheater/drupal8_modules_experiments/blob/master/webham_formvalidation/webham_formvalidation.module

Prost!

Nikolay Borisov
quelle
Tatsächlich ist es. Genau wie ich oben in meinem Kommentar geschrieben habe. ;)
AngularChef
7

Es ist sehr ähnlich wie in D7. Ein vollständiges Beispiel:

mymodule.module :

use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_form_FORM_ID_alter() for the FORM_ID() form.
 */
function mymodule_form_FORM_ID_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['#validate'][] = '_mymodule_form_FORM_ID_validate';
}

/**
 * Validates submission values in the FORM_ID() form.
 */
function _mymodule_form_FORM_ID_validate(array &$form, FormStateInterface $form_state) {
  // Validation code here
}
nicholas.alipaz
quelle
Das ist ziemlich nah. Nur der hook_form_FORM_ID_alterbenötigt die Formular-ID. Sie können die benutzerdefinierte Validierungsfunktion beliebig gestalten. Befolgen Sie auch die API-Anleitung hier für die richtigen Parameter.
mikeDOTexe
Vorher erfahren Sie, wie Sie die Formular-ID abrufen und wo Sie diesen Code überprüfen können.
Logeshvaran
3

Ergänzend zu diesen guten Antworten möchte ich hinzufügen:

$form['#validate'][] = 'Drupal\your_custom_module_name\CustomClass::customValidate';

So rufen Sie eine entfernte Klassenmethode für eine Formularvalidierung auf. Ich denke, es ist besser, als eine obige Funktion in der Moduldatei wie im Beispiel aufzurufen.

Pauleau
quelle
Es ist nicht mehr erforderlich, von OO aus in den Prozedurcode zu springen.
Colan
1

Sie können das clientseitige Validierungsmodul verwenden . Einige weitere Details dazu (von der Projektseite):

... fügt mit jquery.validate die clientseitige Validierung (auch "Ajax-Formularvalidierung" genannt) für alle Formulare und Webformulare hinzu . Die enthaltene Datei jquery.validate.js ist gepatcht, da leere Nachrichten ausgeblendet werden mussten.

Mohammed ATIFI
quelle