Ease the creation of fields and instances programatically

So you want to build a module that provides some fields and instances for an entity type. Let's start with a quick summary to get in context.

As you should know, fields in drupal are thingies that exist on its own, though they're useless if not attached to entities. Fields are tied to entities through instances. So to attach a field to a node or any other type of entity, it's needed to create instances. In this way, an instance is identified by the field name it instantiates and the entity type - bundle tuple it is tied to.

In Drupal's admin UI, creating fields and instances is overlapped: when you create a field, you're also creating an instance. On the other hand, when you're reusing an existing field, you're just creating a new instance of it.

Last but not least, part of the configuration you can see in the UI pertains to the field definition, and part to the field instance. So field configuration is the same for all of its instances, and instance configuration is specific and not shared by other instances.

Go on

Easiest way to proceed is to create and configure the fields (and instances) via UI. Once you're done, you can simply get the definition from the database and paste it in hook_install() or a anywhere you need it.

Find below a code snippet to obtain an array with a fields' definition.

Obtain field definition
  1. // Field name.
  2. $field_name = 'field_foo';
  4. // Obtain field definition.
  5. $field = field_read_field($field_name);
  7. // Array of relevant keys. See field_create_field() for reference.
  8. $keys = array('field_name', 'type', 'cardinality', 'settings');
  9. // Copy key-values into $field.
  10. $field = array_intersect_key($field, drupal_map_assoc($keys));
  12. // Print an array suitable to paste in code.
  13. dvm($field);

The code is self explanatory, you just need to change the 2nd line to put the name for your field.

I use devel's php block to run it. Also, this code leverages devel's dvm(), a great helper to print an array as code, so for now devel is a requirement. It could be run by other means... yes, drush!... but actually we don't have an equivalent of dvm() for the cli.

Here's a screenshot of the page after running this snippet.

As you can see, the array code is ready to be pasted in your hook_install(), just assign it to a variable and make a call to field_create_field(). An interesting thing is that you can change the field name here. It doesn't need to start with field_ prefix (See related issue: Issue #1393094: Remove arbitrary "field_" prefix from fields created through UI).

Create a field programatically
  1. function MODULE_install() {
  2.   _MODULE_create_field_foo();
  3. }
  5. function _MODULE_create_field_foo() {
  6.   $field = array(
  7.     'settings' => array(
  8.       'max_length' => '255',
  9.     ),
  10.     'field_name' => 'field_foo',
  11.     'type' => 'text',
  12.     'cardinality' => '1',
  13.   );
  14.   $field = field_create_field($field);
  15. }

To create instances we proceed in a similar way. Lets obtain and clean up the instance definition:

Create a field programatically
  1. $entity_type = 'node';
  2. $bundle = 'testing';
  3. $field_name = 'field_foo';
  5. $instance = field_read_instance($entity_type, $field_name, $bundle);
  6. unset($instance['id']);
  7. unset($instance['field_id']);
  8. unset($instance['deleted']);
  9. dvm($instance);

This is the screenshot:

Finally, here is how it looks in hook_install().

Create an instance programatically
  1. function MODULE_install() {
  2.   _MODULE_create_field_foo();
  3.   _MODULE_create_instance_foo();
  4. }
  6. function _MODULE_create_instance_foo() {
  7.   $instance = array(
  8.     'label' => 'foo',
  9.     'widget' => array(
  10.       'weight' => '-3',
  11.       'type' => 'text_textfield',
  12.       'module' => 'text',
  13.       'active' => 1,
  14.       'settings' => array(
  15.         'size' => '60',
  16.       ),
  17.     ),
  18.     'settings' => array(
  19.      'text_processing' => '0',
  20.     ),
  21.     'display' => array(
  22.       'default' => array(
  23.         'label' => 'above',
  24.         'type' => 'text_default',
  25.         'settings' => array(),
  26.         'module' => 'text',
  27.         'weight' => 1,
  28.       ),
  29.     ),
  30.     'required' => 0,
  31.     'description' => '',
  32.     'default_value' => NULL,
  33.     'field_name' => 'field_foo',
  34.     'entity_type' => 'node',
  35.     'bundle' => 'testing',
  36.   );
  38.   $instance = field_create_instance($instance);
  39. }

You can change the two last keys of $instance in order to create more instances of field_foo in other entity types / bundles.

That's all. As a side note, realize that part of the technique shown above can be used for other purpose: alter a field's definition programatically. See How to lock/unlock a field in Drupal for a simple example. In general you can alter a fields' config and store it back to the database. I already do it in a custom module where a date field settings are altered programatically based on some high level settings.