既存のフォーム/フィールドにカスタム検証ハンドラーを追加するにはどうすればよいですか?


21

Drupal 8の既存のフォーム(またはフォームフィールド)にカスタム検証ハンドラーを追加するにはどうすればよいですか?

作成していないフォームがあります。フォームの送信時に、いくつかのフィールドに独自の検証ルールを追加したい。

Drupal 7の場合、フォームのカスタム検証?hook_form_alter()検証ハンドラー] [1] を実装して$form['#validate']配列に追加する方法を説明していますが、Drupal 8ではフォームはクラスです。検証はvalidateForm()メソッドを介して行われ、コードをプラグインに組み込む方法がわかりません。



1
それは正確に複製ではありません。私の質問はD8、リンクはD7です。
AngularChef

今日私はこれに出会いましたが、あなたがPOSTを使用していない場合(既存のビューページへのURL送信が必要でした)、validateFormもsubmitFormも実行していない場合、他の人に注意したいだけです。後知恵でこれは明らかです....しかし、私は気づく前にそれを理解しようとして30分を費やしました....:/
ben.hamelin

回答:


19

この#validateプロパティは引き続きDrupal 8で使用されます(Adiのソリューションを使用すると、既存のバリデーターをオーバーライドできます)。

デフォルトに加えてカスタムバリデーターを追加したい場合、hook_form_FORM_ID_alter(または同様の)に次のようなものを追加する必要があります:

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

ありがとう、Shabir。したがって、カスタムバリデータの追加は、D7とD8でもまったく同じように機能します。;)
AngularChef

正確には、ノードモジュールのコードを参照してください。そこには多くの例があります
シャビールA.

2
試したところ、完璧に機能しました。ありがとうございます。D8 Form API Referenceの#validate(あなたのリンク)とは反対に$form_state、配列(D7の方法)ではなく、オブジェクトの実装FormStateInterface(D8の方法)として使用する必要があることに注意してください。つまり、カスタムバリデーターのコードは、元のvalidateForm()メソッドにあるコードに類似している必要があります。
AngularChef

25

Berdirは、制約がDrupal 8のフィールドに検証を追加する正しい方法であるという正しい答えを与えました。ここに例を示します。

以下の例podcastでは、単一の値fieldを持つtypeのノードで作業しますfield_podcast_duration。このフィールドの値は、HH:MM:SS(時間、分、秒)の形式にする必要があります。

制約を作成するには、2つのクラスを追加する必要があります。1つ目は制約定義で、2つ目は制約バリデーターです。これらは両方とも、の名前空間にあるプラグインですDrupal\[MODULENAME]\Plugin\Validation\Constraint

まず、制約の定義。プラグインIDは、クラスの注釈(コメント)で「PodcastDuration」として指定されていることに注意してください。これはさらに下で使用されます。

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

次に、制約バリデーターを提供する必要があります。このクラスのこの名前は、上記のクラス名にValidator追加されます:

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

最後field_podcast_durationに、podcastノードタイプで制約を使用するようにDrupalに指示する必要があります。以下でこれを行います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');
  }
}

フィールドを検証するために他のフィールド値が必要になる場合、コンテンツタイプに制約を追加できます。このブログ投稿を参照してください:lakshminp.com/entity-validation-drupal-8-part-2
ummdorian

1
D8 Form Validation APIは、Drupal.orgで詳細に説明されています。カスタム検証制約の提供
スフジンダーシン

1
この質問は、フィールドAPIではなく、フォームAPIに関するものであるため、この制約を(エンティティフィールドではなく)フォーム要素にどのように関連付けますか?
アーロンバウマン

フォーム要素には制約を設定できません。#element_validateを使用して、特定のフォーム要素に検証を追加できます。このスレッドの一番上の答えを参照してください-それはD7のようD8でも同じ作品drupal.stackexchange.com/questions/86990/...
Jaypan

$item = $items->first();フィールドが何もない場合はチェックしてNULLを返すようにしてください。そうでない場合は、フィールドの編集時に致命的なエラーが発生します。 nullが指定され、Drupal \ Component \ Utility \ NestedArray :: getValue()の行407の/data/app/core/lib/Drupal/Core/Field/WidgetBase.phpで呼び出されます(core / lib / Drupal / Componentの行69 /Utility/NestedArray.php)。
イヴァンズジェック

16

nodeのようなコンテンツエンティティに対してこれを行う正しい方法は、それを制約として登録することです。

を参照forum_entity_bundle_field_info_alter()し、対応する?ForumLeaf検証制約(2つのクラスが必要なことに注意してください)。

最初はもう少し複雑ですが、利点は検証APIに統合されているため、検証はフォームシステムに限定されず、たとえばREST APIを介して送信されたノードでも機能します。


良い点:これは、Drupal 7のコードを書いた人にとっては新しいものです。制約がより適切な場合に検証ハンドラーを追加しようとするユーザーがたくさんいるはずです。
キアムルノ

Berdir:(ここでhook_entity_bundle_field_info_alter()説明したよう)実装しようとしてこのオプションを検討しましたが、機能しませんでした...このフックには文書化された問題があるようです:drupal.org/node/2346347
AngularChef

いくつかの問題がありますが、それらはあなたの問題に関連するとは思いません。forum.moduleは、それが機能することを示しています。コードを共有してください。そうしないと、実装で起こりうる問題を指摘することはできません。
ベルディール

1
この方法を使いたいのですが、何らかの外部条件が満たされているかどうかを確認するためにデータ型(フィールド固有ではない)で使用する方法の良い例があるまで、フォームの変更に固執しています。記事はこれを掘り下げていません。誰かが親切に私を便利な場所に指し示すか、ここに投稿しますか?ありがとう。
コラン

\ Drupal \ user \ Form \ UserLoginForm :: validateName()のような既存の検証を追加する必要がある場合はどうなりますか。d7では、$ form ['#validate'] = array( 'user_login_name_validate'、 'myother_validaion'、);のように簡単でした。しかし、これらの変更を実装しているモジュールはないようですdrupal.org/node/2185941
18年

8

この問題にもう少し光を加えたいと思います。検証の追加は、以前とまったく同じです:hook_form_alter:

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

validate関数での$ form_state内での値オブジェクトの使用は少し異なります。例えば:

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

  }
}

したがって、プライベート変数オブジェクトに直接アクセスするのではなく、ゲッター関数を使用します。

詳細については、私のgithubで完全な例を見ることができます:https : //github.com/flesheater/drupal8_modules_experiments/blob/master/webham_formvalidation/webham_formvalidation.module

乾杯!


確かにそうです。上記のコメントで書いたように。;)
AngularChef

7

D7とほぼ同じです。完全な例:

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
}

これはかなり近いです。hook_form_FORM_ID_alterフォームID のみが必要です。カスタム検証関数を自由に設定できます。また、適切なパラメーターについては、ここAPIガイドに従ってください。
mikeDOTexe

その前に、フォームIDを取得する方法、このコードをチェックする場所。
logeshvaran

3

これらの良い答えを補完して、私は追加します:

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

これは、フォーム検証のために遠隔クラスメソッドを呼び出す方法です。与えられた例のようにモジュールファイルで上記の関数を呼び出すよりも良いと思います。


OOから手続き型コードに飛び込む必要はもうありません。
コラン

1

クライアントサイド検証モジュールを使用できます。それに関するいくつかの詳細(プロジェクトページから):

...は、使用しているすべてのフォームとWebフォームのためのクライアント側の検証(別名「Ajaxのフォームの検証」)が追加されますjquery.validateを。空のメッセージを非表示にする必要があるため、付属のjquery.validate.jsファイルにパッチが適用されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.