hook_field_load(), hook_field_validate(), hook_field_attach_load() and hook_field_attach_validate() are all very confusing.
1. hook_field_load() isn't a hook, it's called only for the module that defines the field, so it's really a 'load callback'.
2. hook_field_load() isn't actually called for the module that defines the field, it's called for the module that defines the field type - in my custom module I can define a taxonomy field, but I don't get to implement hook_field_load() for it.
3. This is just look hook_load() for node module, except we should've learned that lesson before.
Two examples of the confusion this causes:
Today, I saw a hook_field_attach_validate() on a taxonomy field, and couldn't work out why it was using attach instead of hook_field_validate() when it was the module defining the field. Of course taxonomy_field_validate() gets that honour, but this took several minutes to click.
Yesterday chx swore that hook_field_attach_insert() only gets invoked for the field type module, not any module, which is the opposite of what happens.
Both of these are completely wrong, and we've both done a lot of work with field API in the past 18 months or so.
So, and I realise not all of this can be done for D7:
hook_field_load() needs to be redefined as a 'load callback' for a field type. Either by renaming hook_field_load() to FIELD_TYPE_PROVIDING_MODULE_field_load() (name needs some work), or adding a 'load callback' key in entity info, or something like that which makes it clear it's not a hook. I'd really favour the 'load callback' key, but don't think that will fly for D7.
It should also be SOMETHING_field_type_load() instead of SOMETHING_field_load() if we stick with the fake hook approach - because it's for the field type, not the specific field.
If we did that, hook_field_attach_load() could be renamed to hook_field_load() - which would bring it into line with hook_node_load(), hook_user_load(), hook_comment_load() all of which are similarly free for alls.
Anything we do for _load() needs to happen for _validate() and all the other hooks and fake hooks.
Comments
Comment #1
catchComment #2
yched commentedThis has been true of CCK from the very beginning. hook_field_$op() (hook_field($op) in CCK contrib) is indeed more a callback than a free-for-all hook. It implements the *field type* behavior.
In an OO model, it would be an ->$op() method of the field type class. In the FiC sprint it was decided not to go for an OO rewrite in the D7 cycle.
The pattern is, for all $op:
- hook_field_$op() is invoked only for the field type.
Same goes for hook_field_formatter_view(), hook_field_widget_form() - only called for the module defining the formatter or the widget.
- hook_field_attach_$op() is a free-for-all hook, operating at the object level. It is not an equivalent of hook_field_$op() - it is not called field by field, but once for the whole object. Hence '_attach_' : hook_field_attach_$op() is fired when field_attach_$op() runs.
hook_field_attach_$op() didn't exist in CCK, because back then they were actually hook_nodeapi($op). They are the node-agnostic equivalents (fire on anything that has fields), when entities didn't officially exist and had no associated hooks. It's very possible that they could now be discarded in favor of hook_entity_$op(), but I'm not sure we have all of those yet.
- There is no free-for-all $op hook at the field level. CCK never had those.
- The module defining a specific field (i.e calling field_create_field()) does not get any special treatment, only its field_attach_$op() will get called (unless it also defines the field type). Field API doesn't know which module defined which field, field_create_field() cannot track which module performed the call.
Comment #3
yched commentedOn the proposed changes:
- Callbacks would solve a less-than-ideal consequence with hook_field_$op() : if your module defines several field types (or formatter types, or widget types), its hook_field_$op() (or formatter/widget hooks) will be called for all of of those types, and it possibly needs to be structured around a
switch ($type) {. This is actually more often the case for widgets and formatters 'hooks' (see text_field_formatter_view()).It's possible that there could be an API-freeze friendly way into callbacks: let field types define their callbacks in hook_field_info(), assuming defaults of '[module]_field_$op' if they don't, thus preserving current function names. But the same would need to be done for the other pluggable thingies: hook_field_formatter_info(), hook_field_widget_info(), hook_field_storage_info().
Callbacks raise a problem : we don't know how to document them. Only hooks go in xxx.api.php and on api.d.o. Menu callbacks and entity callbacks are only documented by a pack of full text in the PHPdocs of hook_menu() and hook_entity_info(). Not nearly enough for documenting the [field type|formatter|widget|storage engine] APIs.
- hook_field_load() -> hook_field_type_load() makes sense in a fake-hook approach. Also implies hook_field_info() -> hook_field_type_info(). Late API change, though :-/.
but hook_field_attach_load() -> hook_field_load() cannot be, because its object level vs field level. hook_field_attach_load() -> hook_entity_load() would be best.
Comment #4
webchickI see that this is annoying and a WTF, but it's no worse than hook_access() and hook_load() and friends.
I think we can simply document the hell out of it and resolve this in Drupal 8.
Comment #5
catchYeah fair enough, the documentation is probably a duplicate of the overall Field API docs one, so I'll move this to D8.
Comment #6
sunCross-referencing #1114032: Call callbacks callbacks, and let hooks all be true hooks
Comment #7
catch#1032850: Get rid of Field API's pseudo-hooks was duplicate.
Comment #8
tstoecklerOne thing that is different for fields is that fields, in contrast to nodes and comments, are arrays and not objects, and hence are not passed by reference. There is no real way to attach 'properties' to a field upon load, for instance.
Comment #9
yched commentedFor the record, consensus in #1032850: Get rid of Field API's pseudo-hooks was that field types "pseudo hooks" (hook_field_load() et al...) should rather be methods of a FieldType handler class, that each field type can subclass. Same principle for formatter and widget hooks. This, of course would heavily rely on the 'plugins' architecture that is supposed to come out of the Web services initiative...
The other hooks mentioned in the thread, hook_field_attach_load() and friends, are *real* hooks. As part of the Entity API D8 effort, they might be better off deprecated in favor of hook_entity_load(), though. Entity API didn't exist when those hooks were added.
Comment #10
yched commentedalso, +1 for renaming hook_field_info() to hook_field_type_info(), for accuracy. But I guess that would be better off as part of the "move to plugin classes" task.
Comment #11
bfroehle commentedsubscribe.
Comment #12
sylvain lecoy commentedAlso, it is maybe the wrong place but is there any issues speaking about revamping the field API ?
I mean, even the doc is confusing:
Field API
Field Attach API
While the first sentence explain that Field API actually attach field to entities (which should be the goal of Field Attach API isn't it?), the second claim that it is working on field already attached to entities. This is, to me a big WTF.
Comment #13
sunDo we have an issue for converting field types into plugins already?
I searched the 'field system' component as well as the D8 Field API sandbox queue already, but couldn't find one?
With that conversion issue, plus #1250500: [policy adopted; patch done] Find a (standard) way to document callback signatures, this issue here should actually be a duplicate, I think.
Comment #14
yched commentedNo, no issue yet. I'll open one when I start working on the topic.
Comment #15
sylvain lecoy commentedAdding tag
Comment #16
swentel commentedField types are plugins now, so I guess we're good.