I’m working on a project that requires me to apply a fancy pants style to a Drupal search form. I thought this would be simple enough, as it’s pretty easy to override default themes for pretty much everything else in Drupal, but it turns out either that I’m a dolt or that there’s not much clarity out there on this topic. After screwing around with a lot of things (e.g. poring over debug_backtrace() output, writing die() statements all over the place, temporarily hacking the search and node modules, etc.), I searched Drupal’s site and found a promising link that turned out not to be the solution I needed (it simply didn’t work).
At long last, I tried creating a theme function named mytheme_search_form(). The search module has a function named search_form(), but in all my hair-pulling, I never saw anything that indicated that you could override this function by prepending your theme name to it (I would have expected to find calls to “theme(‘search_form’, $args)” somewhere). At any rate, I ultimately created the above-named function and gave it the following definition:
function mytheme_search_form($form){ return _phptemplate_callback('search_theme_form', array('form' => $form)); }
Then I created a file in my template directory named “search_theme_form.tpl.php” and built a custom form.
Next up was adding additional search fields to the form. To do this, I looked at node.module, where the function node_form_alter() adds fields to the default form. It didn’t seem necessary to jump through that hoop since my form was mostly hard-coded anyway, so I just added some radio buttons with the name “category” so that I could filter search results by taxonomy. Simple enough. But the search never actually filtered on my results. Here I did more hair-pulling and weird debugging. Finally, I went back to the hook_form_alter() functionality, having noticed a “processed_keys” key in the $form array. So to make my form honor my category search, I added the following things to my template.php file. It’s not clear to me how much of this is necessary, and I rather suspect I’m doing something stupid here, but it seems to work and I’m on deadline, so I’m rolling with it.
function mytheme_search_keys($type = null){ $keys = search_get_keys(); if($type){ $keys = search_query_insert($keys, 'type', $type); } if($_POST['category']){ $keys = search_query_insert($keys, 'category', $_POST['category']); } return $keys; } /** * Taking over this function so that I can call mytheme_search_validate to do the advanced search. */ function blog_form_alter($form_id, &$form){ if($form_id == 'search_form'){ $form['#validate']['mytheme_search_validate'] = array(); } } /** * Need to call this to add category to the processed_keys array item so that * the category actually gets searched in the mini-advanced form we generate * in mytheme_search_form(). */ function mytheme_search_validate($form_id, $form_values, &$form){ $keys = $form_values['processed_keys']; $keys = mytheme_search_keys($form_values['module']); form_set_value($form['basic']['inline']['processed_keys'], $keys); } function faq_search($op = 'search', $keys){ switch($op){ case 'name': return t('content'); default: $keys = mytheme_search_keys('faq'); return node_search('search', $keys); } } function forum_search($op = 'search', $keys){ switch($op){ case 'name': return t('content'); default: $keys = mytheme_search_keys('forum'); return node_search('search', $keys); } } function blog_search($op = 'search', $keys){ switch($op){ case 'name': return t('content'); default: $keys = mytheme_search_keys('blog'); return node_search('search', $keys); } }
Now for an explanation. The “mytheme_search_keys()” function is a helper that lets me make sure I’m limiting the search to a given node type. It’s not clear to me that this is absolutely necessary, but things seem not to work if I don’t add the “type:” string to the search, so I’m leaving it in. Note that this function also looks for $_POST[‘category’] and adds it to the keys. If I wanted to add other search fields, I’d add them here as well. I suppose that since mytheme_search_validate() calls this function to set keys, I could eliminate the extra function and just do the same work in mytheme_search_validate().
Next up, blog_form_alter(). The hook_form_alter() functions are associated with modules, so I chose one I knew my site would be using that didn’t have a form_alter hook defined already. It feels kind of hacky, but it seems to work. The idea here is that we need to make the form run a validation function in order to add the keys we’re pulling in from mytheme_search_keys(). It was when I added this code that the category filter actually started working, so it seems to be a crucial bit. The key seems to be adding the keys to the $form[‘basic’][‘inline’][‘processed_keys’] array item, which seems to handle adding the search criteria to the URL and to the search itself.
Finally, I added the three _search() functions, which again feel a little extraneous, but the thing doesn’t work unless I add them, so they’re staying put for now. All we’re doing in these functions is adding the node type to the keys being searched (the search code extracts things like “type:blog” and “category:3” from the query to do advanced searches) and then executing the node_search() function with the revised $keys value.
So, there you have it, the long way around to having a custom-themed Drupal search form with additional filters based on node type and category. Hope this saves somebody a few of the 10 or so hours I spent staring incredulously at my screen as solution after solution failed to work.