You are here

OfficeHoursFormatterTrait.php in Office Hours 8

File

src/OfficeHoursFormatterTrait.php
View source
<?php

namespace Drupal\office_hours;

use Drupal\Component\Utility\Html;
use Drupal\office_hours\Element\OfficeHoursDatetime;

/**
 * Factors out OfficeHoursItemList->getItems()->getRows().
 */
trait OfficeHoursFormatterTrait {

  /**
   * Returns the items of a field.
   *
   * @param array $items
   *   Result from FieldItemListInterface $items->getValue().
   * @param array $settings
   * @param array $field_settings
   * @param $time
   *
   * @return array
   *   The formatted list of slots.
   */
  public function getRows(array $items, array $settings, array $field_settings, $time = NULL) {
    $default_office_hours = [
      'startday' => NULL,
      'endday' => NULL,
      'closed' => $this
        ->t(Html::escape($settings['closed_format'])),
      'current' => FALSE,
      'next' => FALSE,
      'slots' => [],
      'formatted_slots' => [],
      'comments' => [],
    ];

    // Initialize days and times, using date_api as key (0=Sun, 6=Sat).
    // Empty days are not yet present in $items, and are now added in $days.
    $office_hours = [];
    for ($day = 0; $day < 7; $day++) {
      $office_hours[$day] = [
        'startday' => $day,
      ] + $default_office_hours;
    }

    // Loop through all lines.
    // Detect the current line and the open/closed status.
    // Convert the day number to (int) to get '0' for Sundays, not 'false'.
    $time = $time === NULL ? \Drupal::time()
      ->getRequestTime() : $time;
    $today = (int) idate('w', $time);

    // Get day_number: (0=Sun, 6=Sat).
    $now = date('Hi', $time);

    // 'Hi' format, with leading zero (0900).
    foreach ($items as $key => $item) {

      // Calculate start and end times.
      $day = (int) $item['day'];

      // Format to 'Hi' format, with leading zero (0900).
      $start = OfficeHoursDatetime::get($item['starthours'], 'Hi');
      $end = OfficeHoursDatetime::get($item['endhours'], 'Hi');
      $office_hours[$day] = $office_hours[$day] ?? [
        'startday' => $day,
      ] + $default_office_hours;
      $office_hours[$day]['slots'][] = [
        // Format to 'Hi' format, with leading zero (0900).
        'start' => $start,
        'end' => $end,
        'comment' => $item['comment'],
      ];
    }
    $next = NULL;
    foreach ($office_hours as $day => &$day_data) {
      foreach ($day_data['slots'] as $slot_id => $slot) {
        if ($day <= $today) {

          // Initialize to first day of (next) week, in case we're closed
          // the rest of the week.
          // @todo Use $settings['office_hours_first_day'] ?
          if ($next === NULL) {
            $next = $day;
          }
        }
        if ($day == $today) {
          $start = $slot['start'];
          $end = $slot['end'];
          if ($start > $now) {

            // We will open later today.
            $next = $day;
          }
          elseif ($start < $end && $end < $now) {

            // We were open today, but are already closed.
          }
          else {

            // We are still open.
            $day_data['current'] = TRUE;
            $next = $day;
          }
        }
        elseif ($day > $today) {
          if ($next === NULL) {
            $next = $day;
          }
          elseif ($next < $today) {
            $next = $day;
          }
          else {

            // Just for analysis.
          }
        }
        else {

          // Just for analysis.
        }
      }
    }
    if ($next !== NULL) {
      $office_hours[$next]['next'] = TRUE;
    }

    /*
     * We have a list of all possible rows, marking the next and current day.
     * Now, filter according to formatter settings.
     */

    // Reorder weekdays to match the first day of the week, using formatter settings.
    $office_hours = OfficeHoursDateHelper::weekDaysOrdered($office_hours, $settings['office_hours_first_day']);

    // Compress all slots of the same day into one item.
    if ($settings['compress']) {
      $office_hours = $this
        ->compressSlots($office_hours);
    }

    // Group identical, consecutive days into one item.
    if ($settings['grouped']) {
      $office_hours = $this
        ->groupDays($office_hours);
    }

    // From here, no more adding/removing, only formatting.
    // Format the day names.
    $office_hours = $this
      ->formatLabels($office_hours, $settings);

    // Format the start and end time into one slot.
    $office_hours = $this
      ->formatSlots($office_hours, $settings, $field_settings);

    // Return the filtered days/slots/items/rows.
    switch ($settings['show_closed']) {
      case 'open':
        $office_hours = $this
          ->keepOpenDays($office_hours);
        break;
      case 'next':
        $office_hours = $this
          ->keepNextDay($office_hours);
        break;
      case 'none':
        $office_hours = [];
        break;
      case 'current':
        $office_hours = $this
          ->keepCurrentDay($office_hours);
        break;
    }
    return $office_hours;
  }

  /**
   * Formatter: compress the slots: E.g., 0900-1100 + 1300-1700 = 0900-1700.
   *
   * @param array $office_hours
   *   Office hours array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function compressSlots(array $office_hours) {
    foreach ($office_hours as &$info) {
      if (is_array($info['slots']) && !empty($info['slots'])) {

        // Initialize first slot of the day.
        $compressed_slot = $info['slots'][0];

        // Compress other slot in first slot.
        foreach ($info['slots'] as $index => $slot) {
          $compressed_slot['start'] = min($compressed_slot['start'], $slot['start']);
          $compressed_slot['end'] = max($compressed_slot['end'], $slot['end']);
        }
        $info['slots'] = [
          0 => $compressed_slot,
        ];
      }
    }
    return $office_hours;
  }

  /**
   * Formatter: group days with same slots into 1 line.
   *
   * @param array $office_hours
   *   Office hours array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function groupDays(array $office_hours) {
    $previous_day = NULL;

    // Keys -7 to +7 are for sorted weekdays.
    $previous_key = -100;
    foreach ($office_hours as $key => &$day) {

      // N.B. for 0=Sundays, we need to (int) the indices.
      // @todo Enable groupDays() for Exception days.
      if ($day['slots'] == $previous_day['slots'] && $key < 8) {
        $day['endday'] = $day['startday'];
        $day['startday'] = $previous_day['startday'];
        $day['current'] = $day['current'] || $previous_day['current'];
        $day['next'] = $day['next'] || $previous_day['next'];
        unset($office_hours[(int) $previous_key]);
      }
      $previous_key = (int) $key;
      $previous_day = $day;
    }
    return $office_hours;
  }

  /**
   * Formatter: remove closed days, keeping open days.
   *
   * @param array $office_hours
   *   Office hours array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function keepOpenDays(array $office_hours) {
    $new_office_hours = [];
    foreach ($office_hours as $day => $info) {
      if (!empty($info['slots'])) {
        $new_office_hours[] = $info;
      }
    }
    return $new_office_hours;
  }

  /**
   * Formatter: remove all days, except the first open day.
   *
   * @param array $office_hours
   *   Office hours array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function keepNextDay(array $office_hours) {
    $new_office_hours = [];
    foreach ($office_hours as $day => $info) {
      if ($info['current'] || $info['next']) {
        $new_office_hours[$day] = $info;
      }
    }
    return $new_office_hours;
  }

  /**
   * Formatter: remove all days, except for today.
   *
   * @param array $office_hours
   *   Office hours array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function keepCurrentDay(array $office_hours) {
    $new_office_hours = [];

    // Get day_number (0=Sun, 6=Sat).
    $today = (int) idate('w', $_SERVER['REQUEST_TIME']);
    foreach ($office_hours as $info) {
      if ($info['startday'] == $today) {
        $new_office_hours[$today] = $info;
      }
    }
    return $new_office_hours;
  }

  /**
   * Formatter: format the day name.
   *
   * @param array $office_hours
   *   Office hours array.
   * @param array $settings
   *   User settings array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function formatLabels(array $office_hours, array $settings) {
    $day_format = $settings['day_format'];
    $day_names = OfficeHoursDateHelper::weekDaysByFormat($settings['day_format']);
    $group_separator = $settings['separator']['grouped_days'];
    $days_suffix = $settings['separator']['day_hours'];
    foreach ($office_hours as $key => &$info) {

      // @todo Expose the date format as a formatter option.
      // @todo Use OfficeHoursDateHelper::getTimeFormat for dates.

      /** @var \Drupal\Core\StringTranslation\TranslatableMarkup $label */
      $label = isset($day_names[$info['startday']]) ? $day_names[$info['startday']] : \Drupal::service('date.formatter')
        ->format($info['startday'], 'html_date');

      // For Exception days.
      if ($day_format == 'none') {
        $info['label'] = $label;
        continue;
      }
      if (isset($info['endday'])) {

        // Adding 'Group days' support for dates seems not feasible.
        $label .= $group_separator . $day_names[$info['endday']];
      }
      $info['label'] = $label ? $label . $days_suffix : '';
    }
    return $office_hours;
  }

  /**
   * Formatter: format the office hours list.
   *
   * @param array $office_hours
   *   Office hours array.
   * @param array $settings
   *   User settings array.
   * @param array $field_settings
   *   User field settings array.
   *
   * @return array
   *   Reformatted office hours array.
   */
  protected function formatSlots(array $office_hours, array $settings, array $field_settings) {
    $time_format = OfficeHoursDateHelper::getTimeFormat($settings['time_format']);
    $time_separator = $settings['separator']['hours_hours'];
    $slot_separator = $settings['separator']['more_hours'];
    foreach ($office_hours as &$day_data) {
      $day_data['formatted_slots'] = [];
      $day_data['comments'] = [];
      foreach ($day_data['slots'] as $key => &$slot_data) {
        $formatted_slot = OfficeHoursDateHelper::formatTimeSlot($slot_data['start'], $slot_data['end'], $time_format, $time_separator);

        // Store the formatted slot in the slot itself.
        $slot_data['formatted_slot'] = $formatted_slot;

        // Store the arrays of formatted slots & comments in the day.
        $day_data['formatted_slots'][] = $formatted_slot;

        // Always add comment to keep aligned with time slot.
        $day_data['comments'][] = $slot_data['comment'];
      }
      $day_data['formatted_slots'] = empty($day_data['formatted_slots']) ? $day_data['closed'] : implode($slot_separator, $day_data['formatted_slots']);

      // Escape and Translate the comments.
      $day_data['comments'] = array_map('Drupal\\Component\\Utility\\Html::escape', $day_data['comments']);
      if ($field_settings['comment'] == 2) {
        $day_data['comments'] = array_map('t', $day_data['comments']);
      }
      $day_data['comments'] = $field_settings['comment'] ? implode($slot_separator, $day_data['comments']) : '';
    }
    return $office_hours;
  }

}

Traits

Namesort descending Description
OfficeHoursFormatterTrait Factors out OfficeHoursItemList->getItems()->getRows().