Bagaimana cara menambahkan penangan validasi khusus ke formulir / bidang yang ada?

21

Bagaimana cara menambahkan penangan validasi khusus ke formulir yang ada (atau bidang formulir) di Drupal 8?

Saya memiliki formulir yang belum saya buat. Saya ingin menambahkan aturan validasi saya sendiri pada beberapa bidang ketika formulir dikirimkan.

Untuk Drupal 7, validasi khusus untuk formulir? menjelaskan untuk mengimplementasikan hook_form_alter()dan kemudian menambahkan penangan validasi Anda] [1] ke $form['#validate']array, tetapi dalam Drupal 8 form adalah kelas. Validasi dilakukan melalui validateForm()metode dan saya tidak tahu cara memasukkan kode saya ke dalamnya.

AngularChef
sumber
4
Kemungkinan duplikat validasi khusus untuk formulir?
Bummi
1
Ini bukan duplikat. Pertanyaan saya adalah untuk D8, tautan Anda adalah untuk D7.
AngularChef
Saya menemukan ini hari ini dan hanya ingin mencatat untuk orang lain jika Anda tidak menggunakan POST (saya ingin pengiriman URL ke halaman tampilan yang ada) baik validateForm maupun menjalankan submitForm. Kalau dipikir-pikir, ini sudah jelas .... tetapi saya menghabiskan 30 menit untuk mencari tahu sebelum saya sadar ....: /
ben.hamelin

Jawaban:

19

The #validateproperti masih digunakan di Drupal 8. (Dengan solusi Adi Anda akan menimpa validator yang ada)

Jika Anda ingin menambahkan validator khusus Anda sebagai tambahan pada default, Anda harus menambahkan sesuatu seperti ini di hook_form_FORM_ID_alter (atau serupa):

$form['#validate'][] = 'my_test_validate';
Shabir A.
sumber
Terima kasih, Shabir. Jadi, menambahkan validator khusus berfungsi sama di D7 dan D8. ;)
AngularChef
Tepat, konsultasikan kode modul simpul. ada banyak contoh di sana
Shabir A.
2
Saya baru saja mencobanya dan itu berhasil dengan sempurna, terima kasih. Harap perhatikan bahwa bertentangan dengan apa yang dikatakan Referensi API Formulir D8 untuk #validate(tautan Anda), Anda tidak boleh menggunakan $form_statesebagai array (cara D7), tetapi sebagai objek yang mengimplementasikan FormStateInterface(cara D8). Dengan kata lain, kode di validator khusus Anda harus analog dengan kode yang Anda temukan dalam validateForm()metode asli .
AngularChef
25

Berdir memberikan jawaban yang benar, bahwa kendala adalah cara yang benar untuk menambahkan validasi ke bidang di Drupal 8. Berikut adalah contohnya.

Dalam contoh di bawah ini, saya akan bekerja dengan simpul tipe podcast, yang memiliki bidang nilai tunggal field_podcast_duration. Nilai untuk bidang ini perlu diformat sebagai HH: MM: SS (jam, menit dan detik).

Untuk membuat batasan, dua kelas perlu ditambahkan. Yang pertama adalah definisi kendala, dan yang kedua adalah validator kendala. Keduanya adalah plugin, di namespace of Drupal\[MODULENAME]\Plugin\Validation\Constraint.

Pertama, definisi kendala. Perhatikan bahwa ID plugin diberikan sebagai 'PodcastDuration', dalam anotasi (komentar) kelas. Ini akan digunakan lebih jauh ke bawah.

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

Selanjutnya, kita perlu menyediakan validator kendala. Nama kelas ini akan menjadi nama kelas dari atas, dengan Validatormenambahkannya:

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

Akhirnya, kita perlu memberitahu Drupal menggunakan kendala kami pada field_podcast_durationpada podcastjenis node. Kami melakukan ini di 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
sumber
Jika Anda akhirnya membutuhkan nilai bidang lain untuk memvalidasi bidang Anda, Anda bisa menambahkan batasan untuk tipe konten. Lihat posting blog ini: lakshminp.com/entity-validation-drupal-8-part-2
ummdorian
1
API Validasi Formulir D8 telah dijelaskan secara rinci di Drupal.org di sini. Memberikan batasan validasi khusus
Sukhjinder Singh
1
Karena pertanyaan ini khusus tentang API Formulir, bukan API Bidang, bagaimana cara seseorang melampirkan batasan ini ke elemen formulir (bukan bidang entitas)?
aaronbauman
Elemen formulir tidak dapat memiliki kendala. Anda bisa menambahkan validasi ke elemen bentuk tertentu menggunakan #element_validate. Lihat jawaban teratas di utas ini - ini berfungsi sama di D8 seperti D7 drupal.stackexchange.com/questions/86990/…
Jaypan
Pastikan Anda memeriksa $item = $items->first();dan mengembalikan NULL jika tidak ada di sana atau Anda akan mendapatkan kesalahan fatal saat mengedit bidang: TypeError: Argumen 2 diteruskan ke Drupal \ Component \ Utility \ NestedArray :: getValue () harus dari jenis array, null diberikan, dipanggil di /data/app/core/lib/Drupal/Core/Field/WidgetBase.php pada baris 407 di Drupal \ Component \ Utility \ NestedArray :: getValue () (baris 69 core / lib / Drupal / Component /Utility/NestedArray.php).
Ivan Zugec
16

Cara yang benar untuk melakukan ini untuk entitas konten seperti simpul adalah mendaftarkannya sebagai kendala.

Lihat forum_entity_bundle_field_info_alter()dan yang sesuai? ForumLeafkendala validasi (perhatikan bahwa ada dua kelas yang diperlukan).

Itu sedikit lebih rumit pada awalnya, tetapi keuntungannya adalah ia diintegrasikan ke dalam API validasi, sehingga validasi Anda tidak terbatas pada sistem formulir tetapi dapat, misalnya, juga bekerja dengan node yang dikirimkan melalui REST API.

Berdir
sumber
Poin bagus: Ini adalah sesuatu yang baru bagi yang biasa menulis kode untuk Drupal 7. Saya yakin ada banyak pengguna yang akan mencoba menambahkan penangan validasi ketika kendala lebih tepat.
kiamlaluno
Berdir: Saya memang mengeksplorasi opsi ini dengan mencoba mengimplementasikan hook_entity_bundle_field_info_alter()(seperti dijelaskan di sini ) tetapi tidak pernah berhasil ... Tampaknya ada masalah yang terdokumentasi dengan kait ini: drupal.org/node/2346347 .
AngularChef
Ada beberapa masalah tetapi saya tidak berpikir mereka terkait dengan masalah Anda. forum.module menunjukkan bahwa ia berfungsi. Bagikan kode Anda, jika tidak mungkin menunjukkan kemungkinan masalah dalam implementasi Anda.
Berdir
1
Saya ingin menggunakan metode ini, tetapi sampai ada contoh yang baik tentang bagaimana menggunakannya dengan tipe data (yaitu tidak spesifik lapangan) untuk memeriksa apakah beberapa kondisi eksternal terpenuhi, saya terjebak dengan form alter. Artikel tidak menggali ini. Akankah seseorang dengan ramah menunjukkan kepada saya suatu tempat yang berguna, atau mempostingnya di sini? Terima kasih.
colan
bagaimana jika kita perlu menambahkan validasi yang ada seperti \ Drupal \ user \ Form \ UserLoginForm :: validateName (). Di d7 itu sederhana seperti $ form ['# validate'] = array ('user_login_name_validate', 'myother_validaion',); Tetapi tampaknya modul no-contrib menerapkan perubahan ini drupal.org/node/2185941
kiranking
8

Saya ingin menambahkan sedikit lebih banyak tentang masalah ini. Penambahan validasi persis sama seperti sebelumnya: di hook_form_alter:

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

Penggunaan objek nilai di dalam $ form_state dalam fungsi validasi sedikit berbeda. misalnya:

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

  }
}

Jadi tidak dengan akses langsung ke objek variabel pribadi, melainkan dengan fungsi pengambil.

untuk info lebih lanjut, Anda dapat melihat contoh lengkapnya di github saya: https://github.com/flesheater/drupal8_modules_experiments/blob/master/webham_formvalidation/webham_formvalidation.module

tepuk tangan!

Nikolay Borisov
sumber
Memang itu. Seperti yang saya tulis di komentar saya di atas. ;)
AngularChef
7

Ini sangat mirip dengan di D7. Contoh lengkap:

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
sumber
Ini cukup dekat. Hanya hook_form_FORM_ID_alterperlu form ID. Anda dapat memiliki fungsi validasi khusus menjadi apa pun yang Anda inginkan. Juga, ikuti panduan API di sini untuk parameter yang tepat.
mikeDOTexe
Sebelum itu, cara mendapatkan id formulir juga, di mana memeriksa kode ini.
logeshvaran
3

Sebagai pelengkap dari jawaban baik ini saya akan menambahkan:

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

Ini adalah cara memanggil metode kelas jauh untuk validasi formulir. Saya pikir lebih baik memanggil fungsi di atas dalam file modul seperti pada contoh yang diberikan.

Pauleau
sumber
Tidak perlu lagi beralih ke kode prosedural dari OO.
colan
1

Anda dapat menggunakan modul Validasi Clientside . Beberapa detail lebih lanjut tentang hal ini (dari halaman proyeknya):

... menambahkan validasi di sisi klien (alias "validasi formulir Ajax") untuk semua formulir dan formulir web menggunakan jquery.validate . File jquery.validate.js yang disertakan ditambal karena kami harus dapat menyembunyikan pesan kosong.

Mohammed ATIFI
sumber