The Case of the Drupal Form that Would Not Post

This is the story of a Drupal form that would not handle data submissions correctly when a large number of fields were present.

Working on a Drupal site, I came across the strange case of a settings form that would not save its values when a large number of fields were present. The form helps site admins associate certain vehicle model numbers to their appropriate model name, class, and group. A class is basically a collection of similar models, and a group is a collection of similar body types such as sedan, coupe, or SUV. We have many model numbers, most of which have no actual inventory on existence, so we don’t really display those in the settings form, but yesterday we found a bug that requires us to display all model numbers. When all model numbers are being displayed in the form, the form stops working properly, and no submission is ever saved.

The form submission process in Drupal is rather complex, but all you need to know at this point is that Drupal sets an element process_input in the $form_state array that it passes around the functions that handle form processing. If process_input is set to true, then the form submission is handled, otherwise it is not. This happens in the form_builder function defined in includes/form.inc. In order for process_input to be set to truethe form must either be a programmed form, or the $form_state['input']['form_id'] variable must be defined and equal to the $form_id variable that Drupal passes around the form processing functions.

In my case the form was failing to process because the $form_state['input']['form_id'] variable was set to null. At this point, you need to know that $form_state['input'] is set to the value of $_POST for forms submitted using the POST method. Looking at both, the form, and the parameters it sent in the request, I could see that there was a form_id input, and that it was being sent with a value equal to $form_id but PHP had no knowledge of it. It turns out the request wasn’t being fully caught by PHP.

At this point, my guess is that the request is too big for my server settings, or there are too many fields being sent. PHP has settings for both, the maximum size of a POST request that it handles, and the maximum number of input variables. As far as I know there are two places were you can modify these settings: the php.ini file, and the .htaccess file. The preferred way would be to modify the php.ini file, but depending on your host you may not have access to that. In that case, using an .htaccess file would be your best option.

In this stackoverflow question you can get an idea of how to go about modifying the values in case you ever encounter the same issue.

Drupal Error: PDOException: SQLSTATE[42000]: Syntax error or access violation: 1231 Variable ‘sql_mode’ can’t be set to the value of ‘NO_AUTO_CREATE_USER’ in lock_may_be_available()

I was setting up a drupal 7 site on a Mac OS computer, and got the error in the title when trying to sync the local DB with a remote DB. After a lot of time, the problem came down to a mysql incompatibility. I had installed MySQL 8, and had to downgrade to MySQL 5.7.24. The full error message contained a reference to the file includes/loc.inc line 167 as the source of the exception, but scrolling a little up the file I saw a catch statement in the lock_acquire function, and decided to dump the error caught. It turns out the exception is actually thrown on file includes/database/mysql/database.inc, line 94, in the constructor of the DatabaseConnection_mysql object. In the constructor is where Drupal tries to set sql_mode to a value which MySQL 8 is rejecting. For more information visit https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html#mysql-nutshell-removals

Nested Form Input in Drupal

HTML forms are made up of input controls that, when submitted, are converted into an array of values. For example, consider the following form:

<form method="post" action="/">
<input type="text" name="input1" />
<input type="text" name="input2" />
<input type="submit" name="send" value="Send" />
</form>

When a user fills out this form, and submits it, the data is sent by the browser using the POST HTTP method in this fashion:

input1: value
input2: value
send: send

If you were to read that with PHP, you would have to access the `$_POST` array, which would contain those key-value pairs. However, some forms are complex enough that flat arrays like that make it hard to work with the data. In those cases it is useful to get multidimensional arrays. HTML allows you to do just that:

<form method="post" action="/">
<input type="text" name="persons[0][name]" />
<input type="text" name="persons[0][address]" />
<input type="text" name="persons[1][name]" />
<input type="text" name="persons[1][address]" />
<input type="text" name="persons[2][name]" />
<input type="text" name="persons[2][address]" />
<input type="submit" name="send" value="Send" />
</form>

In PHP that results in the following `​$_POST` array

Array
(
 [persons] => Array(
   [0] => Array(
     [name] => john
     [address] => smith
   )
   [1] => Array(
     [name] => jane
     [address] => doe
   )
   [2] => Array(
     [name] => jason
     [address] => foo
   )
 )
 [send] => send
)

As you can see, the information for `persons` is now nicely arranged in an array of arrays, each of which contains the information associated with a person.

If you want to replicate that same structure in Drupal, it is quite easy. Using the Drupal Form Api you can easily create form arrays that Drupal knows how to render. But those forms usually submit data in the flat array form. If you want to get multidimensional array like we did above, you need to use the `#tree` attribute on the element that you want to be the root array member. For example, to get the same input we have above, you would do something like:

$form = [
  'persons' => [
    '#type' => 'markup',
    '#tree' => TRUE,
    '0' => [
      'name' => [
        '#type' => 'textfield'
      ],
      'address' => [
        '#type' => 'textfield'
      ],
    ],
    '1' => [
      'name' => [
        '#type' => 'textfield'
      ],
      'address' => [
        '#type' => 'textfield'
      ],
    ],
    '2' => [
      'name' => [
        '#type' => 'textfield'
      ],
      'address' => [
        '#type' => 'textfield'
      ],
    ],
  ],
];

If you were to omit the `#tree` attribute on the `persons` element in the form array, you would get a flat array of values with name, and address values for the last input fields only. Also note that the nested arrays begin at the point where the `#tree` attribute is specified. For example, if `persons` was itself a child of another element called `personal_data`, the resulting POST data wold remain the same, unless the `#tree` attribute was specified in the `personal_data` element, in which case, the `persons` array would also be child to a `personal_data` member in the `values` array where Drupal saves the submitted data when dealing with form submissions.

404 on Private Drupal Files

A few days ago, it was brought to my attention that some links pointing to private images in a drupal site were not working. The images are submitted by users of the site to request an estimate on repairs for their luxurious cars, and they go to the `private` directory in the drupal site.

For those who don’t know, drupal uses different types of files, such as public, and private. Private files, with the protocol `private://` live in a different place than public ones in the server. When Drupal builds a link to the file, it uses a path that is not really the actual path on disk. The path usually starts with  `system/files/` , but in the drupal directory structure there is no directory called `system`. This means a redirection is made from that url to the actual path of the file in the system.

For some reason, however, when trying to access a private file in the site, all that was returned was a 404 error. This started to happen after the site was moved to a new server. Tracking the problem I found that the issue was that the drupal function drupal_fast_404 was being called in the settings.php file. This function takes care of returning a 404 page whenever the requested url doesn’t match certain criteria. In this case, the fix was to simply edit the 404_fast_paths_exclude to make sure that any path starting with system/files/ and ending with a recognized image extension would not fast 404.