You are here

search_api_sorts.module in Search API sorts 7

Same filename and directory in other branches
  1. 8 search_api_sorts.module

Create sort options for search queries executed via the Search API.

File

search_api_sorts.module
View source
<?php

/**
 * @file
 * Create sort options for search queries executed via the Search API.
 */

/**
 * Implements hook_help().
 */
function search_api_sorts_help($path, array $arg) {
  if ($path == 'admin/config/search/search_api/index/%/sorts') {
    return t('Select the indexed fields for which you want to enable sorting. Before the sort blocks is actually displayed, you will have to enable and configure it at the <a href="!url">block administration page</a>.', array(
      '!url' => url('admin/structure/block'),
    ));
  }
}

/**
 * Implements hook_menu().
 */
function search_api_sorts_menu() {
  $items['admin/config/search/search_api/index/%search_api_index/sorts'] = array(
    'title' => 'Sorts',
    'description' => 'Select the sort fields to display.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'search_api_sorts_index_select',
      5,
    ),
    'access arguments' => array(
      'administer search_api',
    ),
    'weight' => -1,
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
    'file' => 'search_api_sorts.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function search_api_sorts_theme() {
  $themes['search_api_sorts_form_table'] = array(
    'render element' => 'element',
    'file' => 'search_api_sorts.admin.inc',
  );
  $themes['search_api_sorts_list'] = array(
    'variables' => array(
      'items' => array(),
      'options' => array(),
    ),
    'file' => 'search_api_sorts.theme.inc',
  );
  $themes['search_api_sorts_sort'] = array(
    'variables' => array(
      'name' => '',
      'path' => NULL,
      'options' => array(),
      'order_options' => array(),
      'active' => FALSE,
      'default_sort' => FALSE,
    ),
    'file' => 'search_api_sorts.theme.inc',
  );
  return $themes;
}

/**
 * Implements hook_entity_info().
 */
function search_api_sorts_entity_info() {
  $info['search_api_sort'] = array(
    'label' => t('Search sort'),
    'controller class' => 'EntityAPIControllerExportable',
    'entity class' => 'SearchApiSort',
    'base table' => 'search_api_sort',
    'label callback' => 'search_api_sort_label',
    'module' => 'search_api_sorts',
    'exportable' => TRUE,
    'entity keys' => array(
      'id' => 'id',
      'name' => 'identifier',
      'label' => 'name',
    ),
  );
  return $info;
}

/**
 * Implements hook_permission().
 */
function search_api_sorts_permission() {
  return array(
    'use search_api_sorts' => array(
      'title' => t('Use search sorts'),
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function search_api_sorts_block_info() {
  $blocks = array(
    'search-sorts' => array(
      'info' => t('Search sorts'),
      'cache' => DRUPAL_NO_CACHE,
      'weight' => 4,
    ),
  );
  return $blocks;
}

/**
 * Implements hook_ctools_block_info().
 *
 * @see http://drupal.org/node/1763954
 */
function search_api_sorts_ctools_block_info($module, $delta, &$info) {

  // Give the Search API Sorts block it's own category.
  $info['category'] = t('Search API Sorts');

  // Allow blocks to be used before the search results in Panels.
  $info['render last'] = TRUE;
}

/**
 * Implements hook_block_view().
 */
function search_api_sorts_block_view($delta = '') {
  if (!user_access('use search_api_sorts')) {
    return;
  }
  if ($delta == 'search-sorts') {
    return search_api_sorts_block_search_sorts_view();
  }
}

/**
 * Get a list of sorts field names for the current search index id.
 *
 * @return object
 *   a cached query object
 */
function search_api_sorts_search_sorts($index_id, $enabled = 1, $reset = FALSE) {
  $cache =& drupal_static(__FUNCTION__, array());
  if (!isset($cache[$index_id . '_' . $enabled]) || $reset) {
    $query = db_select('search_api_sort', 's')
      ->fields('s', array(
      'field',
      'name',
      'default_sort',
      'default_sort_no_terms',
      'default_order',
      'weight',
    ))
      ->condition('index_id', $index_id)
      ->condition('enabled', $enabled)
      ->orderby('weight', 'ASC')
      ->execute();
    $data = array();
    $index = search_api_index_load($index_id);
    $fields = $index->options['fields'];
    $fields += array(
      'search_api_relevance' => array(
        'type' => 'decimal',
      ),
      'search_api_id' => array(
        'type' => 'integer',
      ),
    );
    while ($row = $query
      ->fetch()) {

      // Check that this field exists in index.
      if (!empty($fields[$row->field])) {
        $data[] = $row;
      }
    }
    $cache[$index_id . '_' . $enabled] = $data;
  }
  return $cache[$index_id . '_' . $enabled];
}

/**
 * View the "Search sorts" block.
 */
function search_api_sorts_block_search_sorts_view() {
  $query = drupal_static('search_api_sorts_search_api_query_alter', array());
  if (!$query) {
    return;
  }
  $search_sorts = search_api_sorts_search_sorts($query
    ->getIndex()->machine_name);
  if (empty($search_sorts)) {
    return;
  }
  $path = $_GET['q'];
  $params = drupal_get_query_parameters($_GET, array(
    'q',
    'page',
  ));

  // Override the path if facetapi_pretty_paths is enabled.
  if (module_exists('facetapi_pretty_paths')) {

    // Get the facet api adapter by the machine readable name of searcher.
    $adapter = facetapi_adapter_load('search_api@' . $query
      ->getIndex()->machine_name);

    // Get the url processor and check if it uses pretty paths.
    $url_processor = $adapter
      ->getUrlProcessor();
    if ($url_processor instanceof FacetapiUrlProcessorPrettyPaths) {

      // Retrieve the full path from the url processor.
      $path = $url_processor
        ->getFullPath();
    }
  }
  if (isset($params['search_api_views_fulltext'])) {
    $default_sort = _search_api_sorts_get_default_sort($search_sorts, $params['search_api_views_fulltext']);
  }
  else {
    $default_sort = _search_api_sorts_get_default_sort($search_sorts);
  }
  $tmp = array_keys($query
    ->getSort());
  if (!($sort = strtolower(reset($tmp)))) {
    $sort = $default_sort->field;
  }
  $tmp = reset($query
    ->getSort());
  if (!($order = strtolower($tmp))) {
    $order = $default_sort->default_order;
  }
  $items = array();
  foreach ($search_sorts as $search_sort) {
    $options = array(
      'query' => array(
        'sort' => $search_sort->field,
        'order' => $search_sort->default_order,
      ) + $params,
      'attributes' => array(
        'class' => array(
          'sort-item',
        ),
      ),
    );

    // Active sort field logic.
    if ($sort == $search_sort->field) {

      // Add some extra classes.
      $options['attributes']['class'] = array_merge($options['attributes']['class'], array(
        'active-sort',
        'sort-' . $order,
      ));

      // Create a copy of the options, for use in the order-link.
      $order_options = $options;

      // With sort order flipped.
      $order_options['query']['order'] = $order == 'asc' ? 'desc' : 'asc';

      // Remove sort and order for the link, because clicking on the active
      // should remove the sort.
      unset($options['query']['sort']);
      unset($options['query']['order']);
      $items[] = array(
        '#theme' => 'search_api_sorts_sort',
        '#name' => t($search_sort->name),
        '#path' => $path,
        '#options' => $options,
        '#order_options' => $order_options,
        '#active' => TRUE,
        '#default_sort' => $default_sort->field,
      );
    }
    else {

      // Regular sort field logic.
      $items[] = array(
        '#theme' => 'search_api_sorts_sort',
        '#name' => t($search_sort->name),
        '#path' => $path,
        '#options' => $options,
        '#active' => FALSE,
        '#default_sort' => $default_sort->field,
      );
    }
  }
  return array(
    'subject' => t('Sort by'),
    'content' => array(
      '#theme' => 'search_api_sorts_list',
      '#items' => $items,
      '#options' => array(
        'attributes' => array(
          'class' => array(
            'search-api-sorts',
          ),
        ),
      ),
    ),
  );
}

/**
 * Entity_label callback.
 */
function search_api_sort_label($sort) {
  static $indices = NULL;
  if (empty($indices)) {
    $indices = search_api_index_load_multiple(FALSE);
  }
  return $indices[$sort->index_id]->name . ' - ' . $sort->name;
}

/**
 * Load multiple sorts at once, determined by IDs, deltas or other conditions.
 *
 * @param array $ids
 *   An array of sort IDs or machine names.
 * @param array $conditions
 *   An array of conditions on the {search_api_sort} table in the form
 *   'field' => $value.
 * @param bool $reset
 *   Whether to reset the internal entity_load cache.
 *
 * @return array
 *   An array of SearchApiSort objects keyed by machine name.
 *
 * @see entity_load()
 */
function search_api_sorts_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
  return entity_load('search_api_sort', $ids, $conditions, $reset);
}

/**
 * Implements hook_search_api_query_alter().
 */
function search_api_sorts_search_api_query_alter(SearchApiQueryInterface $query) {
  if (!user_access('use search_api_sorts')) {
    return;
  }

  // There's already an existing sort, so abort!
  $existing = $query
    ->getSort();
  if (!empty($existing)) {
    return;
  }
  $search_sorts = search_api_sorts_search_sorts($query
    ->getIndex()->machine_name);
  if (empty($search_sorts)) {
    return;
  }
  $default_sort = _search_api_sorts_get_default_sort($search_sorts, $query
    ->getKeys());

  // Alter sort field and sort order.
  $sort = $default_sort->field;
  $params = drupal_get_query_parameters($_GET, array(
    'q',
    'page',
  ));
  if (isset($params['sort']) && !empty($params['sort'])) {
    $sort = $params['sort'];
  }
  $order = $default_sort->default_order;
  if (isset($params['order']) && !empty($params['order'])) {
    $order = $params['order'];
  }
  if (!empty($order) && !empty($sort)) {
    $query
      ->sort($sort, $order);
  }

  // Static save current search query.
  $_query =& drupal_static(__FUNCTION__, array());
  $_query = $query;
}

/**
 * Helper function to get the default sort field.
 */
function _search_api_sorts_get_default_sort($search_sorts, $keys = NULL) {

  // By default use relevance, which will be overridden when defaults are set.
  $default_sort = (object) array(
    'field' => 'search_api_relevance',
    'name' => t('Relevance'),
    'default_order' => 'desc',
    'active' => TRUE,
  );

  // If there are no keys set, return the default for no keys if set.
  if (empty($keys)) {
    foreach ($search_sorts as $sort) {
      if ($sort->default_sort_no_terms) {
        $sort->active = TRUE;
        $default_sort = $sort;
      }
    }
  }
  else {

    // Return the default if set.
    foreach ($search_sorts as $sort) {
      if ($sort->default_sort) {
        $sort->active = TRUE;
        $default_sort = $sort;
      }
    }
  }

  // Allow altering the default sort.
  drupal_alter('search_api_sorts_default_sort', $default_sort, $search_sorts, $keys);
  return $default_sort;
}

Functions

Namesort descending Description
search_api_sorts_block_info Implements hook_block_info().
search_api_sorts_block_search_sorts_view View the "Search sorts" block.
search_api_sorts_block_view Implements hook_block_view().
search_api_sorts_ctools_block_info Implements hook_ctools_block_info().
search_api_sorts_entity_info Implements hook_entity_info().
search_api_sorts_help Implements hook_help().
search_api_sorts_load_multiple Load multiple sorts at once, determined by IDs, deltas or other conditions.
search_api_sorts_menu Implements hook_menu().
search_api_sorts_permission Implements hook_permission().
search_api_sorts_search_api_query_alter Implements hook_search_api_query_alter().
search_api_sorts_search_sorts Get a list of sorts field names for the current search index id.
search_api_sorts_theme Implements hook_theme().
search_api_sort_label Entity_label callback.
_search_api_sorts_get_default_sort Helper function to get the default sort field.