You are here

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

A null implementation of EntityReference_SelectionHandler.

Hierarchy

Expanded class hierarchy of MerciDefaultController_non_inventory

File

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

View source
class MerciDefaultController_non_inventory extends MerciDefaultController {
  protected $date_column, $date_column2;
  protected $buckets;
  protected function validate() {
    if (!$this->validated) {
      $this->buckets = $this
        ->fillBuckets();
      $this->validated = TRUE;
      $conflicts = array();
      foreach ($this->buckets as $delta => $dates) {
        foreach ($dates as $date_value => $buckets) {
          if (!isset($this->quantity_reserved[$delta])) {
            $this->quantity_reserved[$delta] = array();
          }
          $this->quantity_reserved[$delta][$date_value] = count($buckets);
          if (!isset($conflicts[$delta])) {
            $conflicts[$delta] = array();
          }
          $conflicts[$delta][$date_value] = array();
          foreach ($buckets as $bucket) {
            $conflicts[$delta][$date_value] = array_merge($conflicts[$delta][$date_value], $bucket);
          }
        }
      }
      $this->conflicting_entities = $conflicts;
    }
  }
  public function fillBuckets() {
    $conflicts = array();
    foreach ($this->dates as $dates) {
      $date_value = $dates['value'];
      $result = $this
        ->bestFit($dates);

      // Result is array indexed by $delta of filled buckets.
      foreach ($result as $delta => $buckets) {
        if (!isset($conflicts[$delta])) {
          $conflicts[$delta] = array();
        }
        $conflicts[$delta][$date_value] = $buckets;
      }
    }
    return $conflicts;
  }
  public function reservations($dates, $exclude_id) {
    $bestfit = $this
      ->bestFit($dates);
    $reservations = array();
    foreach ($bestfit as $enity_id => $reservation) {
      $reservations[] = $entity_id;
    }
    return $reservations;
  }

  /*
   * Perform first-fit algorhtym on reservations into buckets.
   *
   * Return array indexed by item delta of array of filled buckets.
   */
  public function bestFit($dates) {
    $entity = $this->entity;
    $context = $this->context;
    $best_fit = array();
    $parent_conflicts = parent::conflicts($dates);
    $date_value = $dates['value'];
    foreach ($this->items as $delta => $item) {

      // No need to sort into buckets if there is nothing to sort into buckets.
      if (!array_key_exists($delta, $parent_conflicts) or !array_key_exists($date_value, $parent_conflicts[$delta])) {
        continue;
      }

      // Determine if the quantity field exists.  If so use it.
      try {
        $quantity = $item->{$context['quantity_field']}
          ->value();
      } catch (EntityMetadataWrapperException $e) {
        $quantity = 1;
      }

      // Split reservations based on quantity.
      $reservations = array();
      foreach ($parent_conflicts[$delta][$date_value] as $reservation) {
        for ($i = 0; $i < $reservation->quantity; $i++) {
          $reservations[] = $reservation;
        }
      }

      // Determine how many bucket items are needed for this time period.
      // Need to sort like this:
      //            .... time ....
      // item1  x x a a a x x x x x f x e e e x x x x x
      // item2  x x x d d d d d d x x x x c c c x x x x
      // item3  x x b b b b b b b b b b b b b x x x x x
      // etc ......
      //
      //      // Order by lenght of reservation descending.
      //      // Do first-fit algorythm.
      // Sort by length of reservation.
      uasort($reservations, array(
        $this,
        "merci_bucket_cmp_length",
      ));
      $buckets = array();

      // First-fit algorythm.
      foreach ($reservations as $test_reservation) {

        // Go through each bucket item to look for a available slot for this reservation.
        //
        // Find a bucket to use for this reservation.
        for ($i = 0; $i < $quantity; $i++) {
          $fits = TRUE;

          // Bucket already has other reservations we need to check against for a fit.
          if (array_key_exists($i, $buckets)) {
            foreach ($buckets[$i] as $reservation) {
              if ($this
                ->merci_bucket_intersects($reservation, $test_reservation)) {

                //Conflict so skip saving the reservation to this slot and try to use the next bucket item.
                $fits = FALSE;
                break;
              }
            }
          }

          // We've found a slot so test the next reservation.
          if ($fits) {
            if (array_key_exists($i, $buckets)) {
              $buckets[$i] = array();
            }
            $buckets[$i][] = $test_reservation;
            break;
          }
        }
      }
      if (count($buckets)) {
        $best_fit[$delta] = $buckets;
      }
    }
    return $best_fit;
  }

  /*
   * |----------------------|        range 1
   * |--->                           range 2 overlap
   *  |--->                          range 2 overlap
   *                        |--->    range 2 overlap
   *                         |--->   range 2 no overlap
   */
  private function merci_bucket_intersects($r1, $r2) {
    $value = $this->date_column;
    $value2 = $this->date_column2;

    /*
     * Make sure r1 start date is before r2 start date.
     */
    if (date_create($r1->{$value}) > date_create($r2->{$value})) {
      $temp = $r1;
      $r1 = $r2;
      $r2 = $temp;
    }
    if (date_create($r2->{$value}) <= date_create($r1->{$value2})) {
      return true;
    }
    return false;
  }
  private function merci_bucket_cmp_length($a, $b) {
    $value = $this->date_column;
    $value2 = $this->date_column2;
    $len_a = date_format(date_create($a->{$value2}), 'U') - date_format(date_create($a->{$value}), 'U');
    $len_b = date_format(date_create($b->{$value2}), 'U') - date_format(date_create($b->{$value}), 'U');
    if ($len_a == $len_b) {
      return 0;
    }
    return $len_a < $len_b ? 1 : -1;
  }

}

Members