Come faccio ad aggiungere un gestore di convalida personalizzato a un modulo / campo esistente?


21

Come faccio ad aggiungere un gestore di convalida personalizzato a un modulo esistente (o campo modulo) in Drupal 8?

Ho un modulo che non ho creato. Voglio aggiungere le mie regole di convalida su alcuni campi quando viene inviato il modulo.

Per Drupal 7, convalida personalizzata per un modulo? spiega di implementare hook_form_alter()e quindi aggiungere il gestore di convalida] [1] $form['#validate']all'array, ma in Drupal 8 i moduli sono classi. La convalida viene effettuata tramite il validateForm()metodo e non so come inserire il mio codice.


4

1
Non è esattamente un duplicato. La mia domanda è per D8, il tuo link è per D7.
AngularChef,

Mi sono imbattuto oggi in questo e volevo solo notare per gli altri se non stai usando POST (volevo un invio di URL a una pagina di visualizzazione esistente) né la validateForm né la corsa submitForm. Con il senno di poi questo è ovvio .... ma ho passato 30 minuti a cercare di capirlo prima di
rendermi

Risposte:


19

La #validateproprietà è ancora utilizzata in Drupal 8. (Con la soluzione Adi sostituirai il validatore esistente)

Se vuoi aggiungere il tuo validatore personalizzato oltre a quello predefinito, dovrai aggiungere qualcosa del genere in hook_form_FORM_ID_alter (o simile):

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

Grazie Shabir. Quindi, l'aggiunta di un validatore personalizzato funziona allo stesso modo in D7 e D8. ;)
AngularChef l'

Esattamente, consultare il codice del modulo nodo. ci sono molti esempi lì
Shabir A.

2
L'ho appena provato e ha funzionato perfettamente, grazie. Si noti che contrariamente a quanto afferma il riferimento API del modulo D8 #validate(il proprio collegamento), non è necessario utilizzare $form_statecome array (il modo D7), ma come implementazione di un oggetto FormStateInterface(il modo D8). In altre parole, il codice nel tuo validatore personalizzato dovrebbe essere analogo al codice che potresti trovare in un validateForm()metodo originale .
AngularChef,

25

Berdir ha dato la risposta corretta, che un vincolo è il modo corretto di aggiungere la convalida a un campo in Drupal 8. Ecco un esempio.

Nell'esempio seguente, lavorerò con un nodo di tipo podcast, che ha il campo a valore singolo field_podcast_duration. Il valore per questo campo deve essere formattato come HH: MM: SS (ore, minuti e secondi).

Per creare un vincolo, è necessario aggiungere due classi. Il primo è la definizione del vincolo e il secondo è il validatore del vincolo. Entrambi sono plugin, nello spazio dei nomi di Drupal\[MODULENAME]\Plugin\Validation\Constraint.

Innanzitutto, la definizione del vincolo. Si noti che l'ID del plug-in è indicato come 'PodcastDuration', nell'annotazione (commento) della classe. Questo sarà usato più in basso.

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';
}

Successivamente, dobbiamo fornire il validatore del vincolo. Questo nome di questa classe sarà il nome della classe dall'alto, con l' Validatoraggiunta ad esso:

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]);
    }
  }
}

Infine, abbiamo bisogno di dire Drupal per usare il nostro vincolo field_podcast_durationsul podcasttipo di nodo. Lo facciamo 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');
  }
}

Se si finiscono per richiedere altri valori di campo per convalidare il campo, è possibile aggiungere un vincolo al tipo di contenuto. Vedi questo post sul blog: lakshminp.com/entity-validation-drupal-8-part-2
ummdorian

1
L'API di convalida del modulo D8 è stata spiegata in dettaglio su Drupal.org qui. Fornire un vincolo di convalida personalizzato
Sukhjinder Singh,

1
Poiché questa domanda riguarda specificamente l'API del modulo, non l'API di campo, come si collega questo vincolo a un elemento del modulo (non un campo di entità)?
aaronbauman,

Gli elementi del modulo non possono avere vincoli. È possibile aggiungere la convalida a un elemento del modulo specifico utilizzando #element_validate. Vedi la risposta migliore su questo thread - funziona lo stesso in D8 di D7 drupal.stackexchange.com/questions/86990/…
Jaypan,

Assicurati di controllare $item = $items->first();e restituire NULL se non c'è nulla altrimenti si verificherà un errore irreversibile durante la modifica del campo: TypeError: l'argomento 2 passato a Drupal \ Component \ Utility \ NestedArray :: getValue () deve essere di tipo array, dato null, chiamato in /data/app/core/lib/Drupal/Core/Field/WidgetBase.php sulla riga 407 in Drupal \ Component \ Utility \ NestedArray :: getValue () (riga 69 di core / lib / Drupal / Component /Utility/NestedArray.php).
Ivan Zugec,

16

Il modo corretto per farlo per un'entità di contenuto come il nodo è registrarlo come vincolo.

Vedi forum_entity_bundle_field_info_alter()e il corrispondente? ForumLeafvincolo di validazione (si noti che sono necessarie due classi).

All'inizio è un po 'più complicato, ma il vantaggio è che è integrato nell'API di convalida, quindi la convalida non è limitata al sistema di moduli ma può, ad esempio, funzionare anche con nodi inviati tramite l'API REST.


Un buon punto: questa è una novità per chi era solito scrivere codice per Drupal 7. Sono sicuro che ci sono molti utenti che proveranno ad aggiungere gestori di validazione quando un vincolo è più appropriato.
kiamlaluno

Berdir: Ho esplorato questa opzione cercando di implementare hook_entity_bundle_field_info_alter()(come descritto qui ) ma non ha mai funzionato ... Sembra che ci sia un problema documentato con questo hook: drupal.org/node/2346347 .
AngularChef,

Ci sono alcuni problemi ma non penso che siano collegati al tuo problema. forum.module mostra che funziona. Condividi il tuo codice, altrimenti non è possibile segnalare possibili problemi nella tua implementazione.
Berdir,

1
Mi piacerebbe seguire questo metodo, ma fino a quando non ci sarà un buon esempio di come usarlo con tipi di dati (cioè non specifici del campo) per verificare se una condizione esterna è soddisfatta, sono bloccato con il modulo alterato. L'articolo non è stato approfondito. Qualcuno potrebbe gentilmente indicarmi un posto utile o pubblicarlo qui? Grazie.
colan

cosa succede se è necessario aggiungere una convalida esistente come \ Drupal \ user \ Form \ UserLoginForm :: validateName (). In d7 era semplice come $ form ['# validate'] = array ('user_login_name_validate', 'myother_validaion',); Ma il modulo no-contrib ha implementato questi cambiamenti drupal.org/node/2185941
Kirking

8

Voglio aggiungere un po 'più di luce su questo argomento. L'aggiunta della convalida è esattamente la stessa di prima: in hook_form_alter:

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

L'uso dell'oggetto valori all'interno di $ form_state nella funzione validate è però leggermente diverso. per esempio:

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'));
     }

  }
}

Quindi non con un accesso diretto all'oggetto variabili private, ma piuttosto con una funzione getter.

per maggiori informazioni puoi vedere un esempio completo nel mio github: https://github.com/flesheater/drupal8_modules_experiments/blob/master/webham_formvalidation/webham_formvalidation.module

Saluti!


Certo che lo è. Proprio come ho scritto nel mio commento sopra. ;)
AngularChef

7

È praticamente lo stesso di D7. Un esempio completo:

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
}

Questo è abbastanza vicino. È hook_form_FORM_ID_alternecessario solo l' ID modulo. Puoi avere la funzione di convalida personalizzata come desideri. Inoltre, seguire la guida API qui per i parametri corretti.
mikeDOTexe,

Prima di ciò, come ottenere anche l'ID del modulo, dove controllare questo codice.
logeshvaran,

3

In aggiunta a queste buone risposte aggiungerei:

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

È come chiamare un metodo di classe distante per una convalida del modulo. Penso che sia meglio quindi chiamare una funzione sopra nel file del modulo come nell'esempio fornito.


Non è più necessario passare al codice procedurale da OO.
colan

1

è possibile utilizzare il modulo di convalida Clientside . Alcuni ulteriori dettagli al riguardo (dalla sua pagina del progetto):

... aggiunge la convalida del client ( nota anche come "convalida del modulo Ajax") per tutti i moduli e i moduli web utilizzando jquery.validate . Il file jquery.validate.js incluso è patchato perché dovevamo poter nascondere messaggi vuoti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.