public function getEntities(array $options = array())
{
_elgg_check_unsupported_site_guid($options);
$defaults = array('types' => ELGG_ENTITIES_ANY_VALUE, 'subtypes' => ELGG_ENTITIES_ANY_VALUE, 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, 'guids' => ELGG_ENTITIES_ANY_VALUE, 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, 'container_guids' => ELGG_ENTITIES_ANY_VALUE, 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE, 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE, 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, 'reverse_order_by' => false, 'order_by' => 'e.time_created desc', 'group_by' => ELGG_ENTITIES_ANY_VALUE, 'limit' => $this->config->get('default_limit'), 'offset' => 0, 'count' => false, 'selects' => array(), 'wheres' => array(), 'joins' => array(), 'preload_owners' => false, 'preload_containers' => false, 'callback' => 'entity_row_to_elggstar', 'distinct' => true, 'batch' => false, 'batch_inc_offset' => true, 'batch_size' => 25, '__ElggBatch' => null);
$options = array_merge($defaults, $options);
if ($options['batch'] && !$options['count']) {
$batch_size = $options['batch_size'];
$batch_inc_offset = $options['batch_inc_offset'];
// clean batch keys from $options.
unset($options['batch'], $options['batch_size'], $options['batch_inc_offset']);
return new \ElggBatch([$this, 'getEntities'], $options, null, $batch_size, $batch_inc_offset);
}
// can't use helper function with type_subtype_pair because
// it's already an array...just need to merge it
if (isset($options['type_subtype_pair'])) {
if (isset($options['type_subtype_pairs'])) {
$options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], $options['type_subtype_pair']);
} else {
$options['type_subtype_pairs'] = $options['type_subtype_pair'];
}
}
$singulars = array('type', 'subtype', 'guid', 'owner_guid', 'container_guid');
$options = _elgg_normalize_plural_options_array($options, $singulars);
$options = $this->autoJoinTables($options);
// evaluate where clauses
if (!is_array($options['wheres'])) {
$options['wheres'] = array($options['wheres']);
}
$wheres = $options['wheres'];
$wheres[] = $this->getEntityTypeSubtypeWhereSql('e', $options['types'], $options['subtypes'], $options['type_subtype_pairs']);
$wheres[] = $this->getGuidBasedWhereSql('e.guid', $options['guids']);
$wheres[] = $this->getGuidBasedWhereSql('e.owner_guid', $options['owner_guids']);
$wheres[] = $this->getGuidBasedWhereSql('e.container_guid', $options['container_guids']);
$wheres[] = $this->getEntityTimeWhereSql('e', $options['created_time_upper'], $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
// see if any functions failed
// remove empty strings on successful functions
foreach ($wheres as $i => $where) {
if ($where === false) {
return false;
} elseif (empty($where)) {
unset($wheres[$i]);
}
}
// remove identical where clauses
$wheres = array_unique($wheres);
// evaluate join clauses
if (!is_array($options['joins'])) {
$options['joins'] = array($options['joins']);
}
// remove identical join clauses
$joins = array_unique($options['joins']);
foreach ($joins as $i => $join) {
if ($join === false) {
return false;
} elseif (empty($join)) {
unset($joins[$i]);
}
}
// evalutate selects
if ($options['selects']) {
$selects = '';
foreach ($options['selects'] as $select) {
$selects .= ", {$select}";
}
} else {
$selects = '';
}
if (!$options['count']) {
$distinct = $options['distinct'] ? "DISTINCT" : "";
$query = "SELECT {$distinct} e.*{$selects} FROM {$this->db->prefix}entities e ";
} else {
// note: when DISTINCT unneeded, it's slightly faster to compute COUNT(*) than GUIDs
$count_expr = $options['distinct'] ? "DISTINCT e.guid" : "*";
$query = "SELECT COUNT({$count_expr}) as total FROM {$this->db->prefix}entities e ";
}
// add joins
foreach ($joins as $j) {
$query .= " {$j} ";
}
// add wheres
$query .= ' WHERE ';
foreach ($wheres as $w) {
$query .= " {$w} AND ";
}
// Add access controls
$query .= _elgg_get_access_where_sql();
// reverse order by
if ($options['reverse_order_by']) {
$options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']);
}
if ($options['count']) {
$total = $this->db->getDataRow($query);
return (int) $total->total;
}
if ($options['group_by']) {
$query .= " GROUP BY {$options['group_by']}";
}
if ($options['order_by']) {
$query .= " ORDER BY {$options['order_by']}";
}
if ($options['limit']) {
$limit = sanitise_int($options['limit'], false);
$offset = sanitise_int($options['offset'], false);
$query .= " LIMIT {$offset}, {$limit}";
}
if ($options['callback'] === 'entity_row_to_elggstar') {
$results = $this->fetchFromSql($query, $options['__ElggBatch']);
} else {
$results = $this->db->getData($query, $options['callback']);
}
if (!$results) {
// no results, no preloading
return $results;
}
// populate entity and metadata caches, and prepare $entities for preloader
$guids = array();
foreach ($results as $item) {
// A custom callback could result in items that aren't \ElggEntity's, so check for them
if ($item instanceof ElggEntity) {
$this->entity_cache->set($item);
// plugins usually have only settings
if (!$item instanceof ElggPlugin) {
$guids[] = $item->guid;
}
}
}
// @todo Without this, recursive delete fails. See #4568
reset($results);
if ($guids) {
// there were entities in the result set, preload metadata for them
$this->metadata_cache->populateFromEntities($guids);
}
if (count($results) > 1) {
$props_to_preload = [];
if ($options['preload_owners']) {
$props_to_preload[] = 'owner_guid';
}
if ($options['preload_containers']) {
$props_to_preload[] = 'container_guid';
}
if ($props_to_preload) {
// note, ElggEntityPreloaderIntegrationTest assumes it can swap out
// the preloader after boot. If you inject this component at construction
// time that unit test will break. :/
_elgg_services()->entityPreloader->preload($results, $props_to_preload);
}
}
return $results;
}