Drupal and hook_form_alter()

I’ve been working with Drupal a lot in the last few months, and enjoying the hell out of it. Every day I’m learning some new tidbit, some interesting new scheme that at first seems confusing, but them usually makes me go, “Wow, that’s brilliant!”

I’ve been working with Drupal a lot in the last few months, and enjoying the hell out of it. Every day I’m learning some new tidbit, some interesting new scheme that at first seems confusing, but them usually makes me go, “Wow, that’s brilliant!”

Forms, for instance. Drupal offers a nice API to build and process forms; hook_form_alter() lets your modules modify forms defined in other modules, by adding fields, removing fields, even changing the back-end logic. At first I thought all this hook could do was fields, and I looked for something like hook_form_validate_alter(). But no, the real solution is even more elegant:

mymodule_form_alter(&$form, $form_state, $form_id) {
  if($form_id == 'form_whatever') {
    $form['#validate'][] = 'mymodule_form_validation';
  }
}

The $form['#validate'] looks like it contains an array of functions to be called when validating the form (likewise for $form['#submit'], I think). Simple and beautiful. The best part is, nothing is overwritten. Any module may alter any given form, and all their functions will be run. (The order in which they’re run can also be set: turns out modules have weight). I’ve thought for a while that Drupal seems ridiculously over-engineered, but that’s its strength: it allows developers to try out all sorts of crazy schemes like this. The more I learn about Drupal, the more I respect it.

Fieldsets and Drupal settings forms

Blame my lack of experience with Drupal. This week I was held up on a weird problem, and for once the online documentation didn’t seem to be helping.

I was coding a module, with a number of parameters. To keep things tidy, I decided to group them in fieldsets. And, not being too clear on the consequences, I set those fieldsets’ #tree parameter to TRUE. Here’s what it looked like:

Blame my lack of experience with Drupal. This week I was held up on a weird problem, and for once the online documentation didn’t seem to be helping.

I was coding a module, with a number of parameters. To keep things tidy, I decided to group them in fieldsets. And, not being too clear on the consequences, I set those fieldsets’ #tree parameter to TRUE. Here’s what it looked like (simplified example):

function foomodule_admin_settings() {
  $form['foo'] = array('#type' => 'fieldset', '#title' => t('Foo'), '#tree' => TRUE);

  $form['foo']['testmodule_bar'] = array('#type' => 'checkbox',
    '#title' => t('Bar'),
    '#description' => 'Bar',
  );

  $form['foo']['testmodule_baz'] = array('#type' => 'textfield',
    '#title' => t('Baz'),
    '#size' => 20,
    '#description' => t('Baz'));
    

  return system_settings_form($form);
}

What I found then was that the settings wouldn’t save. There were no errors, I got the normal message that “The configuration options have been saved.” but the values in the form were not updated.

After a bit of experimentation and reading up on how Drupal saves these settings, I figured out that the message was correct: my options really were saved, but not in the way I expected.

First, Drupal saves persistent variables in a table, named variable. Each record in this table has two fields: name and value. This field doesn’t exactly hold the value: for some reason Drupal also stores its type and (if it’s a string or array) its length. So (ignoring fieldsets for the moment) the value field for variable “bar” might be

b:1;

(“b” for “boolean”)

—while the value for “baz” could be

s:8:"whatever";

(“s” for “string”)

Apparently, if you’re editing these fields manually, you have to be really careful to make the stored length match the value’s actual length, or Bad Things will happen.

So here’s what it looks like is happening: if a form element is set to '#tree' => TRUE, its child elements’ values will not be stored in the database. Instead, it will get its own line in the variable table, with the value being an array of its child elements’ values. Which here, would look something like this:

a:2:{s:3:"bar";b:1;s:3:"baz";s:8:"whatever";}

This behaviour makes sense, but it seems to break everything about persistent variables. Fortunately there’s a simple solution. Just don’t set ‘#tree’ to TRUE, and the form elements’ values will store correctly. There we go, easy-peasy.

The Joy of Drupal, The Agony of Magento

I’ve been working for a small web development shop for the past few months. It’s interesting work, all open-source, all PHP-based. My first project was with Magento, and it was frakking painful.

I’ve been working for a small web development shop for the past few months. It’s interesting work, all open-source, all PHP-based. My first project was with Magento, and it was frakking painful.

I’m not just talking about the inevitable learning curve of tackling a new code base, and a (to me) whole new kind of CMS. The pain comes from an incredibly unintuitive code architecture that I keep having to wrap my brain around over and over.

From working with WordPress I was used to files for a given theme or plugin sitting together in one directory; this makes sense because they are all part of one module. Not so in Magento. Code, templates and other config elements are spread all across the system. From a design point of view I can sort of see why you’d want your business logic separate from your page templates, and both separate from your javascript, CSS, images and whatnot. You’d think it seems tidier, right? But in practice it makes extensions much harder to code than they should. To enhance or override core functionality, I have to know the exact names of these core classes and where they sit in the code directories, with no tidy hooks or API around which to build my functions.

And that’s not even counting the unholy tag soups in configuration XML files, with weird rules and redundancies that I haven’t been able to grok so far, so I just copy from existing samples and hope it eventually works. For example, according to A book to cook with Magento, config parameters defaults are set in the file config.xml, but if you want to view and modify them in the Web interface, that form is defined in another file, system.xml. My jaw literally dropped when I read up on this. How does it make any sense?

Mind-boggling or not, I’m grateful to blogs like A book to cook because there a serious dearth of online Magento documentation, official or otherwise. As often as not, googling particular issues led me to forum threads asking the same question I was researching… and nothing else, except maybe others chiming in to say they’ve got the same problem, or the same guy posting again a few months later to ask “What, nobody can help me?” Magento just doesn’t seem to have the same kind of active online community as, say, WordPress.

So what you end up with is a system that is way harder than it should be to enhance in any substantial way. Magento is open-source, sure, but if the source is a scary and confusing jungle of code, that doesn’t really help. No wonder people are willing to shell out megabucks for what you’d think are pretty minor extensions, instead of working it out themselves. Case in point:

The Backorder Button extension gives you the ability to display to your customers which of your products are currently on backorder.

If one of your products are on backorder, the Backorder Button extension will change the text’s of your “Add to Cart” buttons to “Backorder” (both in the product list, and on the product details pages), and the “Availability: In Stock” text’s (displayed on the product details pages) to “Availability: Available on Backorder.”. You can of course rewrite / customize the replacement text’s from the System Configuration.

$29 for this? Really? It took my coworker a couple of hours to figure out how to do it. But Magento’s deceptive: what looks simple and obvious isn’t always so. For instance, I had to abandon one extension that I thought at first would take me a few days, but later looked like many weeks of tedious hacking. It’s still on my to-do list, though: it’s useful functionality that should be present in an e-commerce system, but as far as I can tell nobody’s done in Magento yet. I tell you, if I ever make it work I’m going to be a fucking millionaire.

And then there’s Drupal

Now, I only have a few weeks’ actual experience with Drupal. I’d started playing with it a few months ago in preparation for a large volunteer web design project—that will end up using another CMS, but that’s another story. Then last month at work I finally got to do some actual Drupal development.

And it’s true, there’s a steep learning curve, which I’m only starting on. But unlike Magento, the learning process really isn’t frustrating. There are plenty of resources to help me with issues, and at the end of the day I just feel productive: Drupal development is way more straightforward—no mucking around with core classes, no having to create a dozen files in five different directories just for a simple “Hello World” module. Drupal is complex, but not overly complicated. It feels elegant. It makes sense, and I like that. Drupal is, frankly, a joy to work with.