LoginSignup
2
2

More than 3 years have passed since last update.

[Drupal]ノードフォームのバリデーションを一時的に無効にする

Last updated at Posted at 2021-04-15

今回はノードのフォームにこんなボタンを追加してみます。

  • クリックするとノードが保存される
  • フォームの内容が本来バリデーションに引掛かるようなものでもそのまま保存できる

いわゆる「一時保存」ボタン(?)ですね。

ただし、タイトルは入力しないとDrupal\Core\Entity\EntityStorageException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null:というエラーで画面真っ白になるのでバリデーションを残す必要があります。

いろいろ試したのですが、自分の場合最終的に以下のようなコードになりました。

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_node_form_alter(&$form, $form_state, $form_id) {
  // デフォルトの保存ボタンの内容をコピーする.
  $form['actions']['save_as_draft'] = $form['actions']['submit'];
  // ラベルを変更する.
  $form['actions']['save_as_draft']['#value'] = '一時保存';
  // HTML5のバリデーションをオフにする.
  $form['#attributes']['novalidate'] = 'novalidate';
  // タイトル以外のバリデーションを無効化するハンドラーを追加.
  $form['actions']['save_as_draft']['#validate'][] = '_my_module_clear_errors';
}

function _my_module_clear_errors(&$form, $form_state) {
  // タイトルのエラーを取得する.
  $title_error = $form_state->getError($form['title']['widget'][0]['value']);
  // エラーを削除する.
  $form_state->clearErrors();
  // タイトルのエラーがある場合は元に戻す.
  if ($title_error) {
    $form_state->setErrorByName('title][0][value', $title_error);
  }
  // タイトルのエラーが無い場合はバリデーションが完了したことにする.
  else {
    $form_state->setTemporaryValue('entity_validated', TRUE);
  }

}

タイトルのエラーを残す処理が入ってるのでややこしいんですが、要点をまとめると以下のようになります。まず、hook_form_FORM_ID_alterでボタンを追加します。今回はノードのフォームに追加したいので、FORM_IDnode_formになります。

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_node_form_alter(&$form, $form_state, $form_id) {
  // デフォルトの保存ボタンの内容をコピーする.
  $form['actions']['save_as_draft'] = $form['actions']['submit'];
  // ラベルを変更する.
  $form['actions']['save_as_draft']['#value'] = '一時保存';
}

hook_form_FORM_ID_alterのドキュメント
form idの探し方

この時点で一時保存ボタンが追加されます。
スクリーンショット 2021-04-15 21.55.01.png

この状態で一時保存ボタンを押すとHTML5の必須チェックが動いて邪魔なので、
スクリーンショット 2021-04-15 21.55.52.png

こちらのコードでオフにしています。(フォーム全体のHTML5のバリデーションがオフになります。)

// HTML5のバリデーションをオフにする.
  $form['#attributes']['novalidate'] = 'novalidate';

これでDrupalのバリデーションが反応している状態になります。この時点で一時保存ボタンはラベル以外保存ボタンと同一なので、画像のようにバリデーションエラーが出て保存出来ない状態です。
スクリーンショット 2021-04-15 21.57.28.png

このバリデーションをオフにするには、

// タイトル以外のバリデーションを無効化するハンドラーを追加.
  $form['actions']['save_as_draft']['#validate'][] = '_my_module_clear_errors';

でバリデーションハンドラーを追加します。これで一時保存ボタンクリック時に_my_module_clear_errorsが呼び出されるようになります。_my_module_clear_errorsの中では、

  // エラーを削除する.
  $form_state->clearErrors();

でバリデーション時に出たエラーを全て無かったことにしています。(今回はカスタムモジュールが一個だけなので大丈夫ですが、いくつものモジュールがバリデーションハンドラーを追加してる場合は、この処理が一番最後に来るようにする必要があります。)

hookの呼び出し順を変える

ただし、Drupalの仕様上、単純にバリデーションを消すだけだとentity_validatedというフラグがFALSEのままなので、ノードのpresaveの段階でDrupal\Core\Entity\EntityStorageException: Entity validation was skipped.という例外がスローされてしまいます。そのため、最後に

$form_state->setTemporaryValue('entity_validated', TRUE);

でバリデーションが完了したことにしています。

タイトルのエラーは

// タイトルのエラーを取得する.
  $title_error = $form_state->getError($form['title']['widget'][0]['value']);

でいったん退避させて、

 // タイトルのエラーがある場合は元に戻す.
  if ($title_error) {
    $form_state->setErrorByName('title][0][value', $title_error);
  }

で元に戻しています。
public function FormState::getError
public function FormState::setErrorByName

これで一時保存ボタンを押すと、タイトル以外のフィールドでエラーがあっても何も出ません。タイトルだけ入力すればバリデーションをスルーして保存することができます。

スクリーンショット 2021-04-15 22.12.19.png

というわけで、ノードフォームのバリデーションを一時的に無効にする方法でした。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2