Field API tutorial

Last updated on
14 October 2016

In previous Drupal versions, most modules defined their own tables, handled writing and reading data from tables, managed data entry and controlled presentation. Although Content Construction Kit (CCK) existed, few modules integrated with CCK. Generally, CCK was simply too unwieldy for modules to use. In Drupal 7, field API is in core and is an API (Application Programming Interface).

With CCK, the primary way to create a field was to administer a node type, add/modify a field and set up all this via the browser. Although it was possible to create fields programmatically, it was a bolt on and difficult task to accomplish. In Field API, the primary way to create a field is through calling an API (using code); the optional Field UI module still allows the management of fields via the browser.

With Drupal 7, most modules will use Field API for the tasks outlined above. One example of this is Organic Groups which relies on Field API instead of using custom tables. Drupal 7 coding is becoming more “precision surgery” as most of the work is done by the Field API; you just need a few well placed alter statements to get the customization you need.

However, Field API introduces many new concepts and because it is a new API, there are a number of “gotchas”. This writeup is a quick introduction to the new concepts and points out things that may make learning the API more challenging.

Entities

Nodes are one of the most used data containers in Drupal. Nodes are organized by node type. Nodes can behave differently, be themed differently, etc. based solely on their type. Nodes have a primary identifier (the node id or nid). Nodes also have a revisions identifier (the version id or vid).

Taxonomy term is another form of data for which different "behaviors" are collected in vocabularies. For example, the hierarchy can be different for each vocabulary.

Nodes and taxonomy terms are entities.

Entities are collected into bundles. For example, node.module defines its node types as bundles. The taxonomy.module defines its vocabularies as bundles. User is simpler as it only defines the user bundle.

However, the comment module inherits the bundle from the node module which you can see from the entity called comment_node_$type (for example comment_node_article).

When you work with entities, use this analogy: node types are to nodes as bundles are to entities.

Field Storage

A field storage module implements storage primitives. Primitives store and retrieve data. This is somewhat of a database abstraction layer. Although it does not allow you to use the full power of a database, it truly abstracts databases.

Field type

Field type modules contain the business logic of a field, that is, what should happen with the raw data loaded by the storage layer or what should happen to the data entered by the user before it gets saved by the storage layer. We recommend the list.module to see how a module defines a field type (or, in this case, four types). list.module is short and most of its code is spent on dealing with its field types.

Field

A field is a data structure holding some settings. Fields must have a name and a field type.

Fields can have a cardinality; that is 'how many values will this field have?' Fields can have as cardinality: one (default), a pre-specified number or any number.

Note that these are configurations that are hard to upgrade because they can change database schema, semantics, and other database specifics. That kind of operation is typically impossible without data loss, which is why updating the field type is explicitly forbidden.

Instance

Attaching a Field to a Bundle results in an Instance. The instance, much like the field, is an array containing various settings. The most important settings are the field name and the bundle. The instance also stores widget and formatter settings. We will get back to these settings; but for now realize that these instance settings are trivial to update. When we were developing Field API and we needed to decide whether something gets into fields or instances, the important question was: Can this be updated easily?

Widget

Widgets are the way field data is entered by the user and is expressed as a Form API array. Several core modules define widgets:

  • text (single and multiple line text entry)
  • options (select list, checkboxes/radios, single checkbox)
  • file
  • image

Aside from the options.module, these modules define field types (and formatters) as well so the options.module is probably the simplest example module to study (give more attention to which hooks are implemented than to the private helper functions as those are typically not necessary in other widget modules).

Widgets can specify which field type they work with. For example, the image widget only works with the image type. The options.module specifies no field types for the widgets it defines but list module alters it so it can use those.

Formatter

Formatters define the way fields are displayed. In Drupal 7, we do not pass around chunks of HTML to be concatenated together and then displayed as HTML. Instead we pass renderable arrays to be rendered at the end of the request into whatever format the page is supposed to display the content. In a similar fashion, formatters also return renderable arrays. As for the widgets, formatters can specify which field type they can show. This is alterable as well.

Field Attach API

The Field Attach API lets you deal with entities containing fields. The operations are again the usual: hook_field_attach_load, hook_field_attach_validate. The entity modules call the field attach functions directly, for example field_attach_presave('node', $node); If you are writing an entity module, this is how you interact with the Field API. There is no super hook-set that the Field API implements, it gets explicit calls. In turn, the field attach functions call same named hooks, for example module_invoke_all('field_attach_presave', $entity_type, $entity); The arguments are the entity type and the entity. These are real hooks, so every module can implement them.

Now, let's see how can you figure out what's inside an entity, how the instance values are structured inside the object. Once your module gets the entity + entity_type, you can run list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); to get:
the value of the entity identifier (much like $node->nid),
the version identifier (much like $node->vid) and
the bundle (much like $node->type).

This is possible because this information (ie. the identifier is in the object property called nid) is defined in hook_entity_info. Once you have the bundle, you can get the instances attached to the bundle with field_info_instances($entity_type, $bundle). This is possible because this information was saved into the field_config_instance table, mostly serialized. Once you have the instance, you can derive the field name and that's where the information is in the object. To quote from field_attach_insert:

list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);

foreach (field_info_instances($entity_type, $bundle) as $instance) {
  $field = field_info_field_by_id($instance['field_id']);
  $field_id = $field['id'];
  $field_name = $field['field_name'];
  if (!empty($entity->$field_name)) {

Now we have $value = $entity->$field_name . This is going to be a multidimensional array. The first key will be the language code, very often LANGUAGE_NONE but also it typically can be found in $entity->language. The second key is the delta, if you have a single value field it will always be 0. The third key or keys are the columns defined in hook_field_schema.

Taxonomy

The taxonomy module has been converted to the field API. There are a number of interesting things here. First, prior to Drupal 7 terms were related to nodes by simply storing a (tid, nid) pair in a table. Now we have a taxonomy_term_reference which stores a reference to a term. It is similar to the user_reference and the node_reference. As every field, it can be attached to any bundle, so now you can tag users and comments etc. You can even tag tags if that's what you fancy. Actually, it is possible that some time later someone will write a generalized entity_reference module (maybe core Drupal 8?) unifying all this behaviour. With that you could comment users or tags, for example.

As mentioned, the bundles are the vocabularies. Taxonomy_vocabulary is also an entity (because it hooks into the entity system so there is caching and more) but you can not attach fields to it (until someone alters that because there is an alter for hook_entity_info too). This is another gotcha since taxonomy_vocabulary isn't consistent with node types -- there is no node_type entity.

The last gotcha: hierarchy is not converted to the field API; instead, taxonomy uses its own hardwired way to store it. Perhaps in the future someone will write an entity_hierarchy modules which allows any entity type to be organized into hierarchies.

Other Tutorials