Como adiciono um manipulador de validação personalizado a um formulário / campo existente?

21

Como adiciono um manipulador de validação personalizado a um formulário existente (ou campo de formulário) no Drupal 8?

Eu tenho um formulário que não criei. Quero adicionar minhas próprias regras de validação em alguns campos quando o formulário é enviado.

Para o Drupal 7, validação personalizada de um formulário? explica implementar hook_form_alter()e, em seguida, adicione seu manipulador de validação] [1] à $form['#validate']matriz, mas no Drupal 8 os formulários são classes. A validação é feita através do validateForm()método e não sei como conectar meu código a isso.

AngularChef
fonte
4
Possível duplicata da validação personalizada de um formulário?
Bummi
1
Não é exatamente uma duplicata. Minha pergunta é para D8, seu link é para D7.
AngularChef
Me deparei com isso hoje e só queria anotar para outras pessoas se você não estiver usando o POST (eu queria um envio de URL para uma página de exibição existente) nem o validateForm nem o submitForm executados. Em retrospectiva, isso é óbvio ... mas passei 30 minutos tentando descobrir antes de perceber ....: /
ben.hamelin

Respostas:

19

A #validatepropriedade ainda é usada no Drupal 8. (Com a solução da Adi, você substituirá o validador existente)

Se você deseja adicionar seu validador personalizado além do padrão, precisará adicionar algo como isto em hook_form_FORM_ID_alter (ou similar):

$form['#validate'][] = 'my_test_validate';
Shabir A.
fonte
Obrigado, Shabir. Portanto, adicionar um validador personalizado funciona da mesma maneira em D7 e D8. ;)
AngularChef
Exatamente, consulte o código do módulo do nó. existem muitos exemplos lá
Shabir A.
2
Eu apenas tentei e funcionou perfeitamente, obrigado. Observe que, ao contrário do que diz a Referência da API do formulário D8 #validate(seu link), você não deve usá-lo $form_statecomo uma matriz (maneira D7), mas como um objeto implementado FormStateInterface(maneira D8). Em outras palavras, o código no seu validador personalizado deve ser análogo ao código encontrado em um validateForm()método original .
AngularChef
25

Berdir deu a resposta correta, que uma restrição é a maneira correta de adicionar validação a um campo no Drupal 8. Aqui está um exemplo.

No exemplo abaixo, trabalharei com um nó do tipo podcast, que possui o campo de valor único field_podcast_duration. O valor desse campo precisa ser formatado como HH: MM: SS (horas, minutos e segundos).

Para criar uma restrição, duas classes precisam ser adicionadas. O primeiro é a definição de restrição e o segundo é o validador de restrição. Ambos são plugins, no espaço de nomes de Drupal\[MODULENAME]\Plugin\Validation\Constraint.

Primeiro, a definição de restrição. Observe que o ID do plug-in é fornecido como 'PodcastDuration', na anotação (comentário) da classe. Isso será usado mais adiante.

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

Em seguida, precisamos fornecer o validador de restrição. Este nome desta classe será o nome da classe acima, com Validatoranexado a ele:

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

Finalmente, nós precisamos dizer Drupal para usar nosso restrição sobre field_podcast_durationo podcasttipo de nó. Fazemos isso em 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
fonte
Se você precisar de outros valores de campo para validar seu campo, poderá adicionar uma restrição ao tipo de conteúdo. Veja este post: lakshminp.com/entity-validation-drupal-8-part-2
ummdorian
1
A API de validação de formulário D8 foi explicada em detalhes no Drupal.org aqui. Fornecendo uma restrição de validação personalizada
Sukhjinder Singh
1
Como essa pergunta é especificamente sobre API de formulário, não API de campo, como anexar essa restrição a um elemento de formulário (não a um campo de entidade)?
Aaronbauman
Os elementos do formulário não podem ter restrições. Você pode adicionar a validação a um elemento específico do formulário usando #element_validate. Veja a resposta de topo sobre este tópico - ele funciona da mesma em D8 como D7 drupal.stackexchange.com/questions/86990/...
Jaypan
Verifique $item = $items->first();e retorne NULL se não houver nada; caso contrário, você receberá um erro fatal ao editar o campo: TypeError: o argumento 2 passado para Drupal \ Component \ Utility \ NestedArray :: getValue () deve ser do tipo array, nulo fornecido, chamado em /data/app/core/lib/Drupal/Core/Field/WidgetBase.php na linha 407 em Drupal \ Component \ Utility \ NestedArray :: getValue () (linha 69 do core / lib / Drupal / Component /Utility/NestedArray.php).
Ivan Zugec
16

A maneira correta de fazer isso para uma entidade de conteúdo como o nó é registrá-lo como uma restrição.

Veja forum_entity_bundle_field_info_alter()e o correspondente? ForumLeafrestrição de validação (observe que são necessárias duas classes).

Isso é um pouco mais complicado no começo, mas a vantagem é que ele está integrado à API de validação, portanto sua validação não se limita ao sistema de formulários, mas pode, por exemplo, também trabalhar com os nós enviados pela API REST.

Berdir
fonte
Ponto positivo: isso é algo novo para quem costumava escrever código para o Drupal 7. Tenho certeza de que há muitos usuários que tentarão adicionar manipuladores de validação quando uma restrição for mais apropriada.
kiamlaluno
Berdir: Eu explorei essa opção tentando implementar hook_entity_bundle_field_info_alter()(como descrito aqui ), mas nunca funcionou ... Parece haver um problema documentado com esse gancho: drupal.org/node/2346347 .
AngularChef
Existem alguns problemas, mas não acho que estejam relacionados ao seu problema. forum.module mostra que funciona. Compartilhe seu código, não é possível apontar possíveis problemas em sua implementação.
Berdir
1
Eu gostaria de seguir esse método, mas até que exista um bom exemplo de como usá-lo com tipos de dados (por exemplo, não específicos de campos) para verificar se alguma condição externa é atendida, eu fico com o formulário alterado. O artigo não se interessou por isso. Alguém poderia me indicar um lugar útil ou publicá-lo aqui? Obrigado.
Colan
e se precisarmos adicionar a validação existente como \ Drupal \ user \ Form \ UserLoginForm :: validateName (). No d7, era simples como $ form ['# validate'] = array ('user_login_name_validate', 'myother_validaion',); Mas parece não-contrib módulo implementado essas mudanças drupal.org/node/2185941
kiranking
8

Quero acrescentar um pouco mais de luz sobre esse assunto. A adição da validação é exatamente a mesma de antes: em hook_form_alter:

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

O uso do objeto de valores dentro do $ form_state na função validate é um pouco diferente. por exemplo:

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

  }
}

Portanto, não com um acesso direto ao objeto de variáveis ​​privadas, mas com uma função getter.

para obter mais informações, você pode ver um exemplo completo no meu github: https://github.com/flesheater/drupal8_modules_experiments/blob/master/webham_formvalidation/webham_formvalidation.module

Felicidades!

Nikolay Borisov
fonte
De fato é. Assim como eu escrevi no meu comentário acima. ;)
AngularChef 11/04
7

É muito parecido com o D7. Um exemplo 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
}
nicholas.alipaz
fonte
Isto é bem próximo. Somente o hook_form_FORM_ID_alterprecisa do ID do formulário. Você pode ter a função de validação personalizada como desejar. Além disso, siga o guia da API aqui para obter os parâmetros adequados.
MikeDOTexe 19/10/2016
Antes disso, como obter o ID do formulário e onde verificar esse código.
logeshvaran
3

Em complemento dessas boas respostas, eu acrescentaria:

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

É como chamar um método de classe distante para uma validação de formulário. Eu acho que é melhor do que chamar uma função acima no arquivo de módulo, como no exemplo dado.

Pauleau
fonte
Não é mais necessário entrar no código de procedimento do OO.
colan 1/11
1

você pode usar o módulo Validação do cliente . Mais alguns detalhes sobre ele (na página do projeto):

... adiciona validação do lado do cliente (também conhecida como "validação de formulário do Ajax") para todos os formulários e formulários da web usando jquery.validate . O arquivo jquery.validate.js incluído foi corrigido porque precisávamos poder ocultar mensagens vazias.

Mohammed ATIFI
fonte