Just spent most of the day tracking down a nasty little issue involving these three modules.
Let's face it Search API with Facet API (and all the Search API support modules) are brilliant, the core Search is very difficult to customise and you usually have to end up with nasty core hacks if you want to do anything clever.
Search API on the other hand is lovely, and Facet API is just great with almost everything extendible. And, of course, you can display search results as a view, which adds that level of delightfulness.
Display Suite is also awesome (I may have already mentioned this).
But if you have all three together - displaying search results and facet blocks on a Display Suite configured page, you may run into the problem of the facet blocks refusing to appear.
The reason is really simple: there are no facets to display.
But you say (well, I screamed in my head) I'm displaying search results through a view, I'm looking at them right now and I know they have facets.
I finally, eventually, lit upon this issue in Facet API http://drupal.org/node/1392288 which explains the problem but doesn't come up with any solid solution. The issue is that if the page displays the blocks before the search query has been run they will be empty because search results are needed before the facets can be calculated.
Simple? Yes. Solvable? Not easily. There's no way of telling Display Suite what order you want the blocks and content displayed (it would be a nice touch but not worth for just this problem, or maybe a way to defer content rendering of blocks to the end). There is talk of having the facet run the query if it's not been run, but that's in the future if it ever happens.
The solution is legal but ugly.
The way to ensure the query is run first is to do it in hook_init() in my case by rendering the view and saving it. Then adding the view to the output when required. Yucky but it works.
UPDATE: Typical really, what I hadn't done is fully explored the DS Extras module which allows Views pages to be configured. This is very handy but the issue above still remains.
Showing posts with label views. Show all posts
Showing posts with label views. Show all posts
Monday, 9 July 2012
Monday, 30 January 2012
Little debugging aid
I had been having a real problem tracking down a PHP error in Drupal which was passing an array to htmlspecialchars() in check_plain() instead of a string. I needed to use debug_backtrace() but the issue was related to Views which meant that the arguments being passed at higher levels were catastrophically huge.
It was a real problem, using krumo just resulted in out-of-memory errors. And the problem was also happening inside AJAX calls so any attempt to simply print the output was doomed.
I needed a way to get a function backtrace which was not too verbose, didn't crash the machine and would work in an AJAX call.
The solution is the following routine which takes a debug_backtrace() extracts only the information we need and outputs to a file. It's not Drupal-specific but you may need to set up the directory:
/**
* Backtrace to a file for when nothing else works
*/
function debug_backtrace_file($fname = 'backtrace') {
$file = fopen("c:\\tmp\\{$fname}.log", 'ab');
if (!$file) {
return;
}
fputs($file, '=============== ' . date('Y-m-d H:i:s') . " ===============\n");
$line = __LINE__;
foreach (debug_backtrace() as $entry) {
$function = $entry['function'];
if ($function!=__FUNCTION__) {
if (isset($entry['class']) && $entry['class']) {
$function = "{$entry['class']}::$function";
}
fputs($file, sprintf("function %s at line %d in file %s\n", $function, $line, isset($entry['file'])?$entry['file']:t('unknown')));
}
$line = $entry['line'];
}
fclose($file);
}
Happy bug hunting.
It was a real problem, using krumo just resulted in out-of-memory errors. And the problem was also happening inside AJAX calls so any attempt to simply print the output was doomed.
I needed a way to get a function backtrace which was not too verbose, didn't crash the machine and would work in an AJAX call.
The solution is the following routine which takes a debug_backtrace() extracts only the information we need and outputs to a file. It's not Drupal-specific but you may need to set up the directory:
/**
* Backtrace to a file for when nothing else works
*/
function debug_backtrace_file($fname = 'backtrace') {
$file = fopen("c:\\tmp\\{$fname}.log", 'ab');
if (!$file) {
return;
}
fputs($file, '=============== ' . date('Y-m-d H:i:s') . " ===============\n");
$line = __LINE__;
foreach (debug_backtrace() as $entry) {
$function = $entry['function'];
if ($function!=__FUNCTION__) {
if (isset($entry['class']) && $entry['class']) {
$function = "{$entry['class']}::$function";
}
fputs($file, sprintf("function %s at line %d in file %s\n", $function, $line, isset($entry['file'])?$entry['file']:t('unknown')));
}
$line = $entry['line'];
}
fclose($file);
}
Happy bug hunting.
Labels:
backtrace,
check_plain,
debug,
debug_backtrace,
PHP,
views
Thursday, 26 January 2012
Where the **** is it?
You know how it is? You have this complex and massive PHP structure with recursive elements and somewhere in all that is a value you're looking for. Views object I'm looking at you!
Here's a PHP routine, which is not Drupal specific, that will dig down into an object to find the property or array item you're looking for - and it avoids recursion by keeping a list of structures it's already searched by MD5ing the serialized version of the structure.
The parameters are the initial structure to search; the property you're looking for - it can be partial if you're not sure what the property is called; $id would usually be the identifier of the structure you're supplying and $depth should be ignored. It returns an array of strings that show the way into the structure to find the property. It's quite common to get multiple results.
One thing to watch out for: If your structure is recursive (like a Views object) and you start the search deeper into the structure with the idea that you'll shorten the search? Well you might, but if it's recursive you might also end up going into one of the recursed objects because the routine hasn't seen it before.
Anyway, with that in mind, here it is:
function common_locate($struct, $seek, $id = '', $depth = 0) {
static $structs = array(), $results = array();
if (is_array($struct) || is_object($struct)) {
$struct = (array) $struct;
foreach ($struct as $key => $value) {
if (strpos($key, $seek)!==FALSE) {
$results[] = "$id => $key = $value";
}
if (is_array($value)) {
$idx = str_replace('0', 'g', md5(serialize($value)));
if (!isset($structs[$idx])) {
$structs[$idx] = "$key:$depth";
common_locate($value, $seek, "{$id}[$key]", $depth+1);
}
}
elseif (is_object($value)) {
$idx = str_replace('0', 'g', md5(serialize($value)));
if (!isset($structs[$idx])) {
$structs[$idx] = "object:$key:$depth";
common_locate($value, $seek, "{$id}->$key", $depth+1);
}
}
}
}
return $results;
}
Here's a PHP routine, which is not Drupal specific, that will dig down into an object to find the property or array item you're looking for - and it avoids recursion by keeping a list of structures it's already searched by MD5ing the serialized version of the structure.
The parameters are the initial structure to search; the property you're looking for - it can be partial if you're not sure what the property is called; $id would usually be the identifier of the structure you're supplying and $depth should be ignored. It returns an array of strings that show the way into the structure to find the property. It's quite common to get multiple results.
One thing to watch out for: If your structure is recursive (like a Views object) and you start the search deeper into the structure with the idea that you'll shorten the search? Well you might, but if it's recursive you might also end up going into one of the recursed objects because the routine hasn't seen it before.
Anyway, with that in mind, here it is:
function common_locate($struct, $seek, $id = '', $depth = 0) {
static $structs = array(), $results = array();
if (is_array($struct) || is_object($struct)) {
$struct = (array) $struct;
foreach ($struct as $key => $value) {
if (strpos($key, $seek)!==FALSE) {
$results[] = "$id => $key = $value";
}
if (is_array($value)) {
$idx = str_replace('0', 'g', md5(serialize($value)));
if (!isset($structs[$idx])) {
$structs[$idx] = "$key:$depth";
common_locate($value, $seek, "{$id}[$key]", $depth+1);
}
}
elseif (is_object($value)) {
$idx = str_replace('0', 'g', md5(serialize($value)));
if (!isset($structs[$idx])) {
$structs[$idx] = "object:$key:$depth";
common_locate($value, $seek, "{$id}->$key", $depth+1);
}
}
}
}
return $results;
}
Tuesday, 8 March 2011
Using Views custom text
So here's the thing: for the current project I had to produce an administrative list of node types, meetings in this case, and alongside have a bunch of links to edit, delete and one for other management stuff.
Views is your friend. Creating a table output of a list of nodes ordered by meeting date is easy-peasy but the challenge here is adding links.
It crossed my mind that I could intercept Views output in half a dozen places, programmatically or by creating replacement templates but that sounds far too much like hard work. I was sure that I'd seen an option to add custom text to a View somewhere, and I knew from a project last year sometime that Views very kindly allows you to use tokens built from fields being displayed in the row it's building.
And so it turns out ... with some caveats:
I had configured the view to output three columns - Name, Date/Time and Location - now I needed a column with the links.
So I added a field, selected "Global" and chose Custom text.
Then added the text: <a href="node/[nid]/edit">Edit</a>
I'll keep it simple and just use one link.
Now this doesn't work, why? Because the tokens can only be for fields that have been used and appear <em>before</em> the field you need them. So I had to add a "Node: Nid" field and put it at the top of the list - remembering to check the "Exclude from display" box because I don't want a column of nids in the table.
And now - I get garbage in the link. The nid numbers are going in okay, but the HTML is all screwed up.
This is the caveat: I was pretty sure, with Drupal 6, that you get the raw output of the field. So if I ask for the nid all I get is the nid (could be wrong on that, I'm not going to check). But it turns out that with Drupal 7 Views what you get in the token is the processed output of the Node: Nid field.
I recommend unchecking every option, but especially uncheck the "Link this field to its node" box, because that's what I was getting, where I have [nid] in the URL it was embedding a complete link.
Having said that, once you take out all the extra options you just get the raw nid. And the link now works.
Views is your friend. Creating a table output of a list of nodes ordered by meeting date is easy-peasy but the challenge here is adding links.
It crossed my mind that I could intercept Views output in half a dozen places, programmatically or by creating replacement templates but that sounds far too much like hard work. I was sure that I'd seen an option to add custom text to a View somewhere, and I knew from a project last year sometime that Views very kindly allows you to use tokens built from fields being displayed in the row it's building.
And so it turns out ... with some caveats:
I had configured the view to output three columns - Name, Date/Time and Location - now I needed a column with the links.
So I added a field, selected "Global" and chose Custom text.
Then added the text: <a href="node/[nid]/edit">Edit</a>
I'll keep it simple and just use one link.
Now this doesn't work, why? Because the tokens can only be for fields that have been used and appear <em>before</em> the field you need them. So I had to add a "Node: Nid" field and put it at the top of the list - remembering to check the "Exclude from display" box because I don't want a column of nids in the table.
And now - I get garbage in the link. The nid numbers are going in okay, but the HTML is all screwed up.
This is the caveat: I was pretty sure, with Drupal 6, that you get the raw output of the field. So if I ask for the nid all I get is the nid (could be wrong on that, I'm not going to check). But it turns out that with Drupal 7 Views what you get in the token is the processed output of the Node: Nid field.
I recommend unchecking every option, but especially uncheck the "Link this field to its node" box, because that's what I was getting, where I have [nid] in the URL it was embedding a complete link.
Having said that, once you take out all the extra options you just get the raw nid. And the link now works.
Subscribe to:
Comments (Atom)