diff --git includes/common.inc includes/common.inc
index 9477bc1..024ba73 100644
--- includes/common.inc
+++ includes/common.inc
@@ -4264,3 +4264,323 @@ function _drupal_flush_css_js() {
}
variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
}
+
+/**
+ * Get 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.
+ */
+function drupal_get_entity_info($entity_type = NULL) {
+ // We statically cache the information returned by hook_entity_info().
+ $entity_info = &drupal_static(__FUNCTION__, array());
+
+ if (empty($entity_info)) {
+ $entity_info = module_invoke_all('entity_info');
+ // Merge in default values.
+ foreach ($entity_info as $name => $data) {
+ $entity_info[$name] += array(
+ 'fieldable' => FALSE,
+ 'loader class' => 'DrupalDefaultEntityLoader',
+ 'static cache' => TRUE,
+ );
+ }
+ // Let other modules alter the entity info.
+ drupal_alter('entity_info', $entity_info);
+ }
+
+ return empty($entity_type) ? $entity_info : $entity_info[$entity_type];
+}
+
+/**
+ * Load 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.
+ *
+ * @param $entity_type
+ * The entity type to load, e.g. node or user.
+ * @param $ids
+ * An array of entity IDs, or FALSE to load all entities.
+ * @param $conditions
+ * An array of conditions on the entity base table in the form 'field' => $value.
+ * @param $reset
+ * Whether to reset the internal cache for the requested entity type.
+ *
+ * @return
+ * An array of entity objects indexed by their ids.
+ */
+function drupal_load_multiple($entity_type, $ids = array(), $conditions = array(), $reset = FALSE) {
+ if ($reset) {
+ drupal_get_entity_loader($entity_type)->resetCache();
+ }
+ return drupal_get_entity_loader($entity_type)->loadMultiple($ids, $conditions);
+}
+
+/**
+ * Get the entity loader class for an entity type.
+ */
+function drupal_get_entity_loader($entity_type) {
+ $loaders = &drupal_static(__FUNCTION__, array());
+ if (!isset($loaders[$entity_type])) {
+ $type_info = drupal_get_entity_info($entity_type);
+ $class = $type_info['loader class'];
+ $loaders[$entity_type] = new $class($entity_type);
+ }
+ return $loaders[$entity_type];
+}
+
+/**
+ * Interface for entity loader classes.
+ *
+ * All entity loader classes specified via the 'loader class' key returned by
+ * hook_entity_info() or hook_entity_info_alter() have to implement this interface.
+ *
+ * Most simple, SQL-based entity loaders will do better by extending
+ * DrupalDefaultEntityLoader instead of implementing this interface directly.
+ */
+interface DrupalEntityLoader {
+ /**
+ * Constructor.
+ *
+ * @param $entity_type
+ * The entity type for which the instance is created.
+ */
+ public function __construct($entity_type);
+
+ /**
+ * Reset the internal, static entity cache.
+ */
+ public function resetCache();
+
+ /**
+ * Load one or more entities.
+ *
+ * @param $ids
+ * An array of entity IDs, or FALSE to load all entities.
+ * @param $conditions
+ * An array of conditions on the entity base table in the form 'field' => $value.
+ */
+ public function loadMultiple($ids = array(), $conditions = array());
+}
+
+/**
+ * Default implementation of DrupalEntityLoader.
+ *
+ * This class can be used as-is by most simple entity types. Entity types
+ * requiring special handling can extend the class.
+ */
+class DrupalDefaultEntityLoader implements DrupalEntityLoader {
+
+ protected $entity_cache;
+ protected $entity_type;
+ protected $entity_info;
+ protected $hook_load_arguments;
+ protected $id_key;
+ protected $revision_key;
+ protected $revision_table;
+ protected $query;
+
+ /**
+ * Constructor. Set basic variables.
+ */
+ public function __construct($entity_type) {
+ $this->entity_type = $entity_type;
+ $this->entity_info = drupal_get_entity_info($entity_type);
+ $this->entity_cache = array();
+ $this->hook_load_arguments = array();
+
+ $this->id_key = $this->entity_info['id key'];
+
+ // Check if the entity type supports revisions.
+ if (isset($this->entity_info['revision key'])) {
+ $this->revision_key = $this->entity_info['revision key'];
+ $this->revision_table = $entity_type . '_revision';
+ }
+ else {
+ $this->revision_key = FALSE;
+ }
+
+ // Check if the entity type supports static caching of loaded entities.
+ $this->cache = !empty($this->entity_info['static cache']);
+ }
+
+ public function resetCache() {
+ $this->entity_cache = array();
+ }
+
+ public function loadMultiple($ids = array(), $conditions = array()) {
+ $this->ids = $ids;
+ $this->conditions = $conditions;
+
+ $entities = array();
+
+ // Revisions are not statically cached, and require a different query to
+ // other conditions, so separate the revision id into its own variable.
+ if ($this->revision_key && isset($this->conditions[$this->revision_key])) {
+ $this->revision_id = $this->conditions[$this->revision_key];
+ unset($this->conditions[$this->revision_key]);
+ }
+ else {
+ $this->revision_id = FALSE;
+ }
+
+ // Create a new variable which is either a prepared version of the $this->ids
+ // array for later comparison with the entity cache, or FALSE if no $this->ids were
+ // passed. The $this->ids array is reduced as items are loaded from cache, and we
+ // need to know if it's empty for this reason to avoid querying the database
+ // when all requested entities are loaded from cache.
+ $passed_ids = !empty($this->ids) ? array_flip($this->ids) : FALSE;
+
+ if ($this->cache) {
+ // Load any available entities from the internal cache.
+ if (!empty($this->entity_cache) && !$this->revision_id) {
+ if ($this->ids) {
+ $entities += array_intersect_key($this->entity_cache, $passed_ids);
+ // If any entities were loaded, remove them from the $this->ids still to load.
+ $this->ids = array_keys(array_diff_key($passed_ids, $entities));
+ }
+ // If loading entities only by conditions, fetch all available entities from
+ // the cache. Nodes which don't match are removed later.
+ elseif ($this->conditions) {
+ $entities = $this->entity_cache;
+ }
+ }
+
+ // Exclude any entities loaded from cache if they don't match $this->conditions.
+ // This ensures the same behavior whether loading from memory or database.
+ if ($this->conditions) {
+ foreach ($entities as $entity) {
+ $entity_values = (array) $entity;
+ if (array_diff_assoc($this->conditions, $entity_values)) {
+ unset($entities[$entity->{$this->id_key}]);
+ }
+ }
+ }
+ }
+
+ // Load any remaining entities from the database. This is the case if $ids
+ // is set to FALSE (so we load all entities), if there are any ids left to
+ // load, if loading a revision, or if $conditions was passed without $ids.
+ if ($this->ids === FALSE || $this->ids || $this->revision_id || ($this->conditions && !$passed_ids)) {
+ // Build the query.
+ $this->buildQuery();
+ $queried_entities = $this->query->execute()->fetchAllAssoc($this->id_key);
+ }
+
+ // Pass all entities loaded from the database through $this->attachLoad(),
+ // which attaches fields (if supported by the entity type) and calls the
+ // entity type specific load callback.
+ if (!empty($queried_entities)) {
+ $this->attachLoad($queried_entities);
+ $entities += $queried_entities;
+ }
+
+ if ($this->cache) {
+ // Add entities to the cache if we're not loading a revision.
+ if (!empty($queried_entities) && !$this->revision_id) {
+ $this->entity_cache += $queried_entities;
+ }
+ // Ensure that the returned array is ordered the same as the original $ids
+ // array if this was passed in and remove any invalid ids.
+ if ($passed_ids) {
+ // Remove any invalid ids from the array.
+ $passed_ids = array_intersect_key($passed_ids, $entities);
+ foreach ($entities as $entity) {
+ $passed_ids[$entity->{$this->id_key}] = $entity;
+ }
+ $entities = $passed_ids;
+ }
+ }
+
+ return $entities;
+ }
+
+ /**
+ * Build the query to load the entity.
+ *
+ * This has full revision support. For entities requiring special queries,
+ * the class can be extendend, and the default query can be constructed by
+ * calling parent::buildQuery(). See NodeLoader::buildQuery() for an example.
+ */
+ protected function buildQuery() {
+ $this->query = db_select($this->entity_info['base table'], 'base');
+
+ $this->query->addTag($this->entity_type . '_load_multiple');
+
+ if ($this->revision_id) {
+ $this->query->join($this->revision_table, 'revision', "revision.{$this->id_key} = base.{$this->id_key} AND revision.{$this->revision_key} = :revision_id", array(':revision_id' => $this->revision_id));
+ }
+ elseif ($this->revision_key) {
+ $this->query->join($this->revision_table, 'revision', "revision.{$this->revision_key} = base.{$this->revision_key}");
+ }
+
+ // Add fields from the {entity} table.
+ $entity_fields = drupal_schema_fields_sql($this->entity_info['base table']);
+
+ if ($this->revision_key) {
+ // Add all fields from the {entity_revision} table.
+ $entity_revision_fields = drupal_schema_fields_sql($this->revision_table);
+ // The id field is provided by entity, so remove it.
+ unset($entity_revision_fields[$this->id_key]);
+
+ // Change timestamp to revision_timestamp before adding it to the query.
+ // TODO: This is node specific and has to be moved into NodeLoader.
+ unset($entity_revision_fields['timestamp']);
+ $this->query->addField('revision', 'timestamp', 'revision_timestamp');
+
+ // Remove all fields from the base table that are also fields by the same
+ // name in the revision table.
+ $entity_field_keys = array_flip($entity_fields);
+ foreach ($entity_revision_fields as $key => $name) {
+ if (isset($entity_field_keys[$name])) {
+ unset($entity_fields[$entity_field_keys[$name]]);
+ }
+ }
+ $this->query->fields('revision', $entity_revision_fields);
+ }
+
+ $this->query->fields('base', $entity_fields);
+
+ if ($this->ids) {
+ $this->query->condition("base.{$this->id_key}", $this->ids, 'IN');
+ }
+ if ($this->conditions) {
+ foreach ($this->conditions as $field => $value) {
+ $this->query->condition('base.' . $field, $value);
+ }
+ }
+ }
+
+ /**
+ * Attach data to entities upon loading.
+ *
+ * This will attach fields, if the entity is fieldable. It also calls
+ * hook_ENTITY_TYPE_load() on the loaded entites. If your hook_ENTITY_TYPE_load()
+ * expects special parameters apart from the queried entities, you can set
+ * $this->hook_load_arguments prior to calling the method.
+ * See NodeLoader::attachLoad for an example.
+ */
+ protected function attachLoad(&$queried_entities) {
+ // Attach fields.
+ if ($this->entity_info['fieldable']) {
+ if ($this->revision_id) {
+ field_attach_load_revision($this->entity_type, $queried_entities);
+ }
+ else {
+ field_attach_load($this->entity_type, $queried_entities);
+ }
+ }
+
+ // Call hook_ENTITY_TYPE_load(), pass the entity types so modules can return early
+ // if not acting on types in the array.
+ // The first argument for hook_TYPE_load() are always the queried entities,
+ // followed by optionally additional arguments.
+ $args = array_merge(array($queried_entities), $this->hook_load_arguments);
+ foreach (module_implements($this->entity_type . '_load') as $module) {
+ call_user_func_array($module . '_' . $this->entity_type . '_load', $args);
+ }
+ }
+}
diff --git includes/file.inc includes/file.inc
index e59b1a8..43a5333 100644
--- includes/file.inc
+++ includes/file.inc
@@ -272,30 +272,7 @@ function file_check_location($source, $directory = '') {
* @see file_load()
*/
function file_load_multiple($fids = array(), $conditions = array()) {
- $query = db_select('files', 'f')->fields('f');
-
- // If the $fids array is populated, add those to the query.
- if ($fids) {
- $query->condition('f.fid', $fids, 'IN');
- }
-
- // If the conditions array is populated, add those to the query.
- if ($conditions) {
- foreach ($conditions as $field => $value) {
- $query->condition('f.' . $field, $value);
- }
- }
- $files = $query->execute()->fetchAllAssoc('fid');
-
- // Invoke hook_file_load() on the terms loaded from the database
- // and add them to the static cache.
- if (!empty($files)) {
- foreach (module_implements('file_load') as $module) {
- $function = $module . '_file_load';
- $function($files);
- }
- }
- return $files;
+ return drupal_load_multiple('file', $fids, $conditions);
}
/**
diff --git modules/field/field.api.php modules/field/field.api.php
index a1518ed..b973995 100644
--- modules/field/field.api.php
+++ modules/field/field.api.php
@@ -7,53 +7,6 @@
*/
/**
- * Inform the Field API about one or more fieldable types.
- *
- * Inform the Field API about one or more fieldable types, (object
- * types to which fields can be attached).
- *
- * @return
- * An array whose keys are fieldable object type names and
- * whose values identify properties of those types that the Field
- * system needs to know about:
- *
- * name: The human-readable name of the type.
- * id key: The object property that contains the primary id for the
- * object. Every object passed to the Field API must
- * have this property and its value must be numeric.
- * revision key: The object property that contains the revision id
- * for the object, or NULL if the object type is not
- * versioned. The Field API assumes that all revision ids are
- * unique across all instances of a type; this means, for example,
- * that every object's revision ids cannot be 0, 1, 2, ...
- * bundle key: The object property that contains the bundle name for
- * the object (bundle name is what nodes call "content type").
- * The bundle name defines which fields are connected to the object.
- * cacheable: A boolean indicating whether Field API should cache
- * loaded fields for each object, reducing the cost of
- * field_attach_load().
- * bundles: An array of all existing bundle names for this object
- * type. TODO: Define format. TODO: I'm unclear why we need
- * this.
- */
-function hook_fieldable_info() {
- $return = array(
- 'node' => array(
- 'name' => t('Node'),
- 'id key' => 'nid',
- 'revision key' => 'vid',
- 'bundle key' => 'type',
- // Node.module handles its own caching.
- 'cacheable' => FALSE,
- // Bundles must provide human readable name so
- // we can create help and error messages about them.
- 'bundles' => node_get_types('names'),
- ),
- );
- return $return;
-}
-
-/**
* @} End of "ingroup field_fieldable_type"
*/
diff --git modules/field/field.info.inc modules/field/field.info.inc
index 77fcfc9..24ef6fe 100644
--- modules/field/field.info.inc
+++ modules/field/field.info.inc
@@ -118,24 +118,26 @@ function _field_info_collate_types($reset = FALSE) {
}
// Populate information about 'fieldable' entities.
- foreach (module_implements('fieldable_info') as $module) {
- $fieldable_types = (array) module_invoke($module, 'fieldable_info');
+ foreach (module_implements('entity_info') as $module) {
+ $fieldable_types = (array) module_invoke($module, 'entity_info');
foreach ($fieldable_types as $name => $fieldable_info) {
- // Provide defaults.
- $fieldable_info += array(
- 'revision key' => '',
- 'bundle key' => '',
- 'cacheable' => TRUE,
- 'bundles' => array(),
- );
- // If no bundle key provided, then we assume a single bundle, named
- // after the type of the object. Make sure the bundle created
- // has the human-readable name we need for bundle messages.
- if (empty($fieldable_info['bundle key'])) {
- $fieldable_info['bundles'] = array($name => $fieldable_info['name']);
+ if (!empty($fieldable_info['fieldable'])) {
+ // Provide defaults.
+ $fieldable_info += array(
+ 'revision key' => '',
+ 'bundle key' => '',
+ 'cacheable' => TRUE,
+ 'bundles' => array(),
+ );
+ // If no bundle key provided, then we assume a single bundle, named
+ // after the type of the object. Make sure the bundle created
+ // has the human-readable name we need for bundle messages.
+ if (empty($fieldable_info['bundle key'])) {
+ $fieldable_info['bundles'] = array($name => $fieldable_info['name']);
+ }
+ $info['fieldable types'][$name] = $fieldable_info;
+ $info['fieldable types'][$name]['module'] = $module;
}
- $info['fieldable types'][$name] = $fieldable_info;
- $info['fieldable types'][$name]['module'] = $module;
}
}
diff --git modules/field/field.test modules/field/field.test
index 5bb4ff8..444fd87 100644
--- modules/field/field.test
+++ modules/field/field.test
@@ -352,7 +352,7 @@ class FieldAttachTestCase extends DrupalWebTestCase {
function testFieldAttachCreateRenameBundle() {
// Create a new bundle. This has to be initiated by the module so that its
- // hook_fieldable_info() is consistent.
+ // hook_entity_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_create_bundle($new_bundle, $this->randomName());
@@ -376,7 +376,7 @@ class FieldAttachTestCase extends DrupalWebTestCase {
$this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Data are retrieved for the new bundle");
// Rename the bundle. This has to be initiated by the module so that its
- // hook_fieldable_info() is consistent.
+ // hook_entity_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_rename_bundle($this->instance['bundle'], $new_bundle);
@@ -392,7 +392,7 @@ class FieldAttachTestCase extends DrupalWebTestCase {
function testFieldAttachDeleteBundle() {
// Create a new bundle. This has to be initiated by the module so that its
- // hook_fieldable_info() is consistent.
+ // hook_entity_info() is consistent.
$new_bundle = 'test_bundle_' . drupal_strtolower($this->randomName());
field_test_create_bundle($new_bundle, $this->randomName());
@@ -435,7 +435,7 @@ class FieldAttachTestCase extends DrupalWebTestCase {
$this->assertEqual(count($entity->{$field_name}), 1, "Second field got loaded");
// Delete the bundle. This has to be initiated by the module so that its
- // hook_fieldable_info() is consistent.
+ // hook_entity_info() is consistent.
field_test_delete_bundle($this->instance['bundle']);
// Verify no data gets loaded
diff --git modules/forum/forum.test modules/forum/forum.test
index 14f817d..eb444e2 100644
--- modules/forum/forum.test
+++ modules/forum/forum.test
@@ -135,7 +135,7 @@ class ForumTestCase extends DrupalWebTestCase {
$this->assertRaw(t('Updated vocabulary %name.', array('%name' => $title)), t('Vocabulary was edited'));
// Grab the newly edited vocabulary.
- drupal_static_reset('taxonomy_vocabulary_load_multiple');
+ drupal_get_entity_loader('taxonomy_vocabulary')->resetCache();
$current_settings = taxonomy_vocabulary_load($vid);
// Make sure we actually edited the vocabulary properly.
diff --git modules/node/node.module modules/node/node.module
index 605c843..9489494 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -154,14 +154,17 @@ function node_cron() {
}
/**
- * Implementation of hook_fieldable_info().
+ * Implementation of hook_entity_info().
*/
-function node_fieldable_info() {
+function node_entity_info() {
$return = array(
'node' => array(
'name' => t('Node'),
+ 'loader class' => 'NodeLoader',
+ 'base table' => 'node',
'id key' => 'nid',
'revision key' => 'vid',
+ 'fieldable' => TRUE,
'bundle key' => 'type',
// Node.module handles its own caching.
// 'cacheable' => FALSE,
@@ -200,7 +203,7 @@ function node_field_build_modes($obj_type) {
* Gather a listing of links to nodes.
*
* @param $result
- * A DB result object from a query to fetch node objects. If your query
+ * A DB result object from a query to fetch node entites. If your query
* joins the node_comment_statistics
table so that the
* comment_count
field is available, a title attribute will
* be added to show the number of comments.
@@ -771,7 +774,7 @@ function node_invoke($node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
}
/**
- * Load node objects from the database.
+ * Load node entites from the database.
*
* This function should be used whenever you need to load more than one node
* from the database. Nodes are loaded into memory and will not require
@@ -785,151 +788,10 @@ function node_invoke($node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
* Whether to reset the internal node_load cache.
*
* @return
- * An array of node objects indexed by nid.
+ * An array of node entites indexed by nid.
*/
function node_load_multiple($nids = array(), $conditions = array(), $reset = FALSE) {
- static $node_cache = array();
- if ($reset) {
- $node_cache = array();
- }
- $nodes = array();
-
- // Create a new variable which is either a prepared version of the $nids
- // array for later comparison with the node cache, or FALSE if no $nids were
- // passed. The $nids array is reduced as items are loaded from cache, and we
- // need to know if it's empty for this reason to avoid querying the database
- // when all requested nodes are loaded from cache.
- $passed_nids = !empty($nids) ? array_flip($nids) : FALSE;
-
- // Revisions are not statically cached, and require a different query to
- // other conditions, so separate vid into its own variable.
- $vid = isset($conditions['vid']) ? $conditions['vid'] : FALSE;
- unset($conditions['vid']);
-
- // Load any available nodes from the internal cache.
- if ($node_cache && !$vid) {
- if ($nids) {
- $nodes += array_intersect_key($node_cache, $passed_nids);
- // If any nodes were loaded, remove them from the $nids still to load.
- $nids = array_keys(array_diff_key($passed_nids, $nodes));
- }
- // If loading nodes only by conditions, fetch all available nodes from
- // the cache. Nodes which don't match are removed later.
- elseif ($conditions) {
- $nodes = $node_cache;
- }
- }
-
- // Exclude any nodes loaded from cache if they don't match $conditions.
- // This ensures the same behavior whether loading from memory or database.
- if ($conditions) {
- foreach ($nodes as $node) {
- $node_values = (array) $node;
- if (array_diff_assoc($conditions, $node_values)) {
- unset($nodes[$node->nid]);
- }
- }
- }
-
- // Load any remaining nodes from the database. This is the case if there are
- // any $nids left to load, if loading a revision, or if $conditions was
- // passed without $nids.
- if ($nids || $vid || ($conditions && !$passed_nids)) {
- $query = db_select('node', 'n');
-
- if ($vid) {
- $query->join('node_revision', 'r', 'r.nid = n.nid AND r.vid = :vid', array(':vid' => $vid));
- }
- else {
- $query->join('node_revision', 'r', 'r.vid = n.vid');
- }
- $query->join('users', 'u', 'u.uid = n.uid');
-
- // Add fields from the {node} table.
- $node_fields = drupal_schema_fields_sql('node');
-
- // vid and title are provided by node_revision, so remove them.
- unset($node_fields['vid']);
- unset($node_fields['title']);
- $query->fields('n', $node_fields);
-
- // Add all fields from the {node_revision} table.
- $node_revision_fields = drupal_schema_fields_sql('node_revision');
-
- // nid is provided by node, so remove it.
- unset($node_revision_fields['nid']);
-
- // Change timestamp to revision_timestamp before adding it to the query.
- unset($node_revision_fields['timestamp']);
- $query->addField('r', 'timestamp', 'revision_timestamp');
- $query->fields('r', $node_revision_fields);
-
- // Add fields from the {users} table.
- $user_fields = array('name', 'picture', 'data');
- $query->fields('u', $user_fields);
-
- if ($nids) {
- $query->condition('n.nid', $nids, 'IN');
- }
- if ($conditions) {
- foreach ($conditions as $field => $value) {
- $query->condition('n.' . $field, $value);
- }
- }
- $queried_nodes = $query->execute()->fetchAllAssoc('nid');
- }
-
- // Pass all nodes loaded from the database through the node type specific
- // callbacks and hook_node_load(), then add them to the internal cache.
- if (!empty($queried_nodes)) {
- // Create an array of nodes for each content type and pass this to the
- // node type specific callback.
- $typed_nodes = array();
- foreach ($queried_nodes as $nid => $node) {
- $typed_nodes[$node->type][$nid] = $node;
- }
-
- // Call node type specific callbacks on each typed array of nodes.
- foreach ($typed_nodes as $type => $nodes_of_type) {
- if (node_hook($type, 'load')) {
- $function = node_get_types('base', $type) . '_load';
- $function($nodes_of_type);
- }
- }
-
- // Attach fields.
- if ($vid) {
- field_attach_load_revision('node', $queried_nodes);
- }
- else {
- field_attach_load('node', $queried_nodes);
- }
-
- // Call hook_node_load(), pass the node types so modules can return early
- // if not acting on types in the array.
- foreach (module_implements('node_load') as $module) {
- $function = $module . '_node_load';
- $function($queried_nodes, array_keys($typed_nodes));
- }
- $nodes += $queried_nodes;
- // Add nodes to the cache if we're not loading a revision.
- if (!$vid) {
- $node_cache += $queried_nodes;
- }
- }
-
- // Ensure that the returned array is ordered the same as the original $nids
- // array if this was passed in and remove any invalid nids.
- if ($passed_nids) {
- // Remove any invalid nids from the array.
- $passed_nids = array_intersect_key($passed_nids, $nodes);
- foreach ($nodes as $node) {
- $passed_nids[$node->nid] = $node;
- }
- $nodes = $passed_nids;
- }
-
- return $nodes;
+ return drupal_load_multiple('node', $nids, $conditions, $reset);
}
/**
@@ -948,7 +810,6 @@ function node_load_multiple($nids = array(), $conditions = array(), $reset = FAL
function node_load($nid, $vid = array(), $reset = FALSE) {
$vid = isset($vid) ? array('vid' => $vid) : NULL;
$node = node_load_multiple(array($nid), $vid, $reset);
-
return $node ? $node[$nid] : FALSE;
}
@@ -3198,3 +3059,33 @@ function node_elements() {
function theme_node_links($element) {
return theme('links', $element['#value'], array('class' => 'links inline'));
}
+
+class NodeLoader extends DrupalDefaultEntityLoader {
+ protected function buildQuery() {
+ parent::buildQuery();
+ $this->query->join('users', 'u', 'u.uid = base.uid');
+
+ // Add fields from the {users} table.
+ $user_fields = array('name', 'picture', 'data');
+ $this->query->fields('u', $user_fields);
+ }
+
+ protected function attachLoad(&$nodes) {
+ // Create an array of nodes for each content type and pass this to the
+ // object type specific callback.
+ $typed_nodes = array();
+ foreach ($nodes as $id => $object) {
+ $typed_nodes[$object->type][$id] = $object;
+ }
+
+ // Call object type specific callbacks on each typed array of nodes.
+ foreach ($typed_nodes as $node_type => $nodes_of_type) {
+ if (node_hook($node_type, 'load')) {
+ $function = node_get_types('base', $node_type) . '_load';
+ $function($nodes_of_type);
+ }
+ }
+ $this->hook_load_arguments[] = array_keys($typed_nodes);
+ parent::attachLoad($nodes);
+ }
+}
\ No newline at end of file
diff --git modules/simpletest/tests/field_test.module modules/simpletest/tests/field_test.module
index 762924b..d74d4c2 100644
--- modules/simpletest/tests/field_test.module
+++ modules/simpletest/tests/field_test.module
@@ -26,7 +26,7 @@ function field_test_perm() {
*/
function field_test_menu() {
$items = array();
- $info = field_test_fieldable_info();
+ $info = field_test_entity_info();
foreach (array_keys($info['test_entity']['bundles']) as $bundle) {
$bundle_url_str = str_replace('_', '-', $bundle);
@@ -60,11 +60,12 @@ function field_test_menu() {
/**
* Define a test fieldable entity.
*/
-function field_test_fieldable_info() {
+function field_test_entity_info() {
$bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
return array(
'test_entity' => array(
'name' => t('Test Entity'),
+ 'fieldable' => TRUE,
'id key' => 'ftid',
'revision key' => 'ftvid',
'cacheable' => FALSE,
@@ -74,6 +75,7 @@ function field_test_fieldable_info() {
// This entity type doesn't get form handling for now...
'test_cacheable_entity' => array(
'name' => t('Test Entity, cacheable'),
+ 'fieldable' => TRUE,
'id key' => 'ftid',
'revision key' => 'ftvid',
'cacheable' => TRUE,
diff --git modules/system/system.api.php modules/system/system.api.php
index fd0bfc8..5e98be0 100644
--- modules/system/system.api.php
+++ modules/system/system.api.php
@@ -12,6 +12,65 @@
*/
/**
+ * Inform the base system and the Field API about one or more entity types.
+ *
+ * Inform the system about one or more entity types (object types that can be
+ * loaded via drupal_load_multiple() and, optionally, to which fields can be
+ * attached).
+ *
+ * @return
+ * An array whose keys are entity type names and
+ * whose values identify properties of those types that the Field
+ * system needs to know about:
+ *
+ * name: The human-readable name of the type.
+ * loader class: The name of the class that is used to load the objects.
+ * The class has to implement the DrupalEntityLoader interface. Leave blank
+ * to use the DefaultDrupalEntityLoader implementation.
+ * base table: (used by DefaultDrupalEntityLoader) The name of the entity
+ * type's base table.
+ * static cache: (used by DefaultDrupalEntityLoader) FALSE to disable static
+ * caching of loaded entities during a page request. Defaults to TRUE.
+ * fieldable: Set to TRUE if you want your entity type to be fieldable.
+ * id key: The object property that contains the primary id for the
+ * object. Every object passed to the Field API must
+ * have this property and its value must be numeric.
+ * revision key: The object property that contains the revision id
+ * for the object, or NULL if the object type is not
+ * versioned. The Field API assumes that all revision ids are
+ * unique across all instances of a type; this means, for example,
+ * that every object's revision ids cannot be 0, 1, 2, ...
+ * bundle key: The object property that contains the bundle name for
+ * the object (bundle name is what nodes call "content type").
+ * The bundle name defines which fields are connected to the object.
+ * cacheable: A boolean indicating whether Field API should cache
+ * loaded fields for each object, reducing the cost of
+ * field_attach_load().
+ * bundles: An array of all existing bundle names for this object
+ * type. TODO: Define format. TODO: I'm unclear why we need
+ * this.
+ */
+function hook_entity_info() {
+ $return = array(
+ 'node' => array(
+ 'name' => t('Node'),
+ 'loader class' => 'NodeLoader',
+ 'base table' => 'node',
+ 'id key' => 'nid',
+ 'revision key' => 'vid',
+ 'fieldable' => TRUE,
+ 'bundle key' => 'type',
+ // Node.module handles its own caching.
+ // 'cacheable' => FALSE,
+ // Bundles must provide human readable name so
+ // we can create help and error messages about them.
+ 'bundles' => node_get_types('names'),
+ ),
+ );
+ return $return;
+}
+
+/**
* Perform periodic actions.
*
* Modules that require to schedule some commands to be executed at regular
diff --git modules/system/system.module modules/system/system.module
index 58aa9b9..cbd82cb 100644
--- modules/system/system.module
+++ modules/system/system.module
@@ -228,6 +228,20 @@ function system_rdf_namespaces() {
}
/**
+ * Implementation of hook_entity_info().
+ */
+function system_entity_info() {
+ return array(
+ 'file' => array(
+ 'name' => t('File'),
+ 'base table' => 'files',
+ 'id key' => 'fid',
+ 'static cache' => FALSE,
+ ),
+ );
+}
+
+/**
* Implementation of hook_elements().
*/
function system_elements() {
diff --git modules/taxonomy/taxonomy.module modules/taxonomy/taxonomy.module
index 8e412c0..83372ea 100644
--- modules/taxonomy/taxonomy.module
+++ modules/taxonomy/taxonomy.module
@@ -263,7 +263,7 @@ function taxonomy_vocabulary_save($vocabulary) {
}
cache_clear_all();
- drupal_static_reset('taxonomy_vocabulary_load_multiple');
+ drupal_get_entity_loader('taxonomy_vocabulary')->resetCache();
return $status;
}
@@ -293,7 +293,7 @@ function taxonomy_vocabulary_delete($vid) {
module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
cache_clear_all();
- drupal_static_reset('taxonomy_vocabulary_load_multiple');
+ drupal_get_entity_loader('taxonomy_vocabulary')->resetCache();
return SAVED_DELETED;
}
@@ -503,7 +503,7 @@ function taxonomy_terms_static_reset() {
drupal_static_reset('taxonomy_term_count_nodes');
drupal_static_reset('taxonomy_get_tree');
drupal_static_reset('taxonomy_get_synonym_root');
- drupal_static_reset('taxonomy_term_load_multiple');
+ drupal_get_entity_loader('taxonomy_term')->resetCache();
drupal_static_reset('taxonomy_get_term_data');
}
@@ -553,7 +553,7 @@ function taxonomy_form_all($free_tags = 0) {
*/
function taxonomy_get_vocabularies($type = NULL) {
$conditions = !empty($type) ? array('type' => $type) : NULL;
- return taxonomy_vocabulary_load_multiple(array(), $conditions);
+ return taxonomy_vocabulary_load_multiple(FALSE, $conditions);
}
/**
@@ -1113,99 +1113,30 @@ function taxonomy_get_term_by_name($name) {
->execute()->fetchAll();
}
-/**
- * Load multiple taxonomy vocabularies based on certain conditions.
- *
- * This function should be used whenever you need to load more than one
- * vocabulary from the database. Terms are loaded into memory and will not
- * require database access if loaded again during the same page request.
- *
- * @param $vids
- * An array of taxonomy vocabulary IDs.
- * @param $conditions
- * An array of conditions to add to the query.
- *
- * @return
- * An array of vocabulary objects, indexed by vid.
- */
-function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array()) {
- $vocabulary_cache = &drupal_static(__FUNCTION__, array());
- // Node type associations are not stored in the vocabulary table, so remove
- // this from conditions into it's own variable.
- if (isset($conditions['type'])) {
- $type = $conditions['type'];
- unset($conditions['type']);
- }
-
- $vocabularies = array();
-
- // Create a new variable which is either a prepared version of the $vids
- // array for later comparison with the term cache, or FALSE if no $vids were
- // passed. The $vids array is reduced as items are loaded from cache, and we
- // need to know if it's empty for this reason to avoid querying the database
- // when all requested items are loaded from cache.
- $passed_vids = !empty($vids) ? array_flip($vids) : FALSE;
-
- // Load any available items from the internal cache.
- if ($vocabulary_cache) {
- if ($vids) {
- $vocabularies += array_intersect_key($vocabulary_cache, $passed_vids);
- // If any items were loaded, remove them from the $vids still to load.
- $vids = array_keys(array_diff_key($passed_vids, $vocabularies));
- }
- // If only conditions is passed, load all items from the cache. Items
- // which don't match conditions will be removed later.
- elseif ($conditions) {
- $vocabularies = $vocabulary_cache;
- }
- }
+class TaxonomyVocabularyLoader extends DrupalDefaultEntityLoader {
+ protected $type;
- // Remove any loaded terms from the array if they don't match $conditions.
- if ($conditions || isset($type)) {
- foreach ($vocabularies as $vocabulary) {
- $vocabulary_values = (array) $vocabulary;
- if (array_diff_assoc($conditions, $vocabulary_values)) {
- unset($vocabularies[$vocabulary->vid]);
- }
- if (isset($type) && !in_array($type, $vocabulary->nodes)) {
- unset($vocabularies[$vocabulary->vid]);
- }
+ public function loadMultiple($ids = array(), $conditions = array()) {
+ if (isset($conditions['type'])) {
+ $this->type = $conditions['type'];
+ unset($conditions['type']);
}
+ return parent::loadMultiple($ids, $conditions);
}
- // Load any remaining vocabularies from the database, this is necessary if
- // we have $vids still to load, or if no $vids were passed.
- if ($vids || !$passed_vids) {
- $query = db_select('taxonomy_vocabulary', 'v');
- $query->addField('n', 'type');
- $query
- ->fields('v')
- ->orderBy('v.weight')
- ->orderBy('v.name');
-
- if (!empty($type)) {
- $query->leftJoin('taxonomy_vocabulary_node_type', 'n', 'v.vid = n.vid AND n.type = :type', array(':type' => $type));
+ protected function buildQuery() {
+ parent::buildQuery();
+ $this->query->addField('n', 'type');
+ if (!empty($this->type)) {
+ $this->query->leftJoin('taxonomy_vocabulary_node_type', 'n', 'base.vid = n.vid AND n.type = :type', array(':type' => $this->type));
}
else {
- $query->leftJoin('taxonomy_vocabulary_node_type', 'n', 'v.vid = n.vid');
- }
-
- // If the $vids array is populated, add those to the query.
- if ($vids) {
- $query->condition('v.vid', $vids, 'IN');
+ $this->query->leftJoin('taxonomy_vocabulary_node_type', 'n', 'base.vid = n.vid');
}
+ }
- // If the conditions array is populated, add those to the query.
- if ($conditions) {
- foreach ($conditions as $field => $value) {
- $query->condition('v.' . $field, $value);
- }
- }
- $result = $query->execute();
-
- $queried_vocabularies = array();
- $node_types = array();
- foreach ($result as $record) {
+ protected function attachLoad(&$records) {
+ foreach ($records as $record) {
// If no node types are associated with a vocabulary, the LEFT JOIN will
// return a NULL value for type.
if (isset($record->type)) {
@@ -1218,31 +1149,28 @@ function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array(
}
$queried_vocabularies[$record->vid] = $record;
}
-
- // Invoke hook_taxonomy_vocabulary_load() on the vocabularies loaded from
- // the database and add them to the static cache.
- if (!empty($queried_vocabularies)) {
- foreach (module_implements('taxonomy_vocabulary_load') as $module) {
- $function = $module . '_taxonomy_vocabulary_load';
- $function($queried_vocabularies);
- }
- $vocabularies += $queried_vocabularies;
- $vocabulary_cache += $queried_vocabularies;
- }
- }
-
- // Ensure that the returned array is ordered the same as the original $vids
- // array if this was passed in and remove any invalid vids.
- if ($passed_vids) {
- // Remove any invalid vids from the array.
- $passed_vids = array_intersect_key($passed_vids, $vocabularies);
- foreach ($vocabularies as $vocabulary) {
- $passed_vids[$vocabulary->vid] = $vocabulary;
- }
- $vocabularies = $passed_vids;
+ $records = $queried_vocabularies;
+ parent::attachLoad($records);
}
+}
- return $vocabularies;
+/**
+ * Load multiple taxonomy vocabularies based on certain conditions.
+ *
+ * This function should be used whenever you need to load more than one
+ * vocabulary from the database. Terms are loaded into memory and will not
+ * require database access if loaded again during the same page request.
+ *
+ * @param $vids
+ * An array of taxonomy vocabulary IDs, or FALSE to load all vocabularies.
+ * @param $conditions
+ * An array of conditions to add to the query.
+ *
+ * @return
+ * An array of vocabulary objects, indexed by vid.
+ */
+function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array()) {
+ return drupal_load_multiple('taxonomy_vocabulary', $vids, $conditions);
}
/**
@@ -1271,6 +1199,27 @@ function taxonomy_terms_load($str_tids) {
}
/**
+ * Implementation of hook_entity_info().
+ */
+function taxonomy_entity_info() {
+ return array(
+ 'taxonomy_term' => array(
+ 'name' => t('Taxonomy term'),
+ 'base table' => 'taxonomy_term_data',
+ 'id key' => 'tid',
+ 'fieldable' => FALSE,
+ ),
+ 'taxonomy_vocabulary' => array(
+ 'name' => t('Taxonomy vocabulary'),
+ 'loader class' => 'TaxonomyVocabularyLoader',
+ 'base table' => 'taxonomy_vocabulary',
+ 'id key' => 'vid',
+ 'fieldable' => FALSE,
+ )
+ );
+}
+
+/**
* Load multiple taxonomy terms based on certain conditions.
*
* This function should be used whenever you need to load more than one term
@@ -1286,84 +1235,7 @@ function taxonomy_terms_load($str_tids) {
* An array of term objects, indexed by tid.
*/
function taxonomy_term_load_multiple($tids = array(), $conditions = array()) {
- $term_cache = &drupal_static(__FUNCTION__, array());
-
- $terms = array();
-
- // Create a new variable which is either a prepared version of the $tids
- // array for later comparison with the term cache, or FALSE if no $tids were
- // passed. The $tids array is reduced as items are loaded from cache, and we
- // need to know if it's empty for this reason to avoid querying the database
- // when all requested terms are loaded from cache.
- $passed_tids = !empty($tids) ? array_flip($tids) : FALSE;
-
- // Load any available terms from the internal cache.
- if ($term_cache) {
- if ($tids) {
- $terms += array_intersect_key($term_cache, $passed_tids);
- // If any terms were loaded, remove them from the $tids still to load.
- $tids = array_keys(array_diff_key($passed_tids, $terms));
- }
- // If only conditions is passed, load all terms from the cache. Terms
- // which don't match conditions will be removed later.
- elseif ($conditions) {
- $terms = $term_cache;
- }
- }
-
- // Remove any loaded terms from the array if they don't match $conditions.
- if ($conditions) {
- foreach ($terms as $term) {
- $term_values = (array) $term;
- if (array_diff_assoc($conditions, $term_values)) {
- unset($terms[$term->tid]);
- }
- }
- }
-
- // Load any remaining terms from the database, this is necessary if we have
- // $tids still to load, or if $conditions was passed without $tids.
- if ($tids || ($conditions && !$passed_tids)) {
- $query = db_select('taxonomy_term_data', 't');
- $taxonomy_term_data = drupal_schema_fields_sql('taxonomy_term_data');
- $query->fields('t', $taxonomy_term_data);
-
- // If the $tids array is populated, add those to the query.
- if ($tids) {
- $query->condition('t.tid', $tids, 'IN');
- }
-
- // If the conditions array is populated, add those to the query.
- if ($conditions) {
- foreach ($conditions as $field => $value) {
- $query->condition('t.' . $field, $value);
- }
- }
- $queried_terms = $query->execute()->fetchAllAssoc('tid');
- // Invoke hook_taxonomy_term_load() on the terms loaded from the database
- // and add them to the static cache.
- if (!empty($queried_terms)) {
- foreach (module_implements('taxonomy_term_load') as $module) {
- $function = $module . '_taxonomy_term_load';
- $function($queried_terms);
- }
- $terms += $queried_terms;
- $term_cache += $queried_terms;
- }
- }
-
- // Ensure that the returned array is ordered the same as the original $tids
- // array if this was passed in and remove any invalid tids.
- if ($passed_tids) {
- // Remove any invalid tids from the array.
- $passed_tids = array_intersect_key($passed_tids, $terms);
- foreach ($terms as $term) {
- $passed_tids[$term->tid] = $term;
- }
- $terms = $passed_tids;
- }
-
- return $terms;
+ return drupal_load_multiple('taxonomy_term', $tids, $conditions);
}
/**
diff --git modules/taxonomy/taxonomy.test modules/taxonomy/taxonomy.test
index 91baa12..6107f7f 100644
--- modules/taxonomy/taxonomy.test
+++ modules/taxonomy/taxonomy.test
@@ -151,7 +151,7 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase {
// Check the created vocabulary.
$vocabularies = taxonomy_get_vocabularies();
$vid = $vocabularies[count($vocabularies)-1]->vid;
- drupal_static_reset('taxonomy_vocabulary_load_multiple');
+ drupal_get_entity_loader('taxonomy_vocabulary')->resetCache();
$vocabulary = taxonomy_vocabulary_load($vid);
$this->assertTrue($vocabulary, t('Vocabulary found in database'));
@@ -164,7 +164,7 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase {
// Confirm deletion.
$this->drupalPost(NULL, NULL, t('Delete'));
$this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), t('Vocabulary deleted'));
- drupal_static_reset('taxonomy_vocabulary_load_multiple');
+ drupal_get_entity_loader('taxonomy_vocabulary')->resetCache();
$this->assertFalse(taxonomy_vocabulary_load($vid), t('Vocabulary is not found in the database'));
}
}
@@ -278,7 +278,7 @@ public static function getInfo() {
$this->assertTrue(current(taxonomy_vocabulary_load_multiple(array($vocabulary1->vid), array('name' => $vocabulary1->name))) == $vocabulary1, t('Vocabulary loaded successfully by name and ID.'));
// Fetch vocabulary 1 with specified node type.
- drupal_static_reset('taxonomy_vocabulary_load_multiple');
+ drupal_get_entity_loader('taxonomy_vocabulary')->resetCache();
$vocabulary_node_type = current(taxonomy_vocabulary_load_multiple(array($vocabulary1->vid), array('type' => 'article')));
$this->assertEqual($vocabulary_node_type, $vocabulary1, t('Vocabulary with specified node type loaded successfully.'));
}
diff --git modules/user/user.module modules/user/user.module
index ecf36ee..67335de 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -86,11 +86,14 @@ function user_theme() {
/**
* Implementation of hook_fieldable_info().
*/
-function user_fieldable_info() {
+function user_entity_info() {
$return = array(
'user' => array(
'name' => t('User'),
+ 'loader class' => 'UserLoader',
+ 'base table' => 'users',
'id key' => 'uid',
+ 'fieldable' => TRUE,
),
);
return $return;
@@ -182,63 +185,17 @@ function user_external_login($account, $edit = array()) {
* @see user_load_by_name()
*/
function user_load_multiple($uids = array(), $conditions = array(), $reset = FALSE) {
- static $user_cache = array();
- if ($reset) {
- $user_cache = array();
- }
-
- $users = array();
-
- // Create a new variable which is either a prepared version of the $uids
- // array for later comparison with the user cache, or FALSE if no $uids were
- // passed. The $uids array is reduced as items are loaded from cache, and we
- // need to know if it's empty for this reason to avoid querying the database
- // when all requested users are loaded from cache.
- $passed_uids = !empty($uids) ? array_flip($uids) : FALSE;
-
- // Load any available users from the internal cache.
- if ($user_cache) {
- if ($uids && !$conditions) {
- $users += array_intersect_key($user_cache, $passed_uids);
- // If any users were loaded, remove them from the $uids still to load.
- $uids = array_keys(array_diff_key($passed_uids, $users));
- }
- }
-
- // Load any remaining users from the database, this is necessary if we have
- // $uids still to load, or if $conditions was passed without $uids.
- if ($uids || ($conditions && !$passed_uids)) {
- $query = db_select('users', 'u')->fields('u');
-
- // If the $uids array is populated, add those to the query.
- if ($uids) {
- $query->condition('u.uid', $uids, 'IN');
- }
- // If the conditions array is populated, add those to the query.
- if ($conditions) {
- // TODO D7: Using LIKE() to get a case insensitive comparison because Crell
- // and chx promise that dbtng will map it to ILIKE in postgres.
- if (isset($conditions['name'])) {
- $query->condition('u.name', $conditions['name'], 'LIKE');
- unset($conditions['name']);
- }
- if (isset($conditions['mail'])) {
- $query->condition('u.mail', $conditions['mail'], 'LIKE');
- unset($conditions['mail']);
- }
- foreach ($conditions as $field => $value) {
- $query->condition('u.' . $field, $value);
- }
- }
- $result = $query->execute();
+ return drupal_load_multiple('user', $uids, $conditions, $reset);
+}
- $queried_users = array();
+class UserLoader extends DrupalDefaultEntityLoader {
+ function attachLoad(&$queried_users) {
// Build an array of user picture IDs so that these can be fetched later.
$picture_fids = array();
- foreach ($result as $record) {
+ foreach ($queried_users as $key => $record) {
$picture_fids[] = $record->picture;
- $queried_users[$record->uid] = drupal_unpack($record);
- $queried_users[$record->uid]->roles = array();
+ $queried_users[$key] = drupal_unpack($record);
+ $queried_users[$key]->roles = array();
if ($record->uid) {
$queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
}
@@ -247,57 +204,29 @@ function user_load_multiple($uids = array(), $conditions = array(), $reset = FAL
}
}
- if (!empty($queried_users)) {
- // Add any additional roles from the database.
- $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
- foreach ($result as $record) {
- $queried_users[$record->uid]->roles[$record->rid] = $record->name;
- }
+ // Add any additional roles from the database.
+ $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
+ foreach ($result as $record) {
+ $queried_users[$record->uid]->roles[$record->rid] = $record->name;
+ }
- // Add the full file objects for user pictures if enabled.
- if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
- $pictures = file_load_multiple($picture_fids);
- foreach ($queried_users as $account) {
- if (!empty($account->picture) && isset($pictures[$account->picture])) {
- $account->picture = $pictures[$account->picture];
- }
- else {
- $account->picture = NULL;
- }
+ // Add the full file objects for user pictures if enabled.
+ if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
+ $pictures = file_load_multiple($picture_fids);
+ foreach ($queried_users as $account) {
+ if (!empty($account->picture) && isset($pictures[$account->picture])) {
+ $account->picture = $pictures[$account->picture];
+ }
+ else {
+ $account->picture = NULL;
}
}
-
- field_attach_load('user', $queried_users);
-
- // Invoke hook_user_load() on the users loaded from the database
- // and add them to the static cache.
- foreach (module_implements('user_load') as $module) {
- $function = $module . '_user_load';
- $function($queried_users);
- }
-
-
-
- $users = $users + $queried_users;
- $user_cache = $user_cache + $queried_users;
}
+ // Call the default attachLoad() method. This will add fields and call hook_user_load().
+ parent::attachLoad($queried_users);
}
-
- // Ensure that the returned array is ordered the same as the original $uids
- // array if this was passed in and remove any invalid uids.
- if ($passed_uids) {
- // Remove any invalid uids from the array.
- $passed_uids = array_intersect_key($passed_uids, $users);
- foreach ($users as $user) {
- $passed_uids[$user->uid] = $user;
- }
- $users = $passed_uids;
- }
-
- return $users;
}
-
/**
* Fetch a user object.
*