Hi David
I'm trying to integrate Faceted Search with the excellent Context module, by allowing searches to activate conditions. I was wondering if you could please give me a nudge in the right direction.
To integrate the two, I need to provide a keyed array of all options to the Context module. In the array, I need a unique ID as the key to identify each category, and the label of the category as the value. This is so that at a later stage I can find out which categories of which facet are in the current search and enable the right context.
So I could create this unique ID by combining the environment ID, the facet key and the category. Here's the outline of what I'm talking about (this is not real code but just to illustrate):
$options = array(
'3-12-455' => 'Sport', // the "Sport" taxonomy term, a taxonomy_facet.
'3-1-document' => 'Document', // the "Document" content type, a content_type_facet.
);
return $options;
The key '3-12-455' means "Environment ID = 3; Facet key (vocabulary) = 12; Term ID = 455".
The key '3-1-document' means "Environment ID =3; Facet key = 1; Content type = document".
So each category of each facet of each environment has a unique id.
The problem is that I can't seem to find a common interface to retrieve a category's id. I want to just call $category->get_id() or something similar, and a taxonomy_facet_category should return a term id, whereas a content_type_facet_category should return the machine-name of the content type.
Does anything like this exist? Perhaps I'm going about this in the wrong way? Here's an example of how this works with taxonomy: http://drupal.org/node/343110#comment-1533748
Of course, each term id is unique.
And here's what I've got so far:
// $Id$
/**
* @file
* Main module code for context - faceted search integration.
*/
/**
* Implementation of hook_context_conditions().
*/
function context_facets_context_conditions() {
$items = array();
$environments = faceted_search_get_env_ids();
// $all_options stores all facets from the current environment.
$all_options = array();
foreach ($environments as $env_id) {
$env = faceted_search_env_load($env_id);
$env->prepare();
$env->execute();
foreach ($env->get_filters() as $index => $facet) {
if (!$facet->is_browsable()) {
continue; // Not a facet.
}
$categories = $env->load_categories($facet, 0, NULL);
$options = array();
foreach ($categories as $category_index => $category) {
// debugging
$id = $env_id . ':' . $facet->get_id() . ' : ' . $facet->get_text();
$options[$category_index] = $id . ' : ' . $category->get_label(TRUE);
}
$items['facet_' . $env_id . '_' . $index] = array(
'#title' => t('Faceted Search: ' . $env->name . ': ' . $facet->get_label()),
'#description' => t('Set this context when a faceted search with these facets is executed.'),
'#options' => $options,
'#type' => 'checkboxes',
);
}
}
return $items;
}
/**
* Implementation of hook_faceted_search_query_alter().
*/
function context_facets_faceted_search_query_alter($env, $query) {
foreach ($env->get_filters() as $index => $facet) {
if ($facet->is_active()) {
foreach ($facet->get_active_path() as $category_index => $category) {
// debugging
//var_dump($facet->get_label() . $facet->get_key() . $facet->get_id());
//var_dump($facet->get_text());
}
}
}
}
Thanks for any tips!
Mark
| Comment | File | Size | Author |
|---|---|---|---|
| #11 | faceted_search-execute_hook-449722-11.patch | 2.09 KB | Mark Theunissen |
| #5 | Picture 1.png | 58.43 KB | Mark Theunissen |
Comments
Comment #1
Mark Theunissen commentedHere's a newer, working version of the code which supports taxonomy and content type facets:
http://drupal.org/node/450726
There's also a bug in there somewhere that I'm battling to track down.
Comment #2
David Lesieur commentedThe way you currently determine the id (using get_id() and get_text()) is generic because all filters have those methods. You'll probably want to use get_key() as well, because that will distinguish classes of filters. Having to use both get_key() and get_id() to distinguish filters is something that annoys me, but so far I never got to clean this up.
About get_text(): Note that it can contain all sorts of characters, and that its format may vary depending on the facet class. I'm not sure what implications that can have on the Context module, which I have never used, unfortunately... (looks interesting though!)
Comment #3
Mark Theunissen commentedThanks for the response. Maybe I'm missing something, or maybe I wasn't clear enough in my post, but I can't seem to find the generic function you are talking about. For example, let's look at the interfaces for two facet modules, content_type_facet.module and taxonomy_facets.module.
content_type_facet.module
taxonomy_facets.module
taxonomy_facet_categorydefines the variable$_tidandcontent_type_facet_categorydefines the variable$_type, and as far as I can see there is no generic base class method which will return one or the other depending on what type of facet it is.Maybe we could add a base class method and then provide an override in each derived class? Is there another way to do this? Thanks!
Comment #4
David Lesieur commentedWell, the get_key() method I was mentioning is not in the
faceted_search_categoryhierarchy. It comes from thefaceted_search_filterclass, which is the base class for all facet classes.At the moment, the closest thing to what you're looking for is get_text(), but it belongs to the facet classes and can only return the text for the facet's active category. Since the facet classes always know what kind of category class they're dealing with, and since not all facets have the same key:value (search text) format, get_text() is implemented differently for each type of category and no generic accessor has been designed for category classes. I'm pretty sure we could imagine a generic interface at the category level (and this would be more elegant), but in your case that is really needed only if you need an id even for non-active categories. If you need an id only for active categories (i.e. categories used in the current search), then using get_text() as you currently do should be fine (but you'll want to include get_key() in the id as well).
Comment #5
Mark Theunissen commentedYes, that's what I thought: get_text() only deals with the active search.
I think that I do need to get the inactive categories too. Basically, when a user creates a context, they need to set the conditions that trigger the context. I thus need to present a list of all facets and categories in a given environment, (none of which will be active because it's an admin form), so the user can select the ones they want.
Hmm, maybe there is another way to do this. I've looked at the source for the faceted search "guided" block and the admin form and I can't see anything.
Take a look at the screenshot I've attached of the context administration section. In it, I have clicked "Faceted Search: find: Research location" which means the environment is called "find" and the taxonomy vocab is called "Research location". Now I check the boxes on the right, each of which is a category and needs to have a unique id that identifies it's environment, facet and category.
Comment #6
David Lesieur commentedThat would create an awful lot contexts... Wouldn't one context per facet be sufficient in most use cases? The context would be triggered when the facet becomes active.
How do you plan to deal with multiple active facets (or categories)? Which context would then be active? Can more than one context be active at the same time? (sorry, I still haven't tried the Context module :-))
Comment #7
Mark Theunissen commentedMaybe, but not in my use case... ;)
I need a context to become active when a specific set of categories become active. For example, I may want to specify the following conditions:
1) Content type active search must be "document" types
2) Taxonomy active search must be "North America" from vocabulary "Region" and it must also be "Marketing" from "Company division".
So when a user has a faceted search with marketing documents from North America, I want to trigger the context.
Actually, I'm not sure how the context behaviour will handle this. I'm guessing that the behaviour will be undefined, as in, if you set two different contexts to trigger on the same active category, then it will be either the one or the other, but you can't count on anything.
The other problem I'm having is that I feel I'm mis-using the
hook_faceted_search_query_alter(). I'm currently implementing this hook and then using it to determine what the active search is for the current page request, and setting the context in there.Is there a more elegant solution to hook into this? For example, in setting a context based on taxonomy, we use
hook_nodeapi(). In setting a context based on the path, we usehook_init(). But I tried using hook_init and I couldn't get the active search. Any other ideas?Comment #8
Mark Theunissen commentedThe other problem I'm having is a warning:
I suspect this is because I'm not using the API correctly.
Comment #9
David Lesieur commentedI'm not sure that hook_faceted_search_query_alter() is ideal since the search has not really been performed yet when this hook is invoked. I'd be open to invoking a new hook at the end of faceted_search::execute(). Patch welcome!
Regarding the warning: Are you calling faceted_search::execute() somewhere? If so, make sure that ready() is FALSE before calling execute().
Comment #10
Mark Theunissen commentedYes, you're absolutely right. I did some debugging and it seems that in certain circumstances, faceted_search::execute() is being called twice, the second time ready() is TRUE.
The problem is related to the fact that I'm using the hook! Because I am executing a search in the hook, then in the menu callback faceted_search_ui_stage_results() it is being called again, even though ready() == TRUE, because the page handler doesn't check (it assumes that it's not ready).
So a proper hook invoke would solve two problems, in theory. I will write a patch. Thanks for all the help!
Comment #11
Mark Theunissen commentedPatch attached. My problem is almost sorted out, just need to fix a caching issue in Context module.