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, 'entity class' => 'Drupal\Core\Entity\Entity', 'controller class' => 'Drupal\Core\Entity\DatabaseStorageController', 'form controller class' => array( 'default' => 'Drupal\Core\Entity\EntityFormController', ), 'static cache' => TRUE, 'field cache' => TRUE, '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\Core\Entity\DatabaseStorageControllerInterface::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:'); } /** * 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 Drupal\Core\Entity\EntityInterface * The entity object, or FALSE if there is no entity with the given id. * * @see hook_entity_info() * @see entity_load_multiple() * @see Drupal\Core\Entity\EntityStorageControllerInterface * @see Drupal\Core\Entity\DatabaseStorageController * @see Drupal\Core\Entity\EntityFieldQuery */ function entity_load($entity_type, $id, $reset = FALSE) { $entities = entity_load_multiple($entity_type, array($id), $reset); return isset($entities[$id]) ? $entities[$id] : FALSE; } /** * Loads an entity from the database. * * @param string $entity_type * The entity type to load, e.g. node or user. * @param int $revision_id * The id of the entity to load. * * @return Drupal\Core\Entity\EntityInterface * The entity object, or FALSE if there is no entity with the given revision * id. * * @see hook_entity_info() * @see Drupal\Core\Entity\EntityStorageControllerInterface * @see Drupal\Core\Entity\DatabaseStorageController */ function entity_revision_load($entity_type, $revision_id) { return entity_get_controller($entity_type)->loadRevision($revision_id); } /** * Loads an entity by UUID. * * Note that some entity types may not support UUIDs. * * @param string $entity_type * The entity type to load; e.g., 'node' or 'user'. * @param string $uuid * The UUID of the entity to load. * @param bool $reset * Whether to reset the internal cache for the requested entity type. * * @return EntityInterface|FALSE * The entity object, or FALSE if there is no entity with the given UUID. * * @throws Drupal\Core\Entity\EntityStorageException * Thrown in case the requested entity type does not support UUIDs. * * @see hook_entity_info() */ function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) { $entity_info = entity_get_info($entity_type); if (empty($entity_info['entity keys']['uuid'])) { throw new EntityStorageException("Entity type $entity_type does not support UUIDs."); } $uuid_key = $entity_info['entity keys']['uuid']; $controller = entity_get_controller($entity_type); if ($reset) { $controller->resetCache(); } $entities = $controller->loadByProperties(array($uuid_key => $uuid)); return reset($entities); } /** * 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\Core\Entity\EntityStorageControllerInterface interface. By default, * Drupal\Core\Entity\DatabaseStorageController 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\Core\Entity\EntityStorageControllerInterface interface, or, * most commonly, extend the Drupal\Core\Entity\DatabaseStorageController * class. See node_entity_info() and the NodeStorageController in node.module as * an example. * * @param string $entity_type * The entity type to load, e.g. node or user. * @param array $ids * (optional) An array of entity IDs. If omitted, all entities are loaded. * @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. * * @see hook_entity_info() * @see Drupal\Core\Entity\EntityStorageControllerInterface * @see Drupal\Core\Entity\DatabaseStorageController * @see Drupal\Core\Entity\EntityFieldQuery */ function entity_load_multiple($entity_type, array $ids = NULL, $reset = FALSE) { if ($reset) { entity_get_controller($entity_type)->resetCache(); } return entity_get_controller($entity_type)->load($ids); } /** * Load entities by their property values. * * @param string $entity_type * The entity type to load, e.g. node or user. * @param array $values * An associative array where the keys are the property names and the * values are the values those properties must have. * * @return array * An array of entity objects indexed by their ids. */ function entity_load_multiple_by_properties($entity_type, array $values) { return entity_get_controller($entity_type)->loadByProperties($values); } /** * 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\Core\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\Core\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 label of an entity. * * This is a wrapper for Drupal\Core\Entity\EntityInterface::label(). This function * should only be used as a callback, e.g. for menu title callbacks. * * @param Drupal\Core\Entity\EntityInterface $entity * The entity for which to generate the label. * @param $langcode * (optional) The language code of the language that should be used for * getting the label. If set to NULL, the entity's default language is * used. * * @return * The label of the entity, or NULL if there is no label defined. * * @see Drupal\Core\Entity\EntityInterface::label() */ function entity_page_label(EntityInterface $entity, $langcode = NULL) { return $entity->label($langcode); } /** * Returns an entity form controller for the given operation. * * Since there might be different scenarios in which an entity is edited, * multiple form controllers suitable to the different operations may be defined. * If no controller is found for the default operation, the base class will be * used. If a non-existing non-default operation is specified an exception will * be thrown. * * @see hook_entity_info() * * @param $entity_type * The type of the entity. * @param $operation * (optional) The name of an operation, such as creation, editing or deletion, * identifying the controlled form. Defaults to 'default' which is the usual * create/edit form. * * @return Drupal\Core\Entity\EntityFormControllerInterface * An entity form controller instance. */ function entity_form_controller($entity_type, $operation = 'default') { $info = entity_get_info($entity_type); // Check whether there is a form controller class for the specified operation. if (!empty($info['form controller class'][$operation])) { $class = $info['form controller class'][$operation]; } // If no controller is specified default to the base implementation. elseif (empty($info['form controller class']) && $operation == 'default') { $class = 'Drupal\Core\Entity\EntityFormController'; } // If a non-existing operation has been specified stop. else { throw new InvalidArgumentException("Missing form controller for '$entity_type', operation '$operation'"); } return new $class($operation); } /** * Returns the form id for the given entity and operation. * * @param EntityInterface $entity * The entity to be created or edited. * @param $operation * (optional) The operation for the form to be processed. * * @return * A string representing the entity form id. */ function entity_form_id(EntityInterface $entity, $operation = 'default') { $entity_type = $entity->entityType(); $bundle = $entity->bundle(); $form_id = $entity_type; if ($bundle != $entity_type) { $form_id = $bundle . '_' . $form_id; } if ($operation != 'default') { $form_id = $form_id . '_' . $operation; } return $form_id . '_form'; } /** * Returns the default form state for the given entity and operation. * * @param EntityInterface $entity * The entity to be created or edited. * @param $operation * (optional) The operation identifying the form to be processed. * * @return * A $form_state array already filled the entity form controller. */ function entity_form_state_defaults(EntityInterface $entity, $operation = 'default', $langcode = NULL) { $form_state = array(); $controller = entity_form_controller($entity->entityType(), $operation); $form_state['build_info']['callback'] = array($controller, 'build'); $form_state['build_info']['base_form_id'] = $entity->entityType() . '_form'; $form_state['build_info']['args'] = array($entity); $form_state['langcode'] = $langcode; return $form_state; } /** * Retrieves, populates, and processes an entity form. * * @param EntityInterface $entity * The entity to be created or edited. * @param $operation * (optional) The operation identifying the form to be submitted. * @param $form_state * (optional) A keyed array containing the current state of the form. * * @return * A $form_state array already filled with the entity form controller. */ function entity_form_submit(EntityInterface $entity, $operation = 'default', &$form_state = array()) { $form_state += entity_form_state_defaults($entity, $operation); $form_id = entity_form_id($entity, $operation); drupal_form_submit($form_id, $form_state); } /** * Returns the built and processed entity form for the given entity. * * @param EntityInterface $entity * The entity to be created or edited. * @param $operation * (optional) The operation identifying the form variation to be returned. * * @return * The processed form for the given entity and operation. */ function entity_get_form(EntityInterface $entity, $operation = 'default', $langcode = NULL) { $form_state = entity_form_state_defaults($entity, $operation, $langcode); $form_id = entity_form_id($entity, $operation); return drupal_build_form($form_id, $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); // 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, $entity->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); } }