Related Items Block Using Drupal 6 Search

23rd May 2010

A related items block looks at the current content of the page and tries to present the user with a list of items that relate to the current content. Creating a related items block is quite easy, and is a good way of introducing the search module api without having to get to involved in the search module.

Before starting I should point out that there are other related items modules available. These are modules like Related Nodes or Related Items but these modules either aren't released for Drupal 6, or simply don't work in the way I wanted the block to work. I wanted a module that would act with little or no user input.

The module we create will contain 4 files, the .info file, the .module file and two template files. I will go through each file in turn here, but I will also add a download link at the bottom of the post where you can download all the files as a module.

The first thing to do (after creating a folder in the module folder) is create a file called relateditems.info. This file tells Drupal what the name and description of the module are, and also adds a dependency clause that tells Drupal that the search module is required for this function to work.

  1. name = Related Items
  2. description = Provides the functionality needed to create a related items block.
  3. core = 6.x
  4.  
  5. dependencies[] = search

Next, create a file called relateditems.module. This is the main module file that Drupal will look for when running the module. Rather than split up the file into lots of bits I have added lots of comments to the code so that it is clear what is going on. The relateditems_block() function hooks into Drupal's block making code and allows the definition of one or more blocks. The searching is done in the relateditems_query() function, which gets the title and teaser from the current node, extracts the most commonly used keywords and then does a search using them. We only use the title and teaser as these will generally have a higher percentage of good keywords and will cut down on the amount of string processing that PHP has to do.

Here is the relateditems.module file in full.

  1. <?php /**
  2.  * Hook for block. This allows us to define and integrate our block into Drupal.
  3.  *
  4.  * @param string $op Current operation.
  5.  * @param int $delta What delta is being called right now.
  6.  * @param array $edit Edit options for block
  7.  *
  8.  * @return array Information about blocks (list) or the block itself (view).
  9.  */
  10. function relateditems_block($op = 'list', $delta = 0, $edit = array())
  11. {
  12. switch ($op) {
  13. case 'list':
  14. /*
  15.   * The list operation allows us to define our blocks. We only want
  16.   * to define one block here, but you can create more by adding items
  17.   * to the $blocks array.
  18.   */
  19. $blocks[0]['info'] = t('Related Items');
  20. $blocks[0]['cache'] = BLOCK_NO_CACHE;
  21.  
  22. return $blocks;
  23. case 'view':
  24. /*
  25.   * For every block you define the hook_block function will be run
  26.   * with a different $delta being passed for each block.
  27.   * The $delta parameter will correspond to the item in the $blocks
  28.   * array that we defined in the list operation.
  29.   */
  30. if ($delta == 0) {
  31. /*
  32.   * A $delta value of 0 corresponds to the block we created
  33.   * previously. All we do here is get and render our list of
  34.   * related items and add it to the content item in the block
  35.   * array.
  36.   */
  37. $list = relateditems_render();
  38. $block['content'] = $list;
  39. $block['subject'] = t('Related Items');
  40. }
  41. // Always return the block.
  42. return $block;
  43. }
  44. }
  45.  
  46. /**
  47.  * Implementation of hook _theme. This allows us to either define functions or
  48.  * files that will allow us to render the block output in a hookable way.
  49.  *
  50.  * @param array $existing An array of existing implementations that may be used
  51.  * for override purposes. This is primarily useful for
  52.  * themes that may wish to examine existing
  53.  * implementations to extract data (such as arguments)
  54.  * so that it may properly register its own, higher
  55.  * priority implementations.
  56.  * @param string $type What 'type' is being processed. This is primarily
  57.  * useful so that themes tell if they are the actual
  58.  * theme being called or a parent theme.
  59.  * @param string $theme The actual name of theme that is being being checked
  60.  * (mostly only useful for theme engine).
  61.  * @param string $path The directory path of the theme or module, so that it
  62.  * doesn't need to be looked up.
  63.  *
  64.  * @return array A keyed array of theme hooks.
  65.  */
  66. function relateditems_theme($existing, $type, $theme, $path)
  67. {
  68. /*
  69.   * We need to create two theme items. The first is to theme each item and
  70.   * the second is to refine the list wrapper.
  71.   */
  72. return array(
  73. // Define theme for items.
  74. 'relateditems_item' =?> array(
  75. 'arguments' => array(
  76. 'item' => NULL),
  77. 'template' => 'relateditems-item',
  78. ),
  79. // Define theme for list wrapper.
  80. 'relateditems_block' => array(
  81. 'arguments' => array(
  82. 'items' => NULL),
  83. 'template' => 'relateditems-content',
  84. ),
  85. );
  86. }
  87.  
  88.  
  89. /**
  90. * Take a text string and extract the most commonly used keywords. The keywords
  91. * are returned in a format that is compatable with Drupal search. The function
  92. * also contains a list of stop words that can be used to remove junk words.
  93. *
  94. * @param string $content The content that the keywords are to be extracted from.
  95. * @param integer $count The number of keywords to be extracted.
  96. *
  97. * @return string The
  98. */
  99. function relateditems_node_content_keywords($content, $count = 5)
  100. {
  101. // Strip HTML tags from content and convert to lowercase.
  102. $content = strtolower(strip_tags($content));
  103.  
  104. // List of stop words
  105. $stopWords = array('i','a','about','an','are','as','at','be','by','com',
  106. 'for','from','has','how','in','is','it','of','on','or','nbsp','that',
  107. 'the','this','to','was','what','when','where','who','will','with',
  108. 'the','www','were',);
  109.  
  110. // Filter out stop words.
  111. $filterCount = count($stopWords);
  112. for($i=0; $i title . ' ' . $current_node->teaser;
  113. $words = relateditems_node_content_keywords($content);
  114. // Run a search on the keywords.
  115. $searched_nodes = node_search('search', $words);
  116. // Return the nodes found.
  117. return $searched_nodes;
  118. }
  119. }
  120.  
  121. /**
  122. * This function calls relateditems_query() and renders the results as a list.
  123. *
  124. * @return string The rendered output.
  125. */
  126. function relateditems_render()
  127. {
  128. // Find the nodes to render.
  129. $nodes = relateditems_query();
  130.  
  131. $items = array();
  132. $output = '';
  133. if (count($nodes) > 0) {
  134. // If we have more than one node then start rendering.
  135. // Render nodes.
  136. foreach ($nodes as $node) {
  137. $items[] = theme('relateditems_item', $node);
  138. }
  139. // Render overall output.
  140. $output .= theme('relateditems_block', $items);
  141. } else {
  142. // If no items found then return simple message.
  143. $output .= '<p>No Related Items Found</p>';
  144. }
  145. // Return output.
  146. return $output;
  147. }

One thing to note from all this is that I did initially try using a function called do_search(), however in order for this function to work correctly it has to be passed 10 parameters, which can take a bit of working out. I then came across a simpler and more effective way of searching nodes by using the appropriately named node_search() function. The first parameter of the node_search() function is the type of operation we want to perform, in this case we want to search so we use the "search" operation.

Using the node_search() function makes things like filtering possible and quite straightforward. For example, lets say that we wanted to restrict the nodes found to just those of the type "blog", this can be done with the following change to the node_search() function.

$searched_nodes = node_search('search', 'type:blog ' . $words);

We can also use the keyword "category" to restrict the search term to a specific taxonomy term.

Finally, we need to create the two template files that the module uses to theme the output. The first file is called relateditems-content.tpl.php and contains the following:

  1. <?php /**
  2.  * $items = A list of rendered items.
  3.  */
  4. ??>
    1. foreach ($items as $item) {
    2. print $item;
    3. } ?

The final file needed is called relateditems-item.tpl.php and contains the following:

 
  •  print url($item['link']); " title=" print check_plain($item['title']); "> print $item['title']; ?
  • If you want to you can download the entire module right here.

    Comments

    Permalink

    Hi

    I used you given module and it works nicely. thanks for this posting........

    Naren Samant (Mon, 03/21/2011 - 15:11)

    Permalink

    Did you know that there is a very similar module, called the "Similar Entries" module? -> http://drupal.org/project/similar

    It would be nice to know where the differences between these two are.

    Funana (Wed, 06/29/2011 - 19:58)

    Permalink

    How can i  have results of google search as  related items?

    Anonymous (Mon, 10/17/2011 - 13:01)

    Add new comment

    The content of this field is kept private and will not be shown publicly.