You are here

bat_facets.module in Booking and Availability Management Tools for Drupal 7

Same filename and directory in other branches
  1. 8 modules/bat_facets/bat_facets.module

File

modules/bat_facets/bat_facets.module
View source
<?php

/**
 * @file
 */
use Roomify\Bat\Calendar\Calendar;
use Roomify\Bat\Event\Event;
use Roomify\Bat\Store\DrupalDBStore;
use Roomify\Bat\Unit\Unit;

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

/**
 * Implements hook_facetapi_facet_info_alter().
 */
function bat_facets_facetapi_facet_info_alter(array &$facet_info, array $searcher_info) {
  switch ($searcher_info['adapter']) {
    case 'search_api':
      foreach ($facet_info as $name => $info) {
        if ($name == 'type_id') {
          $facet_info[$name]['query types'][] = 'bat_state';
        }
      }
      break;
  }
}

/**
 * Callback for facet availability form.
 */
function bat_facets_availability($form, &$form_state, $elements) {
  $params = drupal_get_query_parameters();
  $now = date_create();

  // Year defaults to current year, although we are not filtering yet.
  $default_year = $now
    ->format('Y');

  // Month doesn't have a default selection.
  $default_month = '';
  $form['container'] = array(
    '#type' => 'container',
    '#attributes' => array(
      'class' => array(
        'container-inline',
      ),
    ),
  );
  if (isset($params['bat_start_date']) && !empty($params['bat_start_date'])) {
    $start_date = new DateTime($params['bat_start_date']);
    $arrival = $start_date
      ->format('Y-m-d H:i');
  }
  if (isset($params['bat_end_date']) && !empty($params['bat_end_date'])) {
    $end_date = new DateTime($params['bat_end_date']);
    $departure = $end_date
      ->format('Y-m-d H:i');
  }

  // Create unique ids and selectors for each picker.
  $start_date_id = drupal_html_id('datepicker-start-date');
  $start_date_selector = '#' . $start_date_id . ' .form-text';
  $end_date_id = drupal_html_id('datepicker-end-date');
  $end_date_selector = '#' . $start_date_id . ' .form-text';

  // Specify the default datepicker parameters (see date_popup_element_info())
  $datepicker_options = array(
    // Limit bookings to X days in advance, depending on the
    // chosen configuration in your BAT installation, defaults
    // to the current day.
    'minDate' => '+' . variable_get('bat_event_start_date', 0) . 'd',
  );
  $form['container']['arrival'] = array(
    '#type' => 'date_popup',
    '#description' => '',
    '#date_format' => variable_get('bat_date_format', 'Y-m-d H:i'),
    '#default_value' => isset($arrival) ? $arrival : '',
    '#datepicker_options' => array_merge($datepicker_options, array(
      'endDateSelector' => $end_date_selector,
    )),
    '#required' => TRUE,
  );
  $form['container']['departure'] = array(
    '#type' => 'date_popup',
    '#description' => '',
    '#date_format' => variable_get('bat_date_format', 'Y-m-d H:i'),
    '#default_value' => isset($departure) ? $departure : '',
    '#datepicker_options' => array_merge($datepicker_options, array(
      'startDateSelector' => $start_date_selector,
    )),
    '#required' => TRUE,
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'bat') . '/js/bat_date_popup.js',
        array(
          'data' => array(
            'bat' => array(
              'batBookingStartDay' => variable_get('bat_event_start_date', 0),
              'batDateFormat' => 'dd/mm/yy',
              // Here we create a listing of all datepickers registered on the
              // current page. This is available for use in your own custom
              // jQuery scripts as Drupal.settings.bat.datepickers.
              'datepickers' => array(
                $start_date_selector => array(
                  'endDateSelector' => $end_date_selector,
                ),
              ),
            ),
          ),
          'type' => 'setting',
        ),
      ),
    ),
  );
  $form['container']['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Search',
  );
  return $form;
}

/**
 * Submit handler.
 */
function bat_facets_availability_submit($form, &$form_state) {
  $form_state['redirect'] = array(
    $_GET['q'],
    array(
      'query' => array(
        'bat_start_date' => $form_state['values']['arrival'],
        'bat_end_date' => $form_state['values']['departure'],
      ),
    ),
  );
}

/**
 * Implements hook_search_api_query_alter().
 *
 * @param SearchApiQueryInterface $query
 *   The search query being executed.
 */
function bat_facets_search_api_query_alter($query) {
  if ($query
    ->getIndex()
    ->getEntityType()) {
    $info = entity_get_info($query
      ->getIndex()
      ->getEntityType());

    // Only modify the query if this query is against bat units.
    if ($info['base table'] == 'bat_types') {

      // Get this widget's settings.
      $index = $query
        ->getIndex();
      $facets = facetapi_get_enabled_facets('search_api@' . $index->machine_name);
      $adapter = facetapi_adapter_load('search_api@' . $index->machine_name);
      $realm = facetapi_realm_load('block');
      if (isset($facets['type_id'])) {
        $settings = $adapter
          ->getFacetSettings($facets['type_id'], $realm);

        // Get URL parameters.
        $params = drupal_get_query_parameters();

        // See if we have dates to search.
        if (isset($params['bat_start_date']) && !empty($params['bat_start_date']) && isset($params['bat_end_date']) && !empty($params['bat_end_date'])) {
          $start_date = new DateTime($params['bat_start_date']);
          $end_date = new DateTime($params['bat_end_date']);

          // Remove a minute because BAT considers as included.
          $end_date
            ->sub(new DateInterval('PT1M'));
          $event_type = $settings->settings['event_type'];
          $state_store = new DrupalDBStore($event_type, DrupalDBStore::BAT_STATE, bat_get_db_prefix());
          $valid_states = $settings->settings['states'];
          $valid_type_ids = array();

          // We need to narrow the query to all Types with Units that have the
          // requested state over the requested dates.
          // First, we retrieve all types.
          // TODO: narrow this to only the types already being searched for.
          $type_ids = bat_type_ids();
          $units = array();
          foreach ($type_ids as $type_id => $name) {

            // Get the units of this type.
            $drupal_units = bat_unit_load_multiple(FALSE, array(
              'type_id' => $type_id,
            ));
            $bat_units = array();
            foreach ($drupal_units as $unit_id => $unit) {
              $bat_units[] = new Unit($unit_id, $unit
                ->getEventDefaultValue($event_type));
            }

            // If this type has associated units, see if any of its units are
            // in the states being searched for over the search period.
            if (count($bat_units)) {
              $calendar = new Calendar($bat_units, $state_store);
              $constraints = array();
              foreach (bat_event_constraints_get_info() as $constraint) {
                $constraints[] = $constraint['constraint'];
              }
              $response = $calendar
                ->getMatchingUnits($start_date, $end_date, $valid_states, $constraints, TRUE);
              $valid_unit_ids = array_keys($response
                ->getIncluded());

              // If there are available units, mark this type as valid.
              if (count($valid_unit_ids)) {
                $valid_type_ids[] = $type_id;
              }
            }
          }
          $context = array(
            'types_before_search' => $type_ids,
            'start_date' => $start_date,
            'end_date' => $end_date,
            'event_type' => $event_type,
            'valid_states' => $valid_states,
          );
          drupal_alter('bat_facets_search_results', $valid_type_ids, $context);

          // If no types are available, zero out results.
          if (empty($valid_type_ids)) {
            $query
              ->condition($info['entity keys']['id'], 1, '<');
          }
          else {

            // Limit the search API query to entity ids with availability.
            $query
              ->condition($info['entity keys']['id'], $valid_type_ids, 'IN');
          }
        }
      }
    }
  }
}