Check out my visual testing tool https://diffy.website. It helps to save testing time during deployments and development.
In this article I would like to show how to build very nice user experience search page with facets. For this we will use Apache Solr as our search engine and some custom code to make it fully AJAX (with full non-javascript support).
Here is demo screencast of what we want to do.
Link http://www.youtube.com/watch?v=LhKWmWRqeJc
In Drupal 7 there are several ways to integrate Apache Solr to our system. I have chosen to use Search API as it has already built integration with Views and we can create several indexes for our site. This is very effecient in way of performance and scalability.
So lets get started!
Installed modules:
- Chaos tools 7.x-1.0-alpha3
- Page manager 7.x-1.0-alpha3
- Entity API 7.x-1.0-beta7
- Panels 7.x-3.0-alpha3
- Search API 7.x-1.0-beta7 (latest dev version)
- Search facets 7.x-1.0-beta7
- Search views 7.x-1.0-beta7
- Solr search 7.x-1.0-beta7 (http://drupal.org/project/search_api_solr)
- Views 7.x-3.0-alpha1 (latest dev version)
- Views UI 7.x-3.0-alpha1
- Devel 7.x-1.0
- Devel generate 7.x-1.0
There is great documentation about installing Solr search in the INSTALL.txt of the module. In brief you need to download Solr PHP client and put it to module folder (http://code.google.com/p/solr-php-client/downloads/list). Download Apache Solr itself from http://www.apache.org/dyn/closer.cgi/lucene/solr/ (it is Java application and you will need to install Sun Java for it. If you do it on Ubuntu 10 here are good instructions http://www.clickonf5.org/linux/how-install-sun-java-ubuntu-1004-lts/7777). Copy schema.xml and solrconfig.xml files to _where you extracted solr_/example/solr/conf. Then go to folder _where you extracted solr_/example and run java -jar start.jar
. Receive a lot of different letters in output and open in your web browser http://localhost:8983/solr/admin/ and if you see following interface, you have installed apache solr correctly.
In my demo is have extracted solr to /srv/apachesolr folder as you can see on screenshot. Also very important to make sure the version of schema. It should be search-api-1.0.
Next step is to create in Search API server and index. You can view screencast by creator of Search API how to do that http://vimeo.com/15556855. Server should be your local Apache Solr. For index with use node entity.
I have named server "Apache Solr" and index "Apache Solr index".
In index lets have following fields selected to be indexed:
Content type as String
Title as Fulltext
Published as Boolean
I have added Related Entity Author and Main Body so we can select
Author » Name as string
The main body text » Text as Fulltext
On the facets page please enable facet Content type and Author Name.
Now in order to have some test content lets generate it with Devel module. First we generate users (lets do 5 users). Second we generate content (50 nodes both Article and Basic page). After we generated content we should index it. Please go to settings of your index Status tab. Clear index and then Index now.
Next is to create a View (new Views UI was great surprise to me as I have made initially this post with old UI and had to rebuild it with new).
In show selectbox we choose Apache Solr index (or how you named your index). We don't create a page, but create block with table display.
On view settings page:
Fields
Node: Title,
Node: Content type
Author: Name
Filter criteria
Node: Published = 1
Search: Fulltext search -- Exposed filter. Searched fields both TItle and Main body.
Advanced settings
Use AJAX: Yes
Exposed form in block: Yes
So view settings page should look like following screenshot.
Now lets create panel that will be the page with search and facets.
Structure -> Panels. Create new Panel page. For path lets use example. I used in my example simple Two column layout. Everything else is standard.
On the Panel content lets add to left column we add views block we created (Miscellaneous: test_view: Block)
To the right column we add our exposed filter and facets (Miscellaneous: Exposed form: test_view-block_1, Apache Solr index: Author » Name, Apache Solr index: Content type).
Here is screenshot for panels settings.
Now we can go to panel page and see our filter working plus exposed filters. Filter and pager work without page reload but filters don't. For that we need to write some custom code. So lets create custom module.
First we need to add click behavior for facets. As example we take javascript from views module (js/ajax_views.js). If you compare to original example I just removed all unneeded parts and changed selector to find proper links (var view = '.search-api-facets';).
/** * @file example.js * * Handles AJAX facets reactions. * * @see views/js/ajax_views.js */ (function ($) { /** * Attaches the AJAX behavior to Views exposed filter forms and key View links. */ Drupal.behaviors.ExampleFacets = {}; Drupal.behaviors.ExampleFacets.attach = function() { if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) { // Retrieve the path to use for views' ajax. var ajax_path = Drupal.settings.views.ajax_path; $.each(Drupal.settings.views.ajaxViews, function(i, settings) { var view = '.search-api-facets'; var element_settings = { url: ajax_path, submit: settings, setClick: true, event: 'click', selector: view, progress: {type: 'throbber'} }; $(view).filter(':not(.views-processed)') .each(function() { // Set a reference that will work in subsequent calls. var target = this; $(this) .addClass('views-processed') // Process facet links. .find('li > a') .each(function () { var viewData = {}; // Construct an object using the settings defaults and then overriding // with data specific to the link. $.extend( viewData, settings, Drupal.Views.parseQueryString($(this).attr('href')), // Extract argument data from the URL. Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path) ); // For anchor tags, these will go to the target of the anchor rather // than the usual location. $.extend(viewData, Drupal.Views.parseViewArgs($(this).attr('href'), settings.view_base_path)); element_settings.submit = viewData; var ajax = new Drupal.ajax(false, this, element_settings); }); // .each function () { }); // $view.filter().each }); // .each Drupal.settings.views.ajaxViews } // if }; })(jQuery);
After we have enabled javascript event handling on our links we need to rebuild facets HTML on answer from server. We can hook into response on hook_ajax_render_alter with adding our own commands. This can be done with following code in module:
// Path of our panel. define('EXMPLE_PATH', 'example'); /** * Implements hook_ajax_render_alter(). */ function example_ajax_render_alter(&$commands) { if ($_GET['q'] != EXMPLE_PATH) { return; } // Facets that present on the page. $facet_delta = array('apache_solr_index_author_name', 'apache_solr_index_type'); foreach ($facet_delta as $delta) { // Load block for facet. $block_array = search_api_facets_block_view($delta); $delta_class = str_replace('_', '-', $delta); // Render block. $output = drupal_render($block_array); // Show or hide block. if (!empty($output)) { $commands[] = ajax_command_invoke('.pane-search-api-facets-' . $delta_class, 'show'); $commands[] = ajax_command_replace('.pane-search-api-facets-' . $delta_class . ' .pane-content .item-list', $output); } else { $commands[] = ajax_command_invoke('.pane-search-api-facets-' . $delta_class, 'hide'); } } } /** * Implements hook_form_FORM_ID_alter(). */ function example_form_views_exposed_form_alter(&$form, &$form_state) { if ($_GET['q'] != EXMPLE_PATH) { return; } // Add action to form so it will work without javascript. $form['#action'] = EXMPLE_PATH; // Add javascript for facets. drupal_add_js(drupal_get_path('module', 'example') . '/example.js'); }
We add javascript on form alter for exposed filter. Also we change #action property of the form so it can work without javascript.
Here we go. Now our page is completely AJAXified and also can work without javascript enabled.
So if you need to add any other facets you will need to change the code in custom javascript. As we are dealing with Views we are in full control of theming, adding new fields, sorting etc.
I am sure there are a lot of possibilities to improve this code (at least not to have so much settings hardcoded :).
Module code is attached.
Thank you for reading.
Comments
search_api_ajax?
different approach
Facets not showing
facets will be shown only if
Allow multiple filter values to work together
Hi Ken,
Updated hook_ajax_render_alter()
Drupal great solution
Drupal search engine friendly
re: