search.api.php

Hooks provided by the Search module.

File

drupal/core/modules/search/search.api.php
View source
  1. <?php
  2. /**
  3. * @file
  4. * Hooks provided by the Search module.
  5. */
  6. /**
  7. * @addtogroup hooks
  8. * @{
  9. */
  10. /**
  11. * Define a custom search type.
  12. *
  13. * This hook allows a module to tell the Search module that it wishes to
  14. * perform searches on content it defines (custom node types, users, or
  15. * comments for example) when a site search is performed.
  16. *
  17. * In order for the search to do anything, your module must also implement
  18. * hook_search_execute(), which is called when someone requests a search on
  19. * your module's type of content. If you want to have your content indexed
  20. * in the standard search index, your module should also implement
  21. * hook_update_index(). If your search type has settings, you can implement
  22. * hook_search_admin() to add them to the search settings page. You can use
  23. * hook_form_FORM_ID_alter(), with FORM_ID set to 'search_form', to add fields
  24. * to the search form (see node_form_search_form_alter() for an example).
  25. * You can use hook_search_access() to limit access to searching, and
  26. * hook_search_page() to override how search results are displayed.
  27. *
  28. * @return
  29. * Array with optional keys:
  30. * - title: Title for the tab on the search page for this module. Defaults to
  31. * the module name if not given.
  32. * - path: Path component after 'search/' for searching with this module.
  33. * Defaults to the module name if not given.
  34. * - conditions_callback: An implementation of callback_search_conditions().
  35. *
  36. * @ingroup search
  37. */
  38. function hook_search_info() {
  39. return array(
  40. 'title' => 'Content',
  41. 'path' => 'node',
  42. 'conditions_callback' => 'callback_search_conditions',
  43. );
  44. }
  45. /**
  46. * Define access to a custom search routine.
  47. *
  48. * This hook allows a module to define permissions for a search tab.
  49. *
  50. * @ingroup search
  51. */
  52. function hook_search_access() {
  53. return user_access('access content');
  54. }
  55. /**
  56. * Take action when the search index is going to be rebuilt.
  57. *
  58. * Modules that use hook_update_index() should update their indexing
  59. * bookkeeping so that it starts from scratch the next time hook_update_index()
  60. * is called.
  61. *
  62. * @ingroup search
  63. */
  64. function hook_search_reset() {
  65. db_update('search_dataset')
  66. ->fields(array('reindex' => REQUEST_TIME))
  67. ->condition('type', 'node')
  68. ->execute();
  69. }
  70. /**
  71. * Report the status of indexing.
  72. *
  73. * The core search module only invokes this hook on active modules.
  74. * Implementing modules do not need to check whether they are active when
  75. * calculating their return values.
  76. *
  77. * @return
  78. * An associative array with the key-value pairs:
  79. * - remaining: The number of items left to index.
  80. * - total: The total number of items to index.
  81. *
  82. * @ingroup search
  83. */
  84. function hook_search_status() {
  85. $total = db_query('SELECT COUNT(DISTINCT nid) FROM {node_field_data} WHERE status = 1')->fetchField();
  86. $remaining = db_query("SELECT COUNT(DISTINCT nid) FROM {node_field_data} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField();
  87. return array('remaining' => $remaining, 'total' => $total);
  88. }
  89. /**
  90. * Add elements to the search settings form.
  91. *
  92. * @return
  93. * Form array for the Search settings page at admin/config/search/settings.
  94. *
  95. * @ingroup search
  96. */
  97. function hook_search_admin() {
  98. // Output form for defining rank factor weights.
  99. $form['content_ranking'] = array(
  100. '#type' => 'details',
  101. '#title' => t('Content ranking'),
  102. );
  103. $form['content_ranking']['#theme'] = 'node_search_admin';
  104. $form['content_ranking']['#tree'] = TRUE;
  105. $form['content_ranking']['info'] = array(
  106. '#value' => '<em>' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em>'
  107. );
  108. // Note: reversed to reflect that higher number = higher ranking.
  109. $options = drupal_map_assoc(range(0, 10));
  110. $ranks = config('node.settings')->get('search_rank');
  111. foreach (module_invoke_all('ranking') as $var => $values) {
  112. $form['content_ranking']['factors'][$var] = array(
  113. '#title' => $values['title'],
  114. '#type' => 'select',
  115. '#options' => $options,
  116. '#default_value' => isset($ranks[$var]) ? $ranks[$var] : 0,
  117. );
  118. }
  119. $form['#submit'][] = 'node_search_admin_submit';
  120. return $form;
  121. }
  122. /**
  123. * Execute a search for a set of key words.
  124. *
  125. * Use database API with the 'Drupal\Core\Database\Query\PagerSelectExtender'
  126. * query extension to perform your search.
  127. *
  128. * If your module uses hook_update_index() and search_index() to index its
  129. * items, use table 'search_index' aliased to 'i' as the main table in your
  130. * query, with the 'Drupal\search\SearchQuery' extension. You can join to your
  131. * module's table using the 'i.sid' field, which will contain the $sid values
  132. * you provided to search_index(). Add the main keywords to the query by using
  133. * method searchExpression(). The functions search_expression_extract() and
  134. * search_expression_insert() may also be helpful for adding custom search
  135. * parameters to the search expression.
  136. *
  137. * See node_search_execute() for an example of a module that uses the search
  138. * index, and user_search_execute() for an example that doesn't use the search
  139. * index.
  140. *
  141. * @param $keys
  142. * The search keywords as entered by the user. Defaults to NULL.
  143. * @param $conditions
  144. * (optional) An array of additional conditions, such as filters. Defaults to
  145. * NULL.
  146. *
  147. * @return
  148. * An array of search results. To use the default search result display, each
  149. * item should have the following keys':
  150. * - link: (required) The URL of the found item.
  151. * - type: The type of item (such as the content type).
  152. * - title: (required) The name of the item.
  153. * - user: The author of the item.
  154. * - date: A timestamp when the item was last modified.
  155. * - extra: An array of optional extra information items.
  156. * - snippet: An excerpt or preview to show with the result (can be generated
  157. * with search_excerpt()).
  158. * - language: Language code for the item (usually two characters).
  159. *
  160. * @ingroup search
  161. */
  162. function hook_search_execute($keys = NULL, $conditions = NULL) {
  163. // Build matching conditions
  164. $query = db_select('search_index', 'i', array('target' => 'slave'))
  165. ->extend('Drupal\search\SearchQuery')
  166. ->extend('Drupal\Core\Database\Query\PagerSelectExtender');
  167. $query->join('node_field_data', 'n', 'n.nid = i.sid');
  168. $query
  169. ->condition('n.status', 1)
  170. ->addTag('node_access')
  171. ->searchExpression($keys, 'node');
  172. // Insert special keywords.
  173. $query->setOption('type', 'n.type');
  174. $query->setOption('langcode', 'n.langcode');
  175. if ($query->setOption('term', 'ti.tid')) {
  176. $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
  177. }
  178. // Only continue if the first pass query matches.
  179. if (!$query->executeFirstPass()) {
  180. return array();
  181. }
  182. // Add the ranking expressions.
  183. _node_rankings($query);
  184. // Load results.
  185. $find = $query
  186. // Add the language code of the indexed item to the result of the query,
  187. // since the node will be rendered using the respective language.
  188. ->fields('i', array('langcode'))
  189. ->limit(10)
  190. ->execute();
  191. $results = array();
  192. foreach ($find as $item) {
  193. // Render the node.
  194. $node = node_load($item->sid);
  195. $build = node_view($node, 'search_result', $item->langcode);
  196. unset($build['#theme']);
  197. $node->rendered = drupal_render($build);
  198. // Fetch comments for snippet.
  199. $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node, $item->langcode);
  200. $extra = module_invoke_all('node_search_result', $node, $item->langcode);
  201. $language = language_load($item->langcode);
  202. $uri = $node->uri();
  203. $results[] = array(
  204. 'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE, 'language' => $language))),
  205. 'type' => check_plain(node_get_type_label($node)),
  206. 'title' => $node->label($item->langcode),
  207. 'user' => theme('username', array('account' => $node)),
  208. 'date' => $node->changed,
  209. 'node' => $node,
  210. 'extra' => $extra,
  211. 'score' => $item->calculated_score,
  212. 'snippet' => search_excerpt($keys, $node->rendered, $item->langcode),
  213. 'langcode' => $node->langcode,
  214. );
  215. }
  216. return $results;
  217. }
  218. /**
  219. * Override the rendering of search results.
  220. *
  221. * A module that implements hook_search_info() to define a type of search may
  222. * implement this hook in order to override the default theming of its search
  223. * results, which is otherwise themed using theme('search_results').
  224. *
  225. * Note that by default, theme('search_results') and theme('search_result')
  226. * work together to create an ordered list (OL). So your hook_search_page()
  227. * implementation should probably do this as well.
  228. *
  229. * @param $results
  230. * An array of search results.
  231. *
  232. * @return
  233. * A renderable array, which will render the formatted search results with a
  234. * pager included.
  235. *
  236. * @see search-result.tpl.php
  237. * @see search-results.tpl.php
  238. */
  239. function hook_search_page($results) {
  240. $output['prefix']['#markup'] = '<ol class="search-results">';
  241. foreach ($results as $entry) {
  242. $output[] = array(
  243. '#theme' => 'search_result',
  244. '#result' => $entry,
  245. '#module' => 'my_module_name',
  246. );
  247. }
  248. $output['suffix']['#markup'] = '</ol>' . theme('pager');
  249. return $output;
  250. }
  251. /**
  252. * Preprocess text for search.
  253. *
  254. * This hook is called to preprocess both the text added to the search index
  255. * and the keywords users have submitted for searching.
  256. *
  257. * Possible uses:
  258. * - Adding spaces between words of Chinese or Japanese text.
  259. * - Stemming words down to their root words to allow matches between, for
  260. * instance, walk, walked, walking, and walks in searching.
  261. * - Expanding abbreviations and acronymns that occur in text.
  262. *
  263. * @param $text
  264. * The text to preprocess. This is a single piece of plain text extracted
  265. * from between two HTML tags or from the search query. It will not contain
  266. * any HTML entities or HTML tags.
  267. *
  268. * @param $langcode
  269. * The language code of the entity that has been found.
  270. *
  271. * @return
  272. * The text after preprocessing. Note that if your module decides not to
  273. * alter the text, it should return the original text. Also, after
  274. * preprocessing, words in the text should be separated by a space.
  275. *
  276. * @ingroup search
  277. */
  278. function hook_search_preprocess($text, $langcode = NULL) {
  279. // If the langcode is set to 'en' then add variations of the word "testing"
  280. // which can also be found during English language searches.
  281. if (isset($langcode) && $langcode == 'en') {
  282. // Add the alternate verb forms for the word "testing".
  283. if ($text == 'we are testing') {
  284. $text .= ' test tested';
  285. }
  286. }
  287. return $text;
  288. }
  289. /**
  290. * Update the search index for this module.
  291. *
  292. * This hook is called every cron run if the Search module is enabled, your
  293. * module has implemented hook_search_info(), and your module has been set as
  294. * an active search module on the Search settings page
  295. * (admin/config/search/settings). It allows your module to add items to the
  296. * built-in search index using search_index(), or to add them to your module's
  297. * own indexing mechanism.
  298. *
  299. * When implementing this hook, your module should index content items that
  300. * were modified or added since the last run. PHP has a time limit
  301. * for cron, though, so it is advisable to limit how many items you index
  302. * per run using config('search.settings')->get('index.cron_limit') (see
  303. * example below). Also, since the cron run could time out and abort in the
  304. * middle of your run, you should update your module's internal bookkeeping on
  305. * when items have last been indexed as you go rather than waiting to the end
  306. * of indexing.
  307. *
  308. * @ingroup search
  309. */
  310. function hook_update_index() {
  311. $limit = (int) config('search.settings')->get('index.cron_limit');
  312. $result = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, n.nid ASC", 0, $limit);
  313. foreach ($result as $node) {
  314. $node = node_load($node->nid);
  315. // Save the changed time of the most recent indexed node, for the search
  316. // results half-life calculation.
  317. \Drupal::state()->set('node.cron_last', $node->changed);
  318. // Render the node.
  319. $build = node_view($node, 'search_index');
  320. $node->rendered = drupal_render($node->content);
  321. $text = '<h1>' . check_plain($node->label()) . '</h1>' . $node->rendered;
  322. // Fetch extra data normally not visible
  323. $extra = module_invoke_all('node_update_index', $node);
  324. foreach ($extra as $t) {
  325. $text .= $t;
  326. }
  327. // Update index
  328. search_index($node->nid, 'node', $text);
  329. }
  330. }
  331. /**
  332. * @} End of "addtogroup hooks".
  333. */
  334. /**
  335. * Provide search query conditions.
  336. *
  337. * Callback for hook_search_info().
  338. *
  339. * This callback is invoked by search_view() to get an array of additional
  340. * search conditions to pass to search_data(). For example, a search module
  341. * may get additional keywords, filters, or modifiers for the search from
  342. * the query string.
  343. *
  344. * This example pulls additional search keywords out of the $_REQUEST variable,
  345. * (i.e. from the query string of the request). The conditions may also be
  346. * generated internally - for example based on a module's settings.
  347. *
  348. * @param $keys
  349. * The search keywords string.
  350. *
  351. * @return
  352. * An array of additional conditions, such as filters.
  353. *
  354. * @ingroup callbacks
  355. * @ingroup search
  356. */
  357. function callback_search_conditions($keys) {
  358. $conditions = array();
  359. if (!empty($_REQUEST['keys'])) {
  360. $conditions['keys'] = $_REQUEST['keys'];
  361. }
  362. if (!empty($_REQUEST['sample_search_keys'])) {
  363. $conditions['sample_search_keys'] = $_REQUEST['sample_search_keys'];
  364. }
  365. if ($force_keys = config('sample_search.settings')->get('force_keywords')) {
  366. $conditions['sample_search_force_keywords'] = $force_keys;
  367. }
  368. return $conditions;
  369. }

Functions

Namesort descending Description
callback_search_conditions Provide search query conditions.
hook_search_access Define access to a custom search routine.
hook_search_admin Add elements to the search settings form.
hook_search_execute Execute a search for a set of key words.
hook_search_info Define a custom search type.
hook_search_page Override the rendering of search results.
hook_search_preprocess Preprocess text for search.
hook_search_reset Take action when the search index is going to be rebuilt.
hook_search_status Report the status of indexing.
hook_update_index Update the search index for this module.