Как добавить пользовательский обработчик проверки в существующую форму / поле?

21

Как добавить собственный обработчик проверки в существующую форму (или поле формы) в Drupal 8?

У меня есть форма, которую я не создал. Я хочу добавить свои собственные правила проверки для некоторых полей при отправке формы.

Для Drupal 7, Пользовательская проверка формы? объясняет, как реализовать, hook_form_alter()а затем добавить ваш обработчик проверки] [1] в $form['#validate']массив, но в Drupal 8 формы являются классами. Проверка выполняется с помощью validateForm()метода, и я не знаю, как подключить мой код к этому.

AngularChef
источник
4
Возможный дубликат пользовательской проверки для формы?
Бумми
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';
Шабир А.
источник
Спасибо, Шабир. Таким образом, добавление собственного валидатора работает точно так же в D7 и D8. ;)
AngularChef
Точно, обратитесь к коду модуля узла. там много примеров
Шабир А.
2
Я только что попробовал, и это сработало отлично, спасибо. Обратите внимание, что вопреки тому, что указано в Справочнике по API формы D8 #validate(ваша ссылка), вы должны использовать $form_stateне массив (способ D7), а объект, реализующий FormStateInterface(способ D8). Другими словами, код в вашем пользовательском валидаторе должен быть аналогом кода, который вы найдете в оригинальном validateForm()методе.
AngularChef
25

Бердир дал правильный ответ, что ограничение - это правильный путь добавления валидации к полю в Drupal 8. Вот пример.

В приведенном ниже примере я буду работать с узлом типа podcast, который имеет поле с одним значением field_podcast_duration. Значение для этого поля должно быть отформатировано как ЧЧ: ММ: СС (часы, минуты и секунды).

Чтобы создать ограничение, необходимо добавить два класса. Первый - это определение ограничения, а второй - это валидатор ограничения. Оба они являются плагинами в пространстве имен Drupal\[MODULENAME]\Plugin\Validation\Constraint.

Во-первых, определение ограничения. Обратите внимание, что идентификатор плагина задан как «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]);
    }
  }
}

Наконец, нам нужно указать Drupal использовать наше ограничение field_podcast_durationна podcastтип узла. Мы делаем это в 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
источник
Если вам в итоге понадобятся другие значения поля для проверки вашего поля, вы можете добавить ограничение к типу контента. Смотрите это сообщение в блоге: lakshminp.com/entity-validation-drupal-8-part-2
ummdorian
1
API проверки формы D8 подробно объясняется на Drupal.org здесь. Предоставление пользовательского ограничения проверки
Сукхиндер Сингх
1
Поскольку этот вопрос конкретно касается API формы, а не API поля, как можно связать это ограничение с элементом формы (а не с полем сущности)?
Аарон Бауман
Элементы формы не могут иметь ограничений. Вы можете добавить проверку к определенному элементу формы, используя #element_validate. Посмотрите верхний ответ в этой теме - в D8 он работает так же, как и в D7 drupal.stackexchange.com/questions/86990/…
Jaypan
Убедитесь, что вы проверили $item = $items->first();и вернули NULL, если ничего не существует, иначе вы получите фатальную ошибку при редактировании поля: TypeError: Аргумент 2, переданный в Drupal \ Component \ Utility \ NestedArray :: getValue (), должен иметь тип массива, дано ноль, вызывается в /data/app/core/lib/Drupal/Core/Field/WidgetBase.php в строке 407 в Drupal \ Component \ Utility \ NestedArray :: getValue () (строка 69 в core / lib / Drupal / Component /Utility/NestedArray.php).
Иван Зугец,
16

Правильный способ сделать это для объекта содержимого, такого как узел, - зарегистрировать его как ограничение.

Видишь forum_entity_bundle_field_info_alter()и соответствующий? ForumLeafограничение проверки (обратите внимание, что нужны два класса).

Сначала это немного сложнее, но преимущество заключается в том, что он интегрирован в API валидации, поэтому ваша валидация не ограничивается системой форм, но может, например, работать и с узлами, отправленными через REST API.

Berdir
источник
Хорошая мысль: это что-то новое для тех, кто писал код для Drupal 7. Я уверен, что есть много пользователей, которые попытаются добавить обработчики проверки, когда ограничение будет более подходящим.
kiamlaluno
Бердир: Я исследовал эту опцию, пытаясь реализовать 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',); Но, похоже, модуль no-contrib реализовал эти изменения. Drupal.org/node/2185941
kiranking
8

Я хочу добавить немного света по этому вопросу. Добавление валидации точно такое же, как и раньше: в hook_form_alter:

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

Использование объекта values ​​внутри $ form_state в функции validate немного отличается. например:

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_experiment/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
}
nicholas.alipaz
источник
Это довольно близко. Только hook_form_FORM_ID_alterнужен идентификатор формы. Пользовательская функция проверки может быть любой, какой вы захотите. Кроме того, следуйте инструкциям API здесь для правильных параметров.
mikeDOTexe
До этого как узнать идентификатор формы, где проверить этот код.
Логешваран
3

В дополнение к этим хорошим ответам я бы добавил:

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

Это как вызвать метод удаленного класса для проверки формы. Я думаю, что лучше, чем вызывать вышеуказанную функцию в файле модуля, как в приведенном примере.

Pauleau
источник
Больше нет необходимости переходить в процедурный код из ОО.
Колан
1

Вы можете использовать модуль проверки клиента . Еще несколько подробностей об этом (со страницы проекта):

... добавляет проверку на стороне клиента (также известную как «Проверка формы Ajax») для всех форм и веб- форм, использующих jquery.validate . Включенный файл jquery.validate.js пропатчен, потому что нам нужно было иметь возможность скрывать пустые сообщения.

Мухаммед АТИФИ
источник