Problem/Motivation
The fieldt-API provides several method to manipulate the data between the form, the formatters and the database (hook_field_presave, hook_field_load, hook_field_formatter_prepare_view, …). Ideally this would lead to a clean separation of up to 4 (possibly different) datastructures of a field item: One in the database, one in the application, one for the widget, one for the formatter.
Sadly this is not the case. Those hooks are not called in any reliable order, because the responsibility for calling the hooks at least partially lies with the entity modules (!).
This circumstance has already lead to bugs and workarounds in modules that implement field types. A good showcase is the taxonomy module, as it provides more than one widget for it's field-type and uses slightly different data-structures for them.
Proposed resolution
I propose to include calls to the corresponding hooks directly in the field-API functions. For backward compatibility a flag may be added that guarantees that each of those hooks is only executed once when it's additionally called by contrib.
Attached is a patch that moves the calls to field_attach_presave() from the entity implementations to field_attach_insert() and field_attach_update().
Remaining tasks
- the flag/check that guarantees that hooks are called only once for backward compatibility has to be added. (ie. use $items['#field_hook'] = 'field_presave';
- check if the same applies to other field hooks.
User interface changes
none.
API changes
The flag for backward-compatibility must not be written to. As there is no module using this name already no issues are to be expected.
Related Issues
This seems to be the root-cause for problems like this:
#1140188: Fatal errors during or after adding default values for autocomplete widgets
The fact that is not called on default values (field settings form) is reported here:
#1944678: On field settings form, hook_field_load() and hook_field_presave() are not called.
#1899498: Field default values do not get hook_field_presave() run on them
| Comment | File | Size | Author |
|---|---|---|---|
| #5 | fields.dot_.png | 125.4 KB | torotil |
| field_guarantee_hook_field_presave.patch | 2.23 KB | torotil |
Comments
Comment #1
torotil commentedComment #2
David_Rothstein commentedI don't think I understand where there's a bug in the field API here... if a particular entity-providing module doesn't call this function, that seems like a bug in that module.
I guess you're referring to this:
But I'm not sure what you mean by that exactly. What is a specific place where "default values from the instance settings" are used and where field_attach_presave() doesn't get called but you think it should?
As for the patch, looks like it would change the order in which several hooks are called, so it doesn't seem backwards-compatible for Drupal 7?
Comment #3
torotil commentedThe problem here lies with field_attach_instance():
The first line of field_attach_insert() possibly loads new default values - that were never passed through hook_field_presave() - and in the same function stores them to the field-storage. So even:
won't execute hook_field_presave() for default values.
Comment #4
torotil commentedIn general it seems that the field API exposes many opportunities to be broken by modules implementing entities or even just by form-submit-handlers. Moving the call of field_attach_presave() into the field_attach_insert() and field_attach_update() would eliminate one such point. But many other remain.
All those errors have something in common: They seem to be bugs in the field implementation. All field types that don't have the same (or a compatible) data structure for items for the widget, storage and formatter, have issues with that … The only reason we don't see this more often is because most of the field_types use the same representation all over the place or have already some workarounds for that lying around.
In my current setups I'm using node, file_entity, user, redhen_contact and message entities. I worked closer with node and file_entity and both have shown field-api breakages - I'm sure I'll find such mistakes in the other entity types as well.
To get back to this bug-reports topic: Field-API provides hook_field_presave() and states that it is called before field items are stored. How can it be not the responsibility of the Field-API to get sure it is indeed called?
Comment #5
torotil commentedHere is an attempt to put all the different field-API behaviors into one sketch. It is based on adding a
$item['functions'][] = __FUNCTION__;into all hooks mentioned.Comment #6
johnvIMO these issues are related, if not duplicates:
#1994594: hook_field_presave() not called reliably
#1899498: Field default values do not get hook_field_presave() run on them
Comment #7
benjifisherSee the instructions for adding an issue summary: http://drupal.org/node/1427826 . There are not a lot of comments here, but some of them look complicated enough that we need a "good parts" version in the summary.
Comment #7.0
johnvUpdated summary, using template.
Comment #7.1
johnvUpdated
Comment #8
johnvThe summary is updated.
#4 is not yet incorporated. @torotil, can you do that?
I marked this as a D8-issue, since this issue contains API-changes, as I described in the summary.
Also, the relation with #1899498 must be cleared.
Comment #8.0
johnvUpdated
Comment #9
johnvIf a field has a hook_field_presave, it is likely to have a hook_field_load().
Added to summary: "The patch must be enhanced with hook_field_load()."
Comment #10
berdir8.x works quite different, is called by the entity storage controllers and soon an integral part of those. No idea if this can be done, but I think it can be moved back to 7.x as it is no longer applicable for 8.x.
Comment #10.0
berdirThe patch must be enhanced with hook_field_load().
Comment #10.1
torotil commentedUpdated issue summary.
Comment #11
torotil commentedI've just updated the summary to reflect the current state.
Comment #11.0
torotil commentedFixed a mistake in the issue summary.
Comment #11.1
torotil commentedhook_field_load is already called automatically in field_attach_load(). Also there is no conflict with #1899498.