Drupal → Порядок и логика работы #validate и #submit колбаков в форме

03.02.2023

1. Форма без #validate и #submit

Если в buildForm() у формы и кнопок не указать свойств #validate и #submit, то при отправке формы поочерёдно выполнятся методы validateForm() и submitForm():

class CustomForm extends FormBase {

  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    return $form;
  }

  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // 1
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    // 2
  }
 
}

Это происходит потому, что друпал добавляет эти свойства всем формам автоматически:

// \Drupal\Core\Form\FormBuilder::prepareForm()
$form['#validate'][] = '::validateForm';
$form['#submit'][] = '::submitForm';

2. Форма с #validate и #submit

Если добавить форме свойства #validate и #submit, то поочерёдно выполнятся методы customValidate(), validateForm(), customSubmit(), submitForm():

class CustomForm extends FormBase {

  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    $form['#validate'][] = '::customValidate';
    $form['#submit'][] = '::customSubmit';

    return $form;
  }

  public function customValidate(array &$form, FormStateInterface $form_state): void {
    // 1
  }
  
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // 2
  }

  public function customSubmit(array &$form, FormStateInterface $form_state): void {
    // 3
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    // 4
  }

}

3. Кнопка с #validate и #submit

Если прописать кнопке #validate и #submit, то выполнятся только они, тогда как колбаки формы будут проигнорированы:

class CustomForm extends FormBase {

  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#validate' => ['::buttonValidate'],
      '#submit' => ['::buttonSubmit'],
    ];

    $form['#validate'][] = '::customValidate';
    $form['#submit'][] = '::customSubmit';

    return $form;
  }

  public function buttonValidate(array &$form, FormStateInterface $form_state): void {
    // 1
  }

  public function customValidate(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }
  
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

  public function buttonSubmit(array &$form, FormStateInterface $form_state): void {
    // 2
  }

  public function customSubmit(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

}

4. Кнопка с #type=>button

Кнопки с '#type' => 'button' ведут себя чуть иначе, #validate колбаки работают как прежде, а вот любые #submit не выполняются, даже если навесить их на саму кнопку:

class CustomForm extends FormBase {

  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['submit'] = [
      '#type' => 'button',
      '#value' => $this->t('Submit'),
      '#validate' => ['::buttonValidate'],
      '#submit' => ['::buttonSubmit'],
    ];

    $form['#validate'][] = '::customValidate';
    $form['#submit'][] = '::customSubmit';

    return $form;
  }

  public function buttonValidate(array &$form, FormStateInterface $form_state): void {
    // 1
  }

  public function customValidate(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }
  
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

  public function buttonSubmit(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

  public function customSubmit(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    // не выполнится
  }

}

5. #submit в hook_form_alter()

Работает как прежде, но меняется порядок выполнения, потому что hook_form_alter() запускается после buildForm() и \Drupal\Core\Form\FormBuilder::prepareForm():

class CustomForm extends FormBase {

  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    return $form;
  }

  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // 1
  }

  public function submitForm(array &$form, FormStateInterface $form_state): void {
    // 3
  }
 
}
function modulename_form_custom_form_alter(array &$form, FormStateInterface $form_state): void {
  $form['#validate'][] = 'modulename_form_custom_form_validate';
  $form['#submit'][] = 'modulename_form_custom_form_submit';
}

function modulename_form_custom_form_validate(array &$form, FormStateInterface $form_state): void {
  // 2
}

function modulename_form_custom_form_submit(array &$form, FormStateInterface $form_state): void {
  // 4
}

Если нужно выполнить свой колбак раньше основного, то просто делаем array_unshift():

function modulename_form_custom_form_alter(array &$form, FormStateInterface $form_state): void {
  array_unshift($form['#validate'], 'modulename_form_custom_form_validate');
  array_unshift($form['#submit'], 'modulename_form_custom_form_submit');
}
Написанное актуально для
Drupal 8+
Похожие записи

Добавить комментарий