You are here

commerce_search_api.module in Commerce Search API 7

Provides Search API integration for Drupal Commerce.

File

commerce_search_api.module
View source
<?php

/**
 * @file
 * Provides Search API integration for Drupal Commerce.
 */
define('COMMERCE_SEARCH_API_INDEX', 'product_display');

/**
 * Implements hook_ctools_plugin_api().
 */
function commerce_search_api_ctools_plugin_api($module, $api) {
  if ($module == "facetapi" && $api == "facetapi_defaults") {
    return array(
      "version" => 1,
    );
  }
}

/**
 * Implements hook_menu().
 */
function commerce_search_api_menu() {
  $items['admin/commerce/config/commerce_search_api'] = array(
    'title' => 'Commerce Search API',
    'description' => 'Configure the generated product display index.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'commerce_search_api_admin_settings_form',
    ),
    'access arguments' => array(
      'configure store',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'includes/commerce_search_api.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_default_search_api_index().
 *
 * Create an automatic product display index.
 */
function commerce_search_api_default_search_api_index() {

  // Don't generate a default Search Index if unnecessary.
  if (!variable_get('commerce_search_api_provide_default_index', TRUE)) {
    return;
  }
  module_load_include('inc', 'commerce_search_api', 'includes/commerce_search_api.callbacks');
  $server = search_api_server_load('frontend');
  if (empty($server)) {
    $servers = search_api_server_load_multiple(FALSE);
    if (empty($servers)) {
      return;
    }
    $server = reset($servers);
  }
  if ($index = commerce_search_api_generate_product_display_index($server, COMMERCE_SEARCH_API_INDEX)) {
    $items[COMMERCE_SEARCH_API_INDEX] = $index;
    return $items;
  }
}

/**
 * Implements hook_entity_property_info_alter().
 */
function commerce_search_api_entity_property_info_alter(&$info) {

  // Retrieve the product display from the product.
  $properties =& $info['commerce_product']['properties'];
  $product_reference_fields = commerce_info_fields('commerce_product_reference', 'node');
  foreach ($product_reference_fields as $field_name => $field) {
    foreach ($field['bundles'] as $entity_type => $bundles) {
      $properties[$field_name . '_' . $entity_type] = array(
        'description' => t('A bridge to @entity_type referenced by @field_name', array(
          '@entity_type' => $entity_type,
          '@field_name' => $field_name,
        )),
        'getter callback' => 'commerce_search_api_backreference_getter',
        'field' => $field,
        'label' => t('A bridge to @entity_type referenced by @field_name', array(
          '@entity_type' => $entity_type,
          '@field_name' => $field_name,
        )),
        'target type' => $entity_type,
        'type' => 'list<' . $entity_type . '>',
      );
    }
  }
}

/**
 * Implements hook_search_api_alter_callback_info().
 */
function commerce_search_api_search_api_alter_callback_info() {
  $callbacks['commerce_search_api_product_display_filter'] = array(
    'name' => t('Product Display filter'),
    'description' => t("Exclude nodes that aren't product displays."),
    'class' => 'CommerceSearchApiProductDisplayFilter',
    // Filters should be executed first.
    'weight' => -10,
  );
  $callbacks['commerce_search_api_alter_product_status'] = array(
    'name' => t('Exclude unpublished products'),
    'description' => t("Exclude unpublished products."),
    'class' => 'CommerceSearchApiAlterProductStatus',
    // Filters should be executed first.
    'weight' => -9,
  );
  return $callbacks;
}

/**
 * Getter callback for retrieving related entities.
 */
function commerce_search_api_backreference_getter($product, array $options, $name, $type, $info) {
  $field = $info['field'];
  $target_type = $info['target type'];
  return commerce_search_api_get_related_entities($product, $field['field_name'], $target_type);
}

/**
 * Helper function used to retrieve the entities that reference a product.
 */
function commerce_search_api_get_related_entities($product, $field_name, $target_type) {
  $entities_ids = array();
  $query = new EntityFieldQuery();
  $query
    ->entityCondition('entity_type', $target_type, '=')
    ->fieldCondition($field_name, 'product_id', $product->product_id, '=');
  $result = $query
    ->execute();
  if (isset($result[$target_type])) {
    $entities_ids = array_keys($result[$target_type]);
  }
  return $entities_ids;
}

/**
 * Implements hook_facetapi_filters().
 */
function commerce_search_api_facetapi_filters() {
  $filters = array(
    'useless_searches' => array(
      'handler' => array(
        'label' => t('Hide items that do not change search result.'),
        'class' => 'CommerceSearchApiSameSearch',
      ),
    ),
    'hide_search_start' => array(
      'handler' => array(
        'label' => t('Hide if no search is performed.'),
        'class' => 'CommerceSearchApiSearchStart',
      ),
    ),
  );
  return $filters;
}

/**
 * Implements hook_facetapi_widgets().
 */
function commerce_search_api_facetapi_widgets() {
  return array(
    'commerce_search_api_fancy' => array(
      'handler' => array(
        'label' => t('Fancy attributes'),
        'class' => 'CommerceSearchApiFancy',
        'query types' => array(
          'term',
        ),
      ),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function commerce_search_api_theme() {
  return array(
    'commerce_search_api_fancy_attributes_color' => array(
      'variables' => array(
        'title' => '',
        'hex' => NULL,
      ),
    ),
  );
}

/**
 * Return a <span> tag representing the passed-in color.
 *
 * @param $variables
 *   Variables available for this theming function:
 *     - hex: Hex representation of the color to display.
 *     - title: The facet count.
 * @return string
 */
function theme_commerce_search_api_fancy_attributes_color($variables) {

  // Make sure the value is prefixed by #.
  if (!substr($variables['hex'], 0, 1) == '#') {
    $variables['hex'] = '#' . $variables['hex'];
  }
  $return = '<span style="background-color: ' . $variables['hex'] . ';" class="commerce-search-api-fancy-attributes-color">';
  if (!empty($variables['title'])) {
    $return .= '<span>' . $variables['title'] . '</span>';
  }
  $return .= '</span>';
  return $return;
}

/**
 * Implements hook_entity_insert().
 */
function commerce_search_api_entity_insert($entity, $type) {
  commerce_search_api_track_item_change($entity, $type);
}

/**
 * Implements hook_entity_insert().
 */
function commerce_search_api_entity_update($entity, $type) {
  commerce_search_api_track_item_change($entity, $type);
}

/**
 * Reindex the product display when a product variation is updated.
 *
 * @param $entity
 *   The entity that is being inserted or updated.
 *
 * @param $type
 *   The type of entity.
 */
function commerce_search_api_track_item_change($entity, $type) {
  if ($type != 'commerce_product') {
    return;
  }
  $product_reference_fields = commerce_info_fields('commerce_product_reference', 'node');
  $properties = array();
  foreach ($product_reference_fields as $field_name => $field) {
    foreach ($field['bundles'] as $entity_type => $bundles) {
      if ($entity_type != 'node') {
        continue;
      }
      $properties[] = $field_name . '_' . $entity_type;
    }
  }

  // Retrieve all the product displays that reference this product.
  if ($properties) {
    $product_wrapper = entity_metadata_wrapper($type, $entity);
    $item_ids = array();
    $item_ids_multiple = array();

    // Gather the product displays nids from the properties we expose.
    foreach ($properties as $property_name) {
      foreach ($product_wrapper->{$property_name}
        ->raw() as $nid) {
        $item_ids[] = $nid;
        $item_ids_multiple[] = 'node/' . $nid;
      }
    }
    search_api_track_item_change('node', $item_ids);
    search_api_track_item_change('multiple', $item_ids_multiple);
  }
}