I'm trying to process my custom form. However, code inside the submitForm function is never called. I have not seen any solutions, yet for getting Drupal 8's submitForm to be called.

namespace Drupal\archive\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;

class DateForm extends FormBase
    public function getFormId() { return 'archive_dateform'; }

    public function buildForm(array $form, FormStateInterface $form_state)
        $form['#action'] = Url::fromRoute('archive.dateform')->toString();

        $form['type'] = [
            '#type' => 'hidden',
            '#value' => $form_state->get('type')

        $form['year'] = [
            '#title'         => 'Year',
            '#type'          => 'number',
            '#size'          => 4,
            '#step'          => 1,
            '#default_value' => $form_state->get('year'),

        $options = [];
        for ($i=1; $i<=12; $i++) { $options[$i] = date('F', mktime(0, 0, 0, $i, 10)); }
        $form['month'] = [
            '#title'         => 'Month',
            '#type'          => 'select',
            '#options'       => $options,
            '#default_value' => $form_state->get('month'),
            '#empty_option'  => 'All',
            '#empty_value'   => 0

        $form['actions']['#type'] = 'actions';
        $form['actions']['submit'] = [
            '#type'        => 'submit',
            '#value'       => 'Search',
            '#button_type' => 'primary'
        return $form;

    public function validateForm(array &$form, FormStateInterface $form_state)
        $type = $form_state->get('type');
        if (!$type) {
            $form_state->setErrorByName('type', 'Missing Type');

    public function submitForm(array &$form, FormStateInterface $form_state)
        echo "submitForm\n";
        $type = $form_state->get('type');

        $year = (int)$form_state->get('year');
        if (!$year) { $year = (int)date('Y'); }
        $params = ['year'=>$year];

        $month = (int)$form_state->get('month');
        if ($month) { $params['month'] = $month; }

        $form_state->setRedirect("archive.$type", $params);

And using from a controller

namespace Drupal\archive\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\FormState;

class ArchiveController extends ControllerBase
    public function archive($type, $year, $month, $day)
        $year  = (int)$year;
        $month = (int)$month;
        $day   = (int)$day;

        if (!$year) { $year = (int)date('Y'); }

        if (!$month) {
            $date = "$year-01-01";
            $period = new \DateInterval('P1Y1D');
        else {
            if (!$day) {
                $date   = "$year-$month-01";
                $period = new \DateInterval('P1M1D');
            else {
                $date   = "$year-$month-$day";
                $period = new \DateInterval('P1D');
        $start = new \DateTime($date);
        $end   = new \DateTime($date);

        $manager = \Drupal::entityTypeManager();
        $query   = \Drupal::entityQuery('node')
                ->condition('type',    $type)
                ->condition('created', $start->format('U'), '>=')
                ->condition('created', $end  ->format('U'), '<')
                ->condition('status',  1);

        $form_state = new FormState();
        $form_state->set('type',  $type);
        $form_state->set('year',  $year);
        $form_state->set('month', $month);
        $form = \Drupal::formBuilder()->buildForm('Drupal\archive\Form\DateForm', $form_state);

        return [
            '#theme'   => 'archive_results',
            '#year'    => $year,
            '#start'   => $start,
            '#form'    => $form,
            '#results' => $manager->getViewBuilder('node')->viewMultiple(
  • If you're determining that the submit handlers is not reached because of your echo statement, that won't work. Change it to drupal_set_message("submit"); and remove the call to exit, and you will likely see the submit handler is called.
    – Jaypan
    May 19, 2017 at 18:20

3 Answers 3


I think your validation fails because you need to

$form_state->setValue('type',  $type);

in your controller instead of

$form_state->set('type',  $type);
  • Calling form_state->setValue() results in the fields not being rendered with those values. I'm not sure setValue() works that way. May 19, 2017 at 18:14
  • Did you also change '#default_value' => $form_state->get('year') to getValue() in your Form class? Think it's supposed to work that way. May 19, 2017 at 18:25
  • Okay, I think that is, a big part of my problem. I am now calling ->getValue() and ->setValue(), instead of just plain ->get() and ->set(). The validate() and submitForm() functions are, in fact, being called now. Now I just have to figure out why the values are not there when I call $form_state->getValue('type') from inside the validate() and submitForm() functions. (but that sounds like a topic for another question.) May 19, 2017 at 18:31
  • The type field is now coming across as well. I had to use #default_value, instead of #value. $form['type'] = [ '#type' => 'hidden', '#default_value' => $form_state->getValue('type') ]; I had thought that #default_value was only for non-hidden fields. Turns out, I was wrong (as I often am). May 19, 2017 at 18:52

You're returning the form in #form. Drupal doesn't process things inside #attributes. That means your form isn't really a form and a lot of the required processing isn't happening.

You need to define your template as accepting a render element, and then e.g. print it as elements.form and set as just 'form' => $form.

  • hey @Berdir how can I pass a form outside of #attributes? So I have a custom module, with a admin_settings_form and I need to load a twig file with this form and some other info. On my_module.form I have submitForm() function, but this is not working when I load a twig file, also I am passing form inside attributes like this return [ '#theme' => 'admin_form', '#teste' => $form, ]; My form appear as expected but when I click submit nothing happen, tried to remove '#' but no luck. Thanks in advance
    – Pedro
    Feb 10, 2021 at 15:54

It's an old post but it help me someway, so I will leave my answear, hope it will help someone.

I had same problem, my submit never called.

I pass my form as @Berdir said, like this:

MYMODULE.module file.

function MYMODULE_theme($existing, $type, $theme, $path) {
  return [
    'admin_form' => [
      'template' => 'admin-form',
      'render element' => 'form',

The on my form.php (MYMODULE/scr/Form/form.php) inside buildForm function

$form['simulator_values']['connection_services'] = [
  '#type' => 'textfield',
  '#maxlength' => 140,
  '#default_value' => $config->get('connection_services'),
  '#required' => TRUE,

$form['actions'] = [
  '#type' => 'actions'

$form['actions']['submit'] = [
  '#type' => 'submit',
  '#value' => $this->t('Submit ss')

return [
  '#theme' => 'admin_form',
  'render element' => $form,

Finally on twig side have to build form like this

<form {{ attributes }}>
  {{ form.form_build_id }}
  {{ form.form_token }}
  {{ form.form_id }}
  {{ form['render element'].advanced_info }}
  {{ form['render element'].actions.submit }}

Without form_build_id, form_token and form_id the submit button won't work.

As far I understood drupal need those information about the form to process everything on the right way, because you can have multiple forms on the same page.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.