The Happy Phase

So you have an idea for a fancy new Drupal 7 custom field. You read the Field API documentation on how to create one.

As you build the field, you realize you need custom data manipulation during the loading of the field as well as just prior to saving it. No big deal, you think to yourself... I'll just use hook_field_load and hook_field_presave. All seems fine with your module. You test thoroughly using stock Drupal 7. You test by installing a number of popular modules such as Views, ctools, pathauto, and token, and your custom field continues to work just fine.

 

The Surprised and Confused Phase

You put your module out there for everyone to use, and your module is used on hundreds, maybe even thousands of sites. Then one day, bam! you get a bug report that baffles you. Your custom field's data is corrupt, but only for some users, and only sporadically. Diving deep into the problem for a few hours, and you may find what appears to be the cause - hook_field_presave isn't always getting called. But why?!? The reports are sporadic, and you can't quite figure out why it works most of the time, but fails occasionally for some people.

 

The Still Surprised but Getting Angry Phase

Well, it turns out that it is a known issue that hook_field_presave isn't always called reliablyis not called when the default value is used for a field, and from my testing, is not called when a node is saved twice during the same request (which may be directly related to the other issues mentioned). This third issue is the one that bit me hard in a recent project. Thankfully the solution to all three of these is the same. But before we get to the solution, let's first examine why this issue could go unnoticed during development and testing for so long.

Some modules, such as State Machine (version 7.x-3.x), Workbench Moderation, Drafty (a dependency of the other two), and others, trigger multiple node saves and loads during a single request. During this rapid-fire save of multiple revisions of the same node, the presave hook is not called after the first save, and the data is stored without your fancy presave processing, causing the data to fail to load properly because your hook_field_load code is assuming hook_field_presave executed before the data was written to the database, and this gives the appearance that the data was corrupted.

 

The Angry but Hopeful Phase

So how to fix it? Easy. You can't fix it in core, but you can fix it in your own custom module. While hook_field_presave is inconsitent, hook_field_insert and hook_field_update are very reliable, and execute just before the data is saved to the database. So the answer is to move your code out of presave and into insert and update.

Following best practices, you wouldn't just copy/paste your code into both functions, but instead should create a function that can be called from both hook_field_insert and hook_field_update. This will ensure that your code will be consistently called before the data is saved to the database.

 

The "Yay! The Solution is Simple!" Phase

If you already have a module that is using hook_field_presave, and your module's name is mymodule, then the change is simple. Just change:

mymodule_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
    ...
}

to:

_mymodule_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
    ...
}
mymodule_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
    _mymodule_field_presave($entity_type, $entity, $field, $instance, $langcode, $items);
}
mymodule_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
    _mymodule_field_presave($entity_type, $entity, $field, $instance, $langcode, $items);
}

This ensures the code is called before insertion into the database regardless of default values, multiple saves, or other behaviors that cause hook_field_presave to fail to execute.

And now you can be done troubleshooting and can get back to the coding you love. Happy coding!