' . t('About') . ''; $output .= '
' . t('The Entity module provides an API for managing entities like nodes and users, i.e. an API for loading and identifying entities. For more information, see the online handbook entry for Entity module', array('!url' => 'http://drupal.org/documentation/modules/entity')) . '
'; return $output; } } /** * Implements hook_modules_preenable(). */ function entity_modules_preenable() { entity_info_cache_clear(); } /** * Implements hook_modules_disabled(). */ function entity_modules_disabled() { entity_info_cache_clear(); } /** * Gets the entity info array of an entity type. * * @param $entity_type * The entity type, e.g. node, for which the info shall be returned, or NULL * to return an array with info about all types. * * @see hook_entity_info() * @see hook_entity_info_alter() */ function entity_get_info($entity_type = NULL) { global $language_interface; // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__); } $entity_info = &$drupal_static_fast['entity_info']; // hook_entity_info() includes translated strings, so each language is cached // separately. $langcode = $language_interface->langcode; if (empty($entity_info)) { if ($cache = cache()->get("entity_info:$langcode")) { $entity_info = $cache->data; } else { $entity_info = module_invoke_all('entity_info'); // Merge in default values. foreach ($entity_info as $name => $data) { $entity_info[$name] += array( 'fieldable' => FALSE, 'controller class' => 'Drupal\entity\EntityController', 'static cache' => TRUE, 'field cache' => TRUE, 'load hook' => $name . '_load', 'bundles' => array(), 'view modes' => array(), 'entity keys' => array(), 'translation' => array(), ); $entity_info[$name]['entity keys'] += array( 'revision' => '', 'bundle' => '', ); foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) { $entity_info[$name]['view modes'][$view_mode] += array( 'custom settings' => FALSE, ); } // If no bundle key is provided, assume a single bundle, named after // the entity type. if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) { $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label'])); } // Prepare entity schema fields SQL info for // Drupal\entity\EntityControllerInterface::buildQuery(). if (isset($entity_info[$name]['base table'])) { $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']); if (isset($entity_info[$name]['revision table'])) { $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']); } } } // Let other modules alter the entity info. drupal_alter('entity_info', $entity_info); cache()->set("entity_info:$langcode", $entity_info); } } if (empty($entity_type)) { return $entity_info; } elseif (isset($entity_info[$entity_type])) { return $entity_info[$entity_type]; } } /** * Resets the cached information about entity types. */ function entity_info_cache_clear() { drupal_static_reset('entity_get_info'); // Clear all languages. cache()->deletePrefix('entity_info:'); } /** * Extracts ID, revision ID, and bundle name from an entity. * * @param $entity_type * The entity type; e.g. 'node' or 'user'. * @param $entity * The entity from which to extract values. * * @return * A numerically indexed array (not a hash table) containing these * elements: * - 0: Primary ID of the entity. * - 1: Revision ID of the entity, or NULL if $entity_type is not versioned. * - 2: Bundle name of the entity. */ function entity_extract_ids($entity_type, $entity) { $info = entity_get_info($entity_type); // Objects being created might not have id/vid yet. $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL; $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL; if (!empty($info['entity keys']['bundle'])) { // Explicitly fail for malformed entities missing the bundle property. if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') { throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type))); } $bundle = $entity->{$info['entity keys']['bundle']}; } else { // The entity type provides no bundle key: assume a single bundle, named // after the entity type. $bundle = $entity_type; } return array($id, $vid, $bundle); } /** * Assembles an object structure with initial IDs. * * This function can be seen as reciprocal to entity_extract_ids(). * * @param $entity_type * The entity type; e.g. 'node' or 'user'. * @param $ids * A numerically indexed array, as returned by entity_extract_ids(), * containing these elements: * - 0: Primary ID of the entity. * - 1: Revision ID of the entity, or NULL if $entity_type is not versioned. * - 2: Bundle name of the entity, or NULL if $entity_type has no bundles. * * @return * An entity object, initialized with the IDs provided. */ function entity_create_stub_entity($entity_type, $ids) { $entity = new stdClass(); $info = entity_get_info($entity_type); $entity->{$info['entity keys']['id']} = $ids[0]; if (!empty($info['entity keys']['revision']) && isset($ids[1])) { $entity->{$info['entity keys']['revision']} = $ids[1]; } if (!empty($info['entity keys']['bundle']) && isset($ids[2])) { $entity->{$info['entity keys']['bundle']} = $ids[2]; } return $entity; } /** * Loads an entity from the database. * * @param string $entity_type * The entity type to load, e.g. node or user. * @param int $id * The id of the entity to load. * @param bool $reset * Whether to reset the internal cache for the requested entity type. * * @return object * The entity object, or FALSE if there is no entity with the given id. * * @see hook_entity_info() * @see entity_load_multiple() * @see Drupal\entity\EntityControllerInterface * @see Drupal\entity\EntityController * @see Drupal\entity\EntityFieldQuery */ function entity_load($entity_type, $id, $reset = FALSE) { $entities = entity_load_multiple($entity_type, array($id), array(), $reset); return isset($entities[$id]) ? $entities[$id] : FALSE; } /** * Loads multiple entities from the database. * * This function should be used whenever you need to load more than one entity * from the database. The entities are loaded into memory and will not require * database access if loaded again during the same page request. * * The actual loading is done through a class that has to implement the * Drupal\entity\EntityControllerInterface interface. By default, * Drupal\entity\EntityController is used. Entity types can specify * that a different class should be used by setting the 'controller class' key * in hook_entity_info(). These classes can either implement the * Drupal\entity\EntityControllerInterface interface, or, most * commonly, extend the Drupal\entity\EntityController class. See * node_entity_info() and the NodeController in node.module as an example. * * @param string $entity_type * The entity type to load, e.g. node or user. * @param array|bool $ids * An array of entity IDs, or FALSE to load all entities. * @param array $conditions * (deprecated) An associative array of conditions on the base table, where * the keys are the database fields and the values are the values those * fields must have. Instead, it is preferable to use EntityFieldQuery to * retrieve a list of entity IDs loadable by this function. * @param bool $reset * Whether to reset the internal cache for the requested entity type. * * @return array * An array of entity objects indexed by their ids. * * @todo Remove $conditions in Drupal 8. * * @see hook_entity_info() * @see Drupal\entity\EntityControllerInterface * @see Drupal\entity\EntityController * @see Drupal\entity\EntityFieldQuery */ function entity_load_multiple($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) { if ($reset) { entity_get_controller($entity_type)->resetCache(); } return entity_get_controller($entity_type)->load($ids, $conditions); } /** * Loads the unchanged, i.e. not modified, entity from the database. * * Unlike entity_load() this function ensures the entity is directly loaded from * the database, thus bypassing any static cache. In particular, this function * is useful to determine changes by comparing the entity being saved to the * stored entity. * * @param $entity_type * The entity type to load, e.g. node or user. * @param $id * The ID of the entity to load. * * @return * The unchanged entity, or FALSE if the entity cannot be loaded. */ function entity_load_unchanged($entity_type, $id) { entity_get_controller($entity_type)->resetCache(array($id)); $result = entity_get_controller($entity_type)->load(array($id)); return reset($result); } /** * Deletes multiple entities permanently. * * @param $entity_type * The type of the entity. * @param $ids * An array of entity IDs of the entities to delete. */ function entity_delete_multiple($entity_type, $ids) { entity_get_controller($entity_type)->delete($ids); } /** * Constructs a new entity object, without permanently saving it. * * @param $entity_type * The type of the entity. * @param $values * An array of values to set, keyed by property name. If the entity type has * bundles the bundle key has to be specified. * * @return Drupal\entity\EntityInterface * A new entity object. */ function entity_create($entity_type, array $values) { return entity_get_controller($entity_type)->create($values); } /** * Gets the entity controller class for an entity type. * * @return Drupal\entity\EntityStorageControllerInterface */ function entity_get_controller($entity_type) { $controllers = &drupal_static(__FUNCTION__, array()); if (!isset($controllers[$entity_type])) { $type_info = entity_get_info($entity_type); $class = $type_info['controller class']; $controllers[$entity_type] = new $class($entity_type); } return $controllers[$entity_type]; } /** * Invokes hook_entity_prepare_view(). * * If adding a new entity similar to nodes, comments or users, you should * invoke this function during the ENTITY_build_content() or * ENTITY_view_multiple() phases of rendering to allow other modules to alter * the objects during this phase. This is needed for situations where * information needs to be loaded outside of ENTITY_load() - particularly * when loading entities into one another - i.e. a user object into a node, due * to the potential for unwanted side-effects such as caching and infinite * recursion. By convention, entity_prepare_view() is called after * field_attach_prepare_view() to allow entity level hooks to act on content * loaded by field API. * * @param $entity_type * The type of entity, i.e. 'node', 'user'. * @param $entities * The entity objects which are being prepared for view, keyed by object ID. * * @see hook_entity_prepare_view() */ function entity_prepare_view($entity_type, $entities) { // To ensure hooks are only run once per entity, check for an // entity_view_prepared flag and only process items without it. // @todo: resolve this more generally for both entity and field level hooks. $prepare = array(); foreach ($entities as $id => $entity) { if (empty($entity->entity_view_prepared)) { // Add this entity to the items to be prepared. $prepare[$id] = $entity; // Mark this item as prepared. $entity->entity_view_prepared = TRUE; } } if (!empty($prepare)) { module_invoke_all('entity_prepare_view', $prepare, $entity_type); } } /** * Returns the uri elements of an entity. * * @param $entity_type * The entity type; e.g. 'node' or 'user'. * @param $entity * The entity for which to generate a path. * * @return * An array containing the 'path' and 'options' keys used to build the uri of * the entity, and matching the signature of url(). NULL if the entity has no * uri of its own. * * @todo * Remove once all entity types are implementing the EntityInterface. */ function entity_uri($entity_type, $entity) { $info = entity_get_info($entity_type); list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); // A bundle-specific callback takes precedence over the generic one for the // entity type. if (isset($info['bundles'][$bundle]['uri callback'])) { $uri_callback = $info['bundles'][$bundle]['uri callback']; } elseif (isset($info['uri callback'])) { $uri_callback = $info['uri callback']; } else { return NULL; } // Invoke the callback to get the URI. If there is no callback, return NULL. if (isset($uri_callback)) { $uri = $uri_callback($entity); // Pass the entity data to url() so that alter functions do not need to // lookup this entity again. $uri['options']['entity_type'] = $entity_type; $uri['options']['entity'] = $entity; return $uri; } } /** * Returns the label of an entity. * * See the 'label callback' component of the hook_entity_info() return value * for more information. * * @param $entity_type * The entity type; e.g., 'node' or 'user'. * @param $entity * The entity for which to generate the label. * * @return * The entity label, or FALSE if not found. * * @todo * Remove once all entity types are implementing the EntityInterface. */ function entity_label($entity_type, $entity) { $label = FALSE; $info = entity_get_info($entity_type); if (isset($info['label callback'])) { $label = $info['label callback']($entity_type, $entity); } elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) { $label = $entity->{$info['entity keys']['label']}; } return $label; } /** * Attaches field API validation to entity forms. */ function entity_form_field_validate($entity_type, $form, &$form_state) { // All field attach API functions act on an entity object, but during form // validation, we don't have one. $form_state contains the entity as it was // prior to processing the current form submission, and we must not update it // until we have fully validated the submitted input. Therefore, for // validation, act on a pseudo entity created out of the form values. $pseudo_entity = (object) $form_state['values']; field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state); } /** * Copies submitted values to entity properties for simple entity forms. * * During the submission handling of an entity form's "Save", "Preview", and * possibly other buttons, the form state's entity needs to be updated with the * submitted form values. Each entity form implements its own builder function * for doing this, appropriate for the particular entity and form, whereas * modules may specify additional builder functions in $form['#entity_builders'] * for copying the form values of added form elements to entity properties. * Many of the main entity builder functions can call this helper function to * re-use its logic of copying $form_state['values'][PROPERTY] values to * $entity->PROPERTY for all entries in $form_state['values'] that are not field * data, and calling field_attach_submit() to copy field data. Apart from that * this helper invokes any additional builder functions that have been specified * in $form['#entity_builders']. * * For some entity forms (e.g., forms with complex non-field data and forms that * simultaneously edit multiple entities), this behavior may be inappropriate, * so the builder function for such forms needs to implement the required * functionality instead of calling this function. */ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) { $info = entity_get_info($entity_type); list(, , $bundle) = entity_extract_ids($entity_type, $entity); // Copy top-level form values that are not for fields to entity properties, // without changing existing entity properties that are not being edited by // this form. Copying field values must be done using field_attach_submit(). $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values']; foreach ($values_excluding_fields as $key => $value) { $entity->$key = $value; } // Invoke all specified builders for copying form values to entity properties. if (isset($form['#entity_builders'])) { foreach ($form['#entity_builders'] as $function) { $function($entity_type, $entity, $form, $form_state); } } // Copy field values to the entity. if ($info['fieldable']) { field_attach_submit($entity_type, $entity, $form, $form_state); } }