You are here

class MerciDefaultController in MERCI (Manage Equipment Reservations, Checkout and Inventory) 7.3

A null implementation of EntityReference_SelectionHandler.

Hierarchy

Expanded class hierarchy of MerciDefaultController

1 string reference to 'MerciDefaultController'
merci_get_controller in merci_core/merci_core.module

File

merci_core/reservation.handler.inc, line 14
Abstraction of the selection logic of an entity reference field.

View source
class MerciDefaultController {
  protected $parent_table, $parent_index, $dates, $date_table, $date_column, $date_column2, $item_table, $item_column, $items, $item_type, $entity, $context;
  protected $conflicting_entities;
  protected $quantity_reserved, $entity_quantity;
  protected $validated = FALSE;
  protected $parent_has_quantity, $parent_has_status, $items_is_list = FALSE;
  protected $errors = NULL;
  public static function getInstance($entity, $item_type, $context) {
    if (class_exists($class_name = 'MerciDefaultController_' . $item_type)) {
      return new $class_name($entity, $item_type, $context);
    }
    else {
      return new MerciDefaultController($entity, $item_type, $context);
    }
  }
  public function __construct($entity, $item_type, $context) {
    $this->context = $context;
    $this->item_type = $item_type;
    $this->entity = $entity;
    $this->items = array();

    // Determine if dealing with multiple values.
    if (substr($entity->{$context['item_field']}
      ->type(), 0, 5) == 'list<') {
      foreach ($entity->{$context['item_field']}
        ->getIterator() as $delta => $item) {
        if ($item
          ->getIdentifier()) {
          $this->items[$delta] = $item;
        }
      }
      $this->items_is_list = TRUE;
    }
    else {
      $this->items[0] = $entity->{$context['item_field']};
    }
    if (substr($entity->{$context['date_field']}
      ->type(), 0, 5) == 'list<') {
      foreach ($entity->{$context['date_field']}
        ->getIterator() as $delta => $item) {
        $this->dates[$delta] = $item
          ->value();
      }
    }
    else {
      $this->dates[0] = $entity->{$context['date_field']}
        ->value();
    }

    // Storage location for date field.
    $date_field_info = field_info_field($context['date_field']);
    $date_storage = $date_field_info['storage']['details']['sql']['FIELD_LOAD_CURRENT'];
    $keys = array_keys($date_storage);
    $this->date_table = reset($keys);
    $this->date_column = $date_storage[$this->date_table]['value'];
    $this->date_column2 = $date_storage[$this->date_table]['value2'];

    // Storage location for item field.
    $item_field_info = field_info_field($context['item_field']);
    $item_storage = $item_field_info['storage']['details']['sql']['FIELD_LOAD_CURRENT'];
    $keys = array_keys($item_storage);
    $this->item_table = reset($keys);
    $keys = array_keys($item_field_info['indexes']);
    $item_key = reset($keys);
    $this->item_column = $item_storage[$this->item_table][$item_key];
    $entity_info = $entity
      ->entityInfo();
    $this->parent_index = $entity
      ->entityKey('id');
    $this->parent_table = $entity_info['base table'];
    $this->parent_has_quantity = in_array('quantity', $entity_info['schema_fields_sql']['base table']);
    $this->parent_has_status = in_array('status', $entity_info['schema_fields_sql']['base table']);
    try {
      $this->entity_quantity = $entity->quantity
        ->value();
    } catch (EntityMetadataWrapperException $e) {
      $this->entity_quantity = 1;
    }
  }
  public function getErrors($delta = NULL, $dates = array()) {

    // Determine if reserving too many of the same item.
    if ($this->errors === NULL) {
      $this
        ->validate();
      $entity = $this->entity;
      $entity_type = $entity
        ->type();
      $context = $this->context;
      $errors = array();
      if ($this->items_is_list) {
        foreach ($entity->{$context['item_field']}
          ->getIterator() as $delta => $resource) {
          $item_id = $resource
            ->getIdentifier();
          if (empty($item_id)) {
            continue;
          }
          if (empty($item_count[$item_id])) {
            $item_count[$item_id] = 0;
          }
          $item_count[$item_id]++;
          try {
            $quantity_reservable = $resource->field_quantity
              ->value();
          } catch (EntityMetadataWrapperException $e) {
            $quantity_reservable = 1;
          }
          if ($item_count[$item_id] > $quantity_reservable) {

            // Selected to many.
            if (!array_key_exists($delta, $errors)) {
              $errors[$delta] = array();
            }
            $parents_path = implode('][', array(
              $context['item_field'],
              'und',
              $delta,
              'target_id',
            ));
            $errors[$delta][MERCI_ERROR_TOO_MANY] = t('%name: You have selected too many of the same item.  We only have %quantity available but you reserved %reserved.', array(
              '%name' => $resource
                ->label(),
              '%quantity' => $quantity_reservable,
              '%reserved' => $item_count[$item_id],
            ));
          }
        }
      }
      else {
        $resource = $entity->{$context['item_field']};
        $item_count[$resource
          ->getIdentifier()] = $this->entity_quantity;
        try {
          $quantity_reservable = $resource->field_quantity
            ->value();
        } catch (EntityMetadataWrapperException $e) {
          $quantity_reservable = 1;
        }
        if ($this->entity_quantity > $quantity_reservable) {
          if (!array_key_exists($delta, $errors)) {
            $errors[$delta] = array();
          }
          $errors[$delta][MERCI_ERROR_TOO_MANY] = t('%name: You have selected too many of the same item.  We only have %quantity available but you reserved %reserved.', array(
            '%name' => $resource
              ->label(),
            '%quantity' => $quantity_reservable,
            '%reserved' => $this->entity_quantity,
          ));

          // Selected to many.
        }
      }
      $reserved = $this
        ->getQuantityReserved();
      $reserved = $reserved ? $reserved : array();
      $reserved_so_far_by_me = array();
      foreach ($reserved as $delta => $start_dates) {
        $conflict_errors = array();

        // Load the resource being reserved.
        if ($this->items_is_list) {
          $resource = $entity->{$context['item_field']}[$delta];
        }
        else {
          $resource = $entity->{$context['item_field']};
        }

        // Determine if the quantity field exists.  If so use it.
        try {
          $quantity_reservable = $resource->field_quantity
            ->value();
        } catch (EntityMetadataWrapperException $e) {
          $quantity_reservable = 1;
        }
        $item_id = $resource
          ->getIdentifier();
        if (empty($reserved_so_far_by_me[$item_id])) {
          $reserved_so_far_by_me[$item_id] = 0;
        }
        $reserved_so_far_by_me[$item_id]++;
        foreach ($this->dates as $dates) {
          $quantity_reserved = $this
            ->getQuantityReserved($delta, $dates);

          // Determine if there are conflicts for this date and item.
          if ($quantity_reservable >= $quantity_reserved + $reserved_so_far_by_me[$item_id]) {
            continue;
          }

          // Load each conflicting entity so we can show information about it to
          // the user.
          $ids = array();
          foreach ($this
            ->getConflicts($delta, $dates) as $conflict) {
            $ids[] = $conflict->parent_id;
          }

          // Load the entities which hold the conflicting item.
          $entities = entity_load($entity_type, $ids);
          $line_items = array();
          foreach ($entities as $id => $line_item) {
            $entity_uri = entity_uri($entity_type, $line_item);
            $entity_label = entity_label($entity_type, $line_item);
            $line_items[] = l(t("@label", array(
              '@label' => $entity_label,
            )), $entity_uri['path']);
          }
          $date_start = $dates['value'];

          // Don't show the date repeat rule in the error message.
          unset($dates['rrule']);
          $render_dates = field_view_value($entity_type, $entity
            ->value(), $context['date_field'], $dates);
          $conflict_errors[$date_start] = t('%name is already reserved by: !items for selected dates !dates', array(
            '%name' => $resource
              ->label(),
            '!items' => implode(', ', $line_items),
            '!dates' => render($render_dates),
          ));
        }
        if ($conflict_errors) {
          if (!array_key_exists($delta, $errors)) {
            $errors[$delta] = array();
          }
          $errors[$delta][MERCI_ERROR_CONFLICT] = $conflict_errors;
        }
      }
      $this->errors = $errors;
    }
    return $this->errors;
  }
  protected function validate() {
    if (!$this->validated) {
      $this->conflicting_entities = $this
        ->conflicts();
      $this->validated = TRUE;
      foreach ($this->conflicting_entities as $delta => $dates) {
        foreach ($dates as $date_start => $conflicts) {
          if (!array_key_exists($delta, $this->quantity_reserved)) {
            $this->quantity_reserved[$delta] = array();
          }
          $this->quantity_reserved[$delta][$date_start] = 1;
        }
      }
    }
  }
  public function getConflicts($delta = NULL, $dates = array()) {
    $this
      ->validate();
    $conflicts = $this->conflicting_entities;
    if ($delta === NULL) {
      return $conflicts;
    }
    if (empty($dates)) {
      return array_key_exists($delta, $conflicts) ? $conflicts[$delta] : FALSE;
    }
    $date_value = $dates['value'];
    return (array_key_exists($delta, $conflicts) and array_key_exists($date_value, $conflicts[$delta])) ? $conflicts[$delta][$date_value] : FALSE;
  }
  public function getQuantityReserved($delta = NULL, $dates = array()) {
    $this
      ->validate();
    $quantity_reserved = $this->quantity_reserved;
    if ($delta === NULL) {
      return $quantity_reserved;
    }
    if (empty($dates)) {
      return array_key_exists($delta, $quantity_reserved) ? $quantity_reserved[$delta] : 0;
    }
    $date_value = $dates['value'];
    return (array_key_exists($delta, $quantity_reserved) and array_key_exists($date_value, $quantity_reserved[$delta])) ? $quantity_reserved[$delta][$date_value] : 0;
  }

  /*
   * Determine if merci_line_item $entity conflicts with any other existing line_items.
   *
   * Returns array of conflicting line items.
   */
  protected function conflicts() {
    $conflicts = array();
    foreach ($this->dates as $dates) {
      $date_value = $dates['value'];
      $query = $this
        ->buildConflictQuery($dates);
      $result = $query
        ->execute();
      foreach ($result as $record) {
        if (!isset($conflicts[$record->item_id])) {
          $conflicts[$record->item_id] = array();
        }
        if (!isset($conflicts[$record->item_id][$date_value])) {
          $conflicts[$record->item_id][$date_value] = array();
        }
        $conflicts[$record->item_id][$date_value][] = $record;
      }
    }
    $return = array();
    foreach ($this->items as $delta => $item) {
      if (isset($conflicts[$item
        ->getIdentifier()])) {
        $return[$delta] = $conflicts[$item
          ->getIdentifier()];
      }
    }
    return $return;
  }
  protected function buildConflictQuery($dates) {
    $context = $this->context;
    $exclude_id = $this->entity
      ->getIdentifier();
    $entity_type = $this->entity
      ->type();
    $item_table = $this->item_table;
    $item_column = $this->item_column;
    $date_table = $this->date_table;
    $date_column = $this->date_column;
    $date_column2 = $this->date_column2;
    $parent_table = $this->parent_table;
    $parent_index = $this->parent_index;
    $items = array();
    foreach ($this->items as $delta => $item) {
      $items[] = $item
        ->getIdentifier();
    }

    // Build the query.
    $query = db_select($item_table, 'item_table');
    $query
      ->addField('item_table', $item_column, 'item_id');
    $query
      ->addField('item_table', 'entity_id', 'parent_id');
    if (count($this->items) == 1) {
      $query
        ->condition($item_column, reset($items));
    }
    else {
      $query
        ->condition($item_column, $items, 'IN');
    }

    // Ignore myself.
    if ($exclude_id) {
      $query
        ->condition('item_table.entity_id', $exclude_id, '!=');
    }
    $query
      ->join($parent_table, 'merci_line_item', 'item_table.entity_id = merci_line_item.' . $parent_index);
    if ($this->parent_has_quantity) {
      $query
        ->addField('merci_line_item', 'quantity', 'quantity');
    }
    else {
      $query
        ->addExpression('1', 'quantity');
    }
    if ($this->parent_has_status) {
      $query
        ->condition('merci_line_item.status', 1, '=');
    }
    $query
      ->join($date_table, 'date_table', 'item_table.entity_id = date_table.entity_id');
    $query
      ->addField('date_table', $date_column);

    //, MERCI_DATE_FIELD_ALIAS);
    $query
      ->addField('date_table', $date_column2);

    //, MERCI_DATE_FIELD_ALIAS2);
    $query
      ->condition('date_table.entity_type', $entity_type, '=');
    $query
      ->condition('date_table.deleted', 0, '=');

    // TODO handled multiple dates.
    $query
      ->condition(db_or()
      ->condition(db_and()
      ->condition($date_column, $dates['value'], '<=')
      ->condition($date_column2, $dates['value'], '>='))
      ->condition(db_and()
      ->condition($date_column, $dates['value2'], '<=')
      ->condition($date_column2, $dates['value2'], '>='))
      ->condition(db_and()
      ->condition($date_column, $dates['value'], '>')
      ->condition($date_column2, $dates['value2'], '<')));
    $query
      ->orderBy($date_column, 'ASC');

    // Add a generic entity access tag to the query.
    $query
      ->addTag('merci_resource');
    $query
      ->addMetaData('merci_reservable_handler', $this);
    return $query;
  }

}

Members