In the previous chapter of this series, we walked you through the process of configuring the Algolia dashboard and your Drupal site to index the site's content on Algolia. In this chapter, you will learn how to display the Algolia search results on your Drupal website.

To display the search result, you will need to create a Drupal custom block and place the block on the /search page. Also, you will find out how to make use of Algolia’s InstantSearch library. Note that this process assumes that you have a basic understanding of how to create custom modules in Drupal, so we will not dive deep into the process of creating custom modules. Read this article to learn how to programmatically create custom modules in Drupal 9.

This is the second chapter of a 2-Part series article. Click here to read the first part where we talk about how to configure Algolia and Drupal.

How to implement Algolia Search in Drupal 9 (Part 2)

Building a Custom Block for the Interface

We will use the following structure to create our custom block.

algolia_search_interface
-   js/
-   	algolia.js
-   src/
-   	Plugin
-   	Block
-   DemoSearchBlock.php
-   templates/
-   algolia_search_interface-block.html.twig
-   algolia_search_interface.info.yml
-   algolia_search_interface.libraries.yml
-   algolia_search_interface.module

Declaring the Libraries

As explained earlier, we will be using Algolia’s InstantSearch library to access search results. So let’s declare all the libraries in algolia_search_interface.libraries.yml as shown below:

algolia-javascript:
  js:
	https://cdn.jsdelivr.net/npm/[email protected]/dist/algoliasearch-lite.umd.js : { type: external }
	https://cdn.jsdelivr.net/npm/ins[email protected] : { type: external }
	js/algolia.js: {preprocess: false}
  dependencies:
	- core/drupalSettings
  css:
	theme:
  	https://cdn.jsdelivr.net/npm/[email protected]/themes/algolia-min.css: { type: external, minified : true }

Accessing and Setting Variables

In the Demo search Block (DemoSearchBlock.php)​​, we will simply access Algolia variables from Drupal configuration and assign them to drupalSettings using the following code:

public function build() {
	// Get Algolia config.
	$config = $this->configFactory->get('search_api.server.algolia_search');
	$app_id = $config->get('backend_config.application_id');
	$api_key = $config->get('backend_config.api_key');
	$index_name = $this->configFactory->get('search_api.index.vehicles_data')->get('options.algolia_index_name');
 
	return [
  	'#theme' => 'demo_search_block',
  	'#attached' => [
    	'library' => 'algolia_search_interface/algolia-javascript',
    	'drupalSettings' => [
      	'app_id' => $app_id,
      	'index_name' => $index_name,
      	'api_key' => $api_key,
    	],
  	],
  	'#cache' => [
    	'contexts' => [
      	'url.path',
      	'url.query_args',
    	],
  	],
	];
  }

Add a Template

Now you will need to add a template for the custom block. So let’s define the hook_theme for our custom block in algolia_search_interface.module:

/**
 * Implements hook_theme().
 */
function algolia_search_interface_theme($existing, $type, $theme, $path) {
  return [
	'demo_search_block' => [],
  ];
}

Define the Containers

Now, let’s define our template data to render the search results. We will simply define the containers where the InstantSearch library will render the results. We will add 4 containers in algolia_search_interface-block.html.twig:

-   Searchbox (#searchbox)
-   Search results (#hits)
-   Facets (#filter-widget)
-   Pagination (#pagination)
<div class="demo-instant-search">
  <div id="searchbox" class="algolia-searchbox"></div>
  <div class="align-left">
	<div class="filter-header">{{ 'Manufacturer'|t }}</div>
	<div id="filter-widget"></div>
  </div>
  <div class="right-col">
	<div id="hits"></div>
  </div>
</div>
<div id="pagination"></div>

Displaying the Search Results

Now you have all the ingredients necessary to display your search results. So let’s have a look at how to use InstantSearch to display our results.

First, let’s get the index name, application id, and application key from the drupalSettings variable. We need them to pass to our Algolia API in algolia.js.

const index_name = drupalSettings.index_name;
const app_id = drupalSettings.app_id;
const api_key = drupalSettings.api_key;

Once you have the application keys and index name in place, you need to initialize and start the InstantSearch.

const search = instantsearch({
  indexName: index_name,
  searchClient: algoliasearch(app_id, api_key),
});
 
search.start();

At this point, you will not see any difference in the search page as you have not added any widget to the InstantSearch. So, let’s add a searchbox widget to the instant search.

search.addWidget(
  instantsearch.widgets.searchBox({
    container: '#searchbox',
  })
);

Notice that we have used addwidget() function of InstantSearch to add a single widget. When using multiple widgets, addwidget() is not feasible. You will need to use it in the following manner:

search.addWidgets([
  instantsearch.widgets.searchBox({
  	container: '#searchbox',
  }),
  instantsearch.widgets.hits({
	container: '#hits'
  }),
]);

Now you will be able to see the search results on your search page. But as we can see, the result is in a very raw format:

Algolia search results - raw format

In order to refine this, let’s format the result using the template attribute as shown below:

 instantsearch.widgets.hits({
	container: '#hits',
	templates: {
	  item: `
  	  <h3 class="title">{{{title.value}}}</h3>
  	  <p class="detail"> {{{field_manufacturer}}} </p>
  	  <p class="uri">{{{uri}}} </p>
	`,
    },
  }),

Note that although the result looks fine, sometimes we need to do some processing on the data before displaying it on the page. For example in the above code, the URI value is in the `public://` format. Here we can use transformItems attribute to alter the results per our requirement.

instantsearch.widgets.hits({
	container: '#hits',
	transformItems(items) {
    	return items.map(item => ({
      	...item,
      	url: item.uri.replace('public://', /sites/default/files/'),
    	}));
  	},
	templates: {
	  item: `
  	  <h3 class="title">{{{title.value}}}</h3>
  	  <p class="detail"> {{{field_manufacturer }}} </p>
  	  <p class="uri">{{{url}}} </p>
	`,
    },
  })

Once the result set is in place you can now move to display facets data to filter our search criteria. You will use the same addWidgets() function to display facets.

search.addWidgets([
  instantsearch.widgets. refinementList({
  	container: '#filter-widget,
  	attribute: 'field_manufacturer',
  }),
]);

The attribute option defines the field name against which we want to display the facet. Remember this also needs to be pre-configured in the Algolia dashboard.

Finally, let’s add pagination to enable more results to be displayed.

search.addWidgets([
  instantsearch.widgets. pagination({
  	container: '#pagination,
  }),
]);

And we’re done! This is how the final code looks like

const index_name = drupalSettings.index_name;
const app_id = drupalSettings.app_id;
const api_key = drupalSettings.api_key;
 
if(index_name && app_id && api_key) {
  const search = instantsearch({
	indexName: index_name,
	searchClient: algoliasearch(app_id, api_key),
  });
  search.addWidgets([
	instantsearch.widgets.searchBox({
  	container: '#searchbox',
	}),
	instantsearch.widgets.hits({
  	container: '#hits',
  	transformItems(items) {
    	return items.map(item => ({
      	...item,
      	url: item.uri.replace('public://', '/sites/default/files/'),
    	}));
  	},
  	templates: {
    	item: `
      	<h3 class="title">{{{_highlightResult.title.value}}}</h3>
      	<p class="detail"> {{{_highlightResult.field_manufacturer.value}}} </p>
      	<p class="uri"><img src="{{{url}}}" /></p>`,
  	},
	}),
	instantsearch.widgets.refinementList({
  	container: '#filter-widget',
  	attribute: 'field_manufacturer',
	}),
	instantsearch.widgets.pagination({
  	container: '#pagination',
	})
  ]);
  search.start();
} else {  throw "Algolia settings missing";}

Final Thoughts

As we conclude our two-part article series, we hope you have gained enough understanding of Algolia. We have covered how you can integrate Algolia search with Drupal to build a powerful and consumer-grade search. We also discussed using the InstantSearch library to customize the search results. Looking for a 100% Drupal-focused company that can help you build ambitious Drupal experiences while utilizing the best of the web? We’d love to hear from you!

Subhash YadavSep 13, 2022