Created
October 27, 2013 03:05
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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?
));
Regards.