Skip to content

Instantly share code, notes, and snippets.

@mrclay
Created October 27, 2013 03:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrclay/7177538 to your computer and use it in GitHub Desktop.
Save mrclay/7177538 to your computer and use it in GitHub Desktop.
Elgg: alter a query $options array to filter entities having certain tags. This does not require the use of elgg_get_entities_by_metadata and can even be used to filter river queries.
<?php
namespace UFCOE\Elgg;
/**
* Utilities for altering queries based on matching optional/required tags
*
* <code>
* // alter $options so it requires entities to have the tags Foo and Bar:
* $ids = $tag_util->tagsToMetastringIds(array('Foo', 'Bar'));
* $options = $tag_util->requireAllOfTags($options, $ids);
* </code>
*/
class TagUtil {
/**
* @var string The metadata name for tags
*/
public $tags_key = 'tags';
/**
* Get the metastring IDs for a set of strings in one query
*
* @param string[] $tag_names strings stored in metastrings (don't have to be tags)
* @return int[] array of string => metastring ID
*/
public function tagsToMetastringIds(array $tag_names) {
if (!$tag_names) {
return array();
}
$tags_needed = array();
foreach ($tag_names as $i => $tag) {
$tags_needed[$i] = "BINARY '" . sanitise_string($tag) . "'";
}
$dbprefix = elgg_get_config('dbprefix');
$rows = get_data("
SELECT `id`, `string` FROM {$dbprefix}metastrings
WHERE `string` IN (" . implode(', ', $tags_needed) . ")
");
$ret = array();
foreach ($rows as $row) {
$ret[$row->string] = (int)$row->id;
}
// handle missing metastrings
foreach ($tag_names as $tag) {
if (empty($ret[$tag])) {
// use Elgg 1.9 API if available
if (function_exists('elgg_get_metastring_id')) {
$ret[$tag] = elgg_get_metastring_id($tag);
} else {
$ret[$tag] = add_metastring($tag);
}
}
}
return $ret;
}
/**
* Alter query options to require the entity to have any of the given tags
*
* @param array $options Options for elgg_get_entities / elgg_get_river
* @param int[] $tag_metastring_ids Metastring IDs of optional tags
* @param bool $is_river Is the query for the river?
* @return array options array for query
*/
public function requireAnyOfTags(array $options, array $tag_metastring_ids, $is_river = false) {
static $i = 1;
if (!$tag_metastring_ids) {
return $options;
}
$dbprefix = elgg_get_config('dbprefix');
$tags_id = $this->getTagsMetastringId();
$table_alias = "aaa{$i}";
$compare_col = 'e.guid';
if ($is_river) {
$compare_col = 'rv.object_guid';
}
$options['joins'][] = "
JOIN (SELECT DISTINCT md.entity_guid FROM {$dbprefix}metadata md
WHERE md.name_id = {$tags_id} AND md.value_id IN (" . implode(',', $tag_metastring_ids) . "))
AS {$table_alias} ON ({$compare_col} = {$table_alias}.entity_guid)
";
$i += 1;
return $options;
}
/**
* Alter query options to require the entity to have all of the given tags
*
* @param array $options Options for elgg_get_entities / elgg_get_river
* @param int[] $tag_metastring_ids Metastring IDs of required tags
* @param bool $is_river Is the query for the river?
* @return array options array for query
*/
public function requireAllOfTags(array $options, array $tag_metastring_ids, $is_river = false) {
static $i = 1;
if (!$tag_metastring_ids) {
return $options;
}
$compare_col = 'e.guid';
if ($is_river) {
$compare_col = 'rv.object_guid';
}
$dbprefix = elgg_get_config('dbprefix');
$tags_id = $this->getTagsMetastringId();
$table_alias = "aab{$i}";
$options['joins'][] = "
JOIN (SELECT md.entity_guid FROM {$dbprefix}metadata md
WHERE md.name_id = {$tags_id}
AND md.value_id IN (" . implode(',', $tag_metastring_ids) . ")
GROUP BY md.entity_guid
HAVING COUNT(1) = " . count($tag_metastring_ids) . ")
AS {$table_alias} ON ({$compare_col} = {$table_alias}.entity_guid)
";
$i += 1;
return $options;
}
/**
* Alter query options to require the entity to have no tags
*
* @param array $options Options for elgg_get_entities / elgg_get_river
* @return array options array for query
*/
public function requireNoTags(array $options) {
$dbprefix = elgg_get_config('dbprefix');
$tags_id = $this->getTagsMetastringId();
// this will surely get slower as metadata scales up
$options['wheres'][] = "
0 = (SELECT COUNT(1)
FROM {$dbprefix}metadata md
WHERE md.name_id = {$tags_id}
AND md.entity_guid = e.guid)
";
return $options;
}
/**
* Get the metastring ID for "tags"
*
* @return int
*/
public function getTagsMetastringId() {
// use Elgg 1.9 API if available
if (function_exists('elgg_get_metastring_id')) {
return elgg_get_metastring_id($this->tags_key);
} else {
return add_metastring($this->tags_key);
}
}
}
@cloudzard
Copy link

Your class looks really amazing =D!!! I place it under the Elgg classes directory in the vendor/elgg folder but I can't use it :(
Do I have to declare it somewhere else?
And how would be an example of usage in a situation like this?

$result = elgg_list_entities(array(
'type' => 'object',
'subtype' => 'image',
'owner_guid' => NULL,
'limit' => $limit,
'offset' => $offset,
'full_view' => false,
'list_type' => 'gallery',
'gallery_class' => 'tidypics-gallery'

));

Regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment