You are here

entity_bundle_plugin.module in Entity bundle plugin 7

EntityBundlePlugin module.

File

entity_bundle_plugin.module
View source
<?php

/**
 * @file
 * EntityBundlePlugin module.
 */

/**
 * Rebuild all fields for all entity types that use bundle plugins.
 */
function entity_bundle_plugin_rebuild_all($force = FALSE) {
  if (!$force) {
    static $registered = FALSE;
    if (!$registered) {
      drupal_register_shutdown_function('_entity_bundle_plugin_rebuild_all');
      $registered = TRUE;
    }
  }
  else {
    _entity_bundle_plugin_rebuild_all();
  }
}

/**
 * Helper function: rebuild the fields for entity types using bundle plugins.
 */
function _entity_bundle_plugin_rebuild_all() {
  foreach (entity_get_info() as $entity_type => $entity_info) {
    if (!empty($entity_info['bundle plugin']['plugin type'])) {
      entity_bundle_plugin_rebuild_fields($entity_type, $entity_info);
    }
  }
}

/**
 * Rebuild the fields for a given entity type.
 *
 * Scan field declarations on all entities to make sure that all declarations of
 * the same field are identical, and create fields and instance in case of
 * success.
 */
function entity_bundle_plugin_rebuild_fields($entity_type, $entity_info) {

  // Load the bundle plugins.
  ctools_include('plugins');
  $plugins = ctools_get_plugins($entity_info['module'], $entity_info['bundle plugin']['plugin type']);
  $plugins = array_filter($plugins, function ($plugin) {
    $r = new ReflectionClass($plugin['class']);
    $ret = !$r
      ->implementsInterface('EntityBundlePluginValidableInterface') || call_user_func(array(
      $plugin['class'],
      'isValid',
    ));
    return $ret;
  });
  uasort($plugins, 'ctools_plugin_sort');

  // Process the bundle plugins and extract the fields they define.
  $fields = array();
  foreach ($plugins as $plugin_name => $plugin_info) {
    $class = ctools_plugin_load_class($entity_info['module'], $entity_info['bundle plugin']['plugin type'], $plugin_name, 'class');

    // Cannot use instanceof: it needs a class instance, not a class name.
    if (in_array('EntityBundlePluginProvideFieldsInterface', class_implements($class))) {
      $fields[$plugin_name] = call_user_func(array(
        $class,
        'fields',
      ));
    }
  }

  // Check if the fields definitions are compatible and gather them.
  $field_definitions = array();
  foreach ($fields as $plugin_name => $plugin_fields) {
    foreach ($plugin_fields as $field_name => $field) {
      $field_definitions[$field_name][$plugin_name] = $field['field'];
    }
  }
  unset($plugin_name, $plugin_fields);
  foreach ($field_definitions as $field_name => $definitions) {
    _entity_bundle_plugin_check_incompatible_fields($field_name, $definitions);
  }
  unset($field_name, $definitions);

  // Now, create or update fields.
  foreach ($field_definitions as $field_name => $plugin_fields) {
    $field = reset($plugin_fields);
    $field += array(
      'field_name' => $field_name,
      'locked' => TRUE,
    );
    if ($existing_field = field_info_field($field_name)) {

      // Merge default values from the cached field definition with the
      // definition that comes from EBP.
      $field = _entity_bundle_plugin_array_merge_deep_array(array(
        $existing_field,
        $field,
      ));

      // Recursively cast to string for a sane comparison.
      array_walk_recursive($existing_field, function (&$value) {
        $value = $value !== FALSE ? (string) $value : '0';
      });
      array_walk_recursive($field, function (&$value) {
        $value = $value !== FALSE ? (string) $value : '0';
      });
      if (serialize($existing_field) != serialize($field)) {
        field_update_field($field);
      }
    }
    else {

      // The field does not exist, create it.
      field_create_field($field);
    }
  }

  // Finally, create or update instances.
  foreach ($fields as $plugin_name => $plugin_fields) {
    foreach ($plugin_fields as $field_name => $field) {
      $instance = $field['instance'];
      $instance += array(
        'field_name' => $field_name,
        'entity_type' => $entity_type,
        'bundle' => $plugin_name,
      );
      if ($existing_instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle'])) {

        // Merge default values from the cached instance definition with the
        // definition that comes from EBP.
        $instance = _entity_bundle_plugin_array_merge_deep_array(array(
          $existing_instance,
          $instance,
        ));

        // Recursively cast to string for a sane comparison.
        array_walk_recursive($existing_instance, function (&$value) {
          $value = $value !== FALSE ? (string) $value : '0';
        });
        array_walk_recursive($instance, function (&$value) {
          $value = $value !== FALSE ? (string) $value : '0';
        });
        if (serialize($existing_instance) != serialize($instance)) {
          field_update_instance($instance);
        }
      }
      else {
        field_create_instance($instance);
      }
    }
  }
}

/**
 * Implements hook_flush_caches().
 */
function entity_bundle_plugin_flush_caches() {
  _entity_bundle_plugin_rebuild_all();
}

/**
 * Utility function: check that a set of fields are compatible with each other.
 */
function _entity_bundle_plugin_check_incompatible_fields($field_name, $field_definitions) {
  if (count($field_definitions) > 1) {
    $groups = array();
    $group_values = array();
    foreach ($field_definitions as $plugin_name => $field_plugin) {
      $found_group = NULL;
      foreach ($group_values as $group_id => $group_value) {
        if ($field_plugin == $group_value) {
          $found_group = $group_id;
          break;
        }
      }
      if (isset($found_group)) {
        $groups[$found_group][] = $plugin_name;
      }
      else {
        $groups[] = array(
          $plugin_name,
        );
        $group_values[] = $field_plugin;
      }
    }
    if (count($groups) > 1) {
      $displayed_groups = array();
      foreach ($groups as $group) {
        $displayed_groups[] = '(' . implode(', ', $group) . ')';
      }
      throw new Exception(t('@field_name is incompatible between @groups', array(
        '@field_name' => $field_name,
        '@groups' => implode(', ', $displayed_groups),
      )));
    }
  }
  return FALSE;
}

/**
 * Merges multiple arrays, recursively, and returns the merged array.
 *
 * This function is pretty much a clone of drupal_array_merge_deep_array() and
 * similar to PHP's array_merge_recursive() function, but it handles non-array
 * values differently. When merging values that are not both arrays, the latter
 * value replaces the former rather than merging with it, and numeric keys are
 * also merged, NOT appended.
 *
 * Example:
 * @code
 * $array_1 = array('test' => array('X'), array(0 => 'A', 1 => 'B'));
 * $array_2 = array('test' => array('Y'), array(0 => 'C', 1 => 'D'));
 *
 * // This results in array('test' => array('X', 'Y'), 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D').
 * $incorrect = drupal_array_merge_deep_array(array($array_1, $array_2));
 *
 * // This results in array('test' => array('X', 'Y'), 0 => 'C', 1 => 'D').
 * $correct = _entity_bundle_plugin_array_merge_deep_array(array($array_1, $array_2));
 * @endcode
 *
 * @param array $arrays
 *   Arrays to merge.
 *
 * @return array
 *   The merged array.
 *
 * @see drupal_array_merge_deep_array()
 */
function _entity_bundle_plugin_array_merge_deep_array(array $arrays) {
  $result = array();
  foreach ($arrays as $array) {
    foreach ($array as $key => $value) {

      // Recurse when both values are arrays.
      if (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
        $result[$key] = _entity_bundle_plugin_array_merge_deep_array(array(
          $result[$key],
          $value,
        ));
      }
      else {
        $result[$key] = $value;
      }
    }
  }
  return $result;
}

Functions

Namesort descending Description
entity_bundle_plugin_flush_caches Implements hook_flush_caches().
entity_bundle_plugin_rebuild_all Rebuild all fields for all entity types that use bundle plugins.
entity_bundle_plugin_rebuild_fields Rebuild the fields for a given entity type.
_entity_bundle_plugin_array_merge_deep_array Merges multiple arrays, recursively, and returns the merged array.
_entity_bundle_plugin_check_incompatible_fields Utility function: check that a set of fields are compatible with each other.
_entity_bundle_plugin_rebuild_all Helper function: rebuild the fields for entity types using bundle plugins.