You are here

class DateRecurBasicFormatter in Recurring Dates Field 3.x

Same name and namespace in other branches
  1. 8.2 src/Plugin/Field/FieldFormatter/DateRecurBasicFormatter.php \Drupal\date_recur\Plugin\Field\FieldFormatter\DateRecurBasicFormatter
  2. 3.0.x src/Plugin/Field/FieldFormatter/DateRecurBasicFormatter.php \Drupal\date_recur\Plugin\Field\FieldFormatter\DateRecurBasicFormatter
  3. 3.1.x src/Plugin/Field/FieldFormatter/DateRecurBasicFormatter.php \Drupal\date_recur\Plugin\Field\FieldFormatter\DateRecurBasicFormatter

Basic recurring date formatter.

Plugin annotation


@FieldFormatter(
  id = "date_recur_basic_formatter",
  label = @Translation("Date recur basic formatter"),
  field_types = {
    "date_recur"
  }
)

Hierarchy

Expanded class hierarchy of DateRecurBasicFormatter

File

src/Plugin/Field/FieldFormatter/DateRecurBasicFormatter.php, line 33

Namespace

Drupal\date_recur\Plugin\Field\FieldFormatter
View source
class DateRecurBasicFormatter extends DateRangeDefaultFormatter {
  use DependencyTrait;
  protected const COUNT_PER_ITEM_ITEM = 'per_item';
  protected const COUNT_PER_ITEM_ALL = 'all_items';

  /**
   * The date recur interpreter entity storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $dateRecurInterpreterStorage;

  /**
   * Date format config ID.
   *
   * @var string|null
   */
  protected $formatType;

  /**
   * Constructs a new DateRecurBasicFormatter.
   *
   * @param string $plugin_id
   *   The plugin_id for the formatter.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
   *   The definition of the field to which the formatter is associated.
   * @param array $settings
   *   The formatter settings.
   * @param string $label
   *   The formatter label display setting.
   * @param string $view_mode
   *   The view mode.
   * @param array $third_party_settings
   *   Third party settings.
   * @param \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter
   *   The date formatter service.
   * @param \Drupal\Core\Entity\EntityStorageInterface $dateFormatStorage
   *   The date format entity storage.
   * @param \Drupal\Core\Entity\EntityStorageInterface $dateRecurInterpreterStorage
   *   The date recur interpreter entity storage.
   */
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatterInterface $dateFormatter, EntityStorageInterface $dateFormatStorage, EntityStorageInterface $dateRecurInterpreterStorage) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $dateFormatter, $dateFormatStorage);
    $this->dateRecurInterpreterStorage = $dateRecurInterpreterStorage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'], $container
      ->get('date.formatter'), $container
      ->get('entity_type.manager')
      ->getStorage('date_format'), $container
      ->get('entity_type.manager')
      ->getStorage('date_recur_interpreter'));
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() : array {
    return [
      // Show number of occurrences.
      'show_next' => 5,
      // Whether number of occurrences should be per item or in total.
      'count_per_item' => TRUE,
      // Date format for occurrences.
      'occurrence_format_type' => 'medium',
      // Date format for end date, if same day as start date.
      'same_end_date_format_type' => 'medium',
      'interpreter' => NULL,
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() : array {
    $this->dependencies = parent::calculateDependencies();

    /** @var string|null $dateFormatId */
    $interpreterId = $this
      ->getSetting('interpreter');
    if ($interpreterId && ($interpreter = $this->dateRecurInterpreterStorage
      ->load($interpreterId))) {
      $this
        ->addDependency('config', $interpreter
        ->getConfigDependencyName());
    }
    $dateFormatDependencies = [
      'format_type',
      'occurrence_format_type',
      'same_end_date_format_type',
    ];
    foreach ($dateFormatDependencies as $dateFormatId) {
      $id = $this
        ->getSetting($dateFormatId);
      $dateFormat = $this->dateFormatStorage
        ->load($id);
      if (!$dateFormat) {
        continue;
      }
      $this
        ->addDependency('config', $dateFormat
        ->getConfigDependencyName());
    }
    return $this->dependencies;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) : array {
    $form = parent::settingsForm($form, $form_state);
    $originalFormatType = $form['format_type'];
    unset($form['format_type']);

    // Redefine format type to change the natural order of form fields.
    $form['format_type'] = $originalFormatType;
    $form['format_type']['#title'] = $this
      ->t('Non-Repeating Date format');
    $form['format_type']['#description'] = $this
      ->t('Date format used for field values without repeat rules.');
    $form['occurrence_format_type'] = $originalFormatType;
    $form['occurrence_format_type']['#title'] = $this
      ->t('Start and end date format');
    $form['occurrence_format_type']['#default_value'] = $this
      ->getSetting('occurrence_format_type');
    $form['occurrence_format_type']['#description'] = $this
      ->t('Date format used for field values with repeat rules.');
    $form['same_end_date_format_type'] = $originalFormatType;
    $form['same_end_date_format_type']['#title'] = $this
      ->t('Same day end date format');
    $form['same_end_date_format_type']['#description'] = $this
      ->t('Date format used for end date if field value has repeat rule. Used only if occurs on same calendar day as start date.');
    $form['same_end_date_format_type']['#default_value'] = $this
      ->getSetting('same_end_date_format_type');

    // Redefine separator to change the natural order of form fields.
    $originalSeparator = $form['separator'];
    unset($form['separator']);
    $form['separator'] = $originalSeparator;

    // Change the width of the field if not already set. (Not set by default)
    $form['separator']['#size'] = $form['separator']['#size'] ?? 5;

    // Redefine timezone to change the natural order of form fields.
    $originalTimezoneOverride = $form['timezone_override'];
    unset($form['timezone_override']);
    $form['timezone_override'] = $originalTimezoneOverride;
    $form['timezone_override']['#empty_option'] = $this
      ->t('Use current user timezone');
    $form['timezone_override']['#description'] = $this
      ->t('Change the timezone used for displaying dates (not recommended).');
    $interpreterOptions = array_map(function (DateRecurInterpreterInterface $interpreter) : string {
      return $interpreter
        ->label() ?? (string) $this
        ->t('- Missing label -');
    }, $this->dateRecurInterpreterStorage
      ->loadMultiple());
    $form['interpreter'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Recurring date interpreter'),
      '#description' => $this
        ->t('Choose a plugin for converting rules into a human readable description.'),
      '#default_value' => $this
        ->getSetting('interpreter'),
      '#options' => $interpreterOptions,
      '#required' => FALSE,
      '#empty_option' => $this
        ->t('- Do not show interpreted rule -'),
    ];
    $form['occurrences'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => [
          'container-inline',
        ],
      ],
      '#tree' => FALSE,
    ];
    $form['occurrences']['show_next'] = [
      '#field_prefix' => $this
        ->t('Show maximum of'),
      '#field_suffix' => $this
        ->t('occurrences'),
      '#type' => 'number',
      '#min' => 0,
      '#default_value' => $this
        ->getSetting('show_next'),
      '#attributes' => [
        'size' => 4,
      ],
      '#element_validate' => [
        [
          static::class,
          'validateSettingsShowNext',
        ],
      ],
    ];
    $form['occurrences']['count_per_item'] = [
      '#type' => 'select',
      '#options' => [
        static::COUNT_PER_ITEM_ITEM => $this
          ->t('per field item'),
        static::COUNT_PER_ITEM_ALL => $this
          ->t('across all field items'),
      ],
      '#default_value' => $this
        ->getSetting('count_per_item') ? static::COUNT_PER_ITEM_ITEM : static::COUNT_PER_ITEM_ALL,
      '#element_validate' => [
        [
          static::class,
          'validateSettingsCountPerItem',
        ],
      ],
    ];
    return $form;
  }

  /**
   * Validation callback for count_per_item.
   *
   * @param array $element
   *   The element being processed.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param array $complete_form
   *   The complete form structure.
   */
  public static function validateSettingsCountPerItem(array &$element, FormStateInterface $form_state, array &$complete_form) : void {
    $countPerItem = $element['#value'] == static::COUNT_PER_ITEM_ITEM;
    $arrayParents = array_slice($element['#array_parents'], 0, -2);
    $formatterForm = NestedArray::getValue($complete_form, $arrayParents);
    $parents = $formatterForm['#parents'];
    $parents[] = 'count_per_item';
    $form_state
      ->setValue($parents, $countPerItem);
  }

  /**
   * Validation callback for show_next.
   *
   * @param array $element
   *   The element being processed.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param array $complete_form
   *   The complete form structure.
   */
  public static function validateSettingsShowNext(array &$element, FormStateInterface $form_state, array &$complete_form) : void {
    $arrayParents = array_slice($element['#array_parents'], 0, -2);
    $formatterForm = NestedArray::getValue($complete_form, $arrayParents);
    $parents = $formatterForm['#parents'];
    $parents[] = 'show_next';
    $form_state
      ->setValue($parents, $element['#value']);
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSummary() : array {
    $this->formatType = $this
      ->getSetting('format_type');
    $summary = parent::settingsSummary();
    $countPerItem = $this
      ->getSetting('count_per_item');
    $showOccurrencesCount = $this
      ->getSetting('show_next');
    if ($showOccurrencesCount > 0) {
      $summary[] = $this
        ->formatPlural($showOccurrencesCount, 'Show maximum of @count occurrence @per', 'Show maximum of @count occurrences @per', [
        '@per' => $countPerItem ? $this
          ->t('per field item') : $this
          ->t('across all field items'),
      ]);
    }
    $start = new DrupalDateTime('today 9am');
    $endSameDay = clone $start;
    $endSameDay
      ->setTime(17, 0, 0);
    $summary['sample_same_day'] = [
      '#type' => 'inline_template',
      '#template' => '{{ label }}: {{ sample }}',
      '#context' => [
        'label' => $this
          ->t('Same day range'),
        'sample' => $this
          ->buildDateRangeValue($start, $endSameDay, TRUE),
      ],
    ];
    $endDifferentDay = clone $endSameDay;
    $endDifferentDay
      ->modify('+1 day');
    $summary['sample_different_day'] = [
      '#type' => 'inline_template',
      '#template' => '{{ label }}: {{ sample }}',
      '#context' => [
        'label' => $this
          ->t('Different day range'),
        'sample' => $this
          ->buildDateRangeValue($start, $endDifferentDay, TRUE),
      ],
    ];
    return $summary;
  }

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) : array {

    // Whether maximum is per field item or in total.
    $isSharedMaximum = !$this
      ->getSetting('count_per_item');

    // Maximum amount of occurrences to be displayed.
    $occurrenceQuota = (int) $this
      ->getSetting('show_next');
    $elements = [];
    foreach ($items as $delta => $item) {
      $value = $this
        ->viewItem($item, $occurrenceQuota);
      $occurrenceQuota -= $isSharedMaximum ? count($value['#occurrences']) : 0;
      $elements[$delta] = $value;
      if ($occurrenceQuota <= 0) {
        break;
      }
    }
    return $elements;
  }

  /**
   * Generate the output appropriate for a field item.
   *
   * @param \Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem $item
   *   A field item.
   * @param int $maxOccurrences
   *   Maximum number of occurrences to show for this field item.
   *
   * @return array
   *   A render array for a field item.
   */
  protected function viewItem(DateRecurItem $item, $maxOccurrences) : array {
    $cacheability = new CacheableMetadata();
    $build = [
      '#theme' => 'date_recur_basic_formatter',
      '#is_recurring' => $item
        ->isRecurring(),
    ];
    $startDate = $item->start_date;

    /** @var \Drupal\Core\Datetime\DrupalDateTime|null $endDate */
    $endDate = $item->end_date ?? $startDate;
    if (!$startDate || !$endDate) {
      return $build;
    }
    $build['#date'] = $this
      ->buildDateRangeValue($startDate, $endDate, FALSE);

    // Render the rule.
    if ($item
      ->isRecurring() && $this
      ->getSetting('interpreter')) {

      /** @var string|null $interpreterId */
      $interpreterId = $this
        ->getSetting('interpreter');
      if ($interpreterId && ($interpreter = $this->dateRecurInterpreterStorage
        ->load($interpreterId))) {
        assert($interpreter instanceof DateRecurInterpreterInterface);
        $rules = $item
          ->getHelper()
          ->getRules();
        $plugin = $interpreter
          ->getPlugin();
        $cacheability
          ->addCacheableDependency($interpreter);
        $build['#interpretation'] = $plugin
          ->interpret($rules, 'en');
      }
    }

    // Occurrences are generated even if the item is not recurring.
    $build['#occurrences'] = array_map(function (DateRange $occurrence) : array {
      $startDate = DrupalDateTime::createFromDateTime($occurrence
        ->getStart());
      $endDate = DrupalDateTime::createFromDateTime($occurrence
        ->getEnd());
      return $this
        ->buildDateRangeValue($startDate, $endDate, TRUE);
    }, $this
      ->getOccurrences($item, $maxOccurrences));
    $cacheability
      ->applyTo($build);
    return $build;
  }

  /**
   * Builds a date range suitable for rendering.
   *
   * @param \Drupal\Core\Datetime\DrupalDateTime $startDate
   *   The start date.
   * @param \Drupal\Core\Datetime\DrupalDateTime $endDate
   *   The end date.
   * @param bool $isOccurrence
   *   Whether the range is an occurrence of a repeating value.
   *
   * @return array
   *   A render array.
   */
  protected function buildDateRangeValue(DrupalDateTime $startDate, DrupalDateTime $endDate, $isOccurrence) : array {
    $this->formatType = $isOccurrence ? $this
      ->getSetting('occurrence_format_type') : $this
      ->getSetting('format_type');
    $startDateString = $this
      ->buildDateWithIsoAttribute($startDate);

    // Show the range if start and end are different, otherwise only start date.
    if ($startDate
      ->getTimestamp() === $endDate
      ->getTimestamp()) {
      return $startDateString;
    }
    else {

      // Start date and end date are different.
      $this->formatType = $startDate
        ->format('Ymd') == $endDate
        ->format('Ymd') ? $this
        ->getSetting('same_end_date_format_type') : $this
        ->getSetting('occurrence_format_type');
      $endDateString = $this
        ->buildDateWithIsoAttribute($endDate);
      return [
        'start_date' => $startDateString,
        'separator' => [
          '#plain_text' => $this
            ->getSetting('separator'),
        ],
        'end_date' => $endDateString,
      ];
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function formatDate($date) : string {
    assert($date instanceof DrupalDateTime);
    if (!is_string($this->formatType)) {
      throw new \LogicException('Date format must be set.');
    }
    $timezone = $this
      ->getSetting('timezone_override') ?: $date
      ->getTimezone()
      ->getName();
    return $this->dateFormatter
      ->format($date
      ->getTimestamp(), $this->formatType, '', $timezone);
  }

  /**
   * Get the occurrences for a field item.
   *
   * Occurrences are abstracted out to make it easier for extending formatters
   * to change.
   *
   * @param \Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem $item
   *   A field item.
   * @param int $maxOccurrences
   *   Maximum number of occurrences to render.
   *
   * @return \Drupal\date_recur\DateRange[]
   *   A render array.
   */
  protected function getOccurrences(DateRecurItem $item, $maxOccurrences) : array {
    $start = new \DateTime('now');
    return $item
      ->getHelper()
      ->getOccurrences($start, NULL, $maxOccurrences);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DateRecurBasicFormatter::$dateRecurInterpreterStorage protected property The date recur interpreter entity storage.
DateRecurBasicFormatter::$formatType protected property Date format config ID.
DateRecurBasicFormatter::buildDateRangeValue protected function Builds a date range suitable for rendering.
DateRecurBasicFormatter::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides PluginSettingsBase::calculateDependencies
DateRecurBasicFormatter::COUNT_PER_ITEM_ALL protected constant
DateRecurBasicFormatter::COUNT_PER_ITEM_ITEM protected constant
DateRecurBasicFormatter::create public static function Creates an instance of the plugin. Overrides DateTimeFormatterBase::create
DateRecurBasicFormatter::defaultSettings public static function Defines the default settings for this plugin. Overrides DateRangeDefaultFormatter::defaultSettings
DateRecurBasicFormatter::formatDate protected function Creates a formatted date value as a string. Overrides DateTimeDefaultFormatter::formatDate
DateRecurBasicFormatter::getOccurrences protected function Get the occurrences for a field item.
DateRecurBasicFormatter::settingsForm public function Returns a form to configure settings for the formatter. Overrides DateRangeDefaultFormatter::settingsForm
DateRecurBasicFormatter::settingsSummary public function Returns a short summary for the current formatter settings. Overrides DateRangeDefaultFormatter::settingsSummary
DateRecurBasicFormatter::validateSettingsCountPerItem public static function Validation callback for count_per_item.
DateRecurBasicFormatter::validateSettingsShowNext public static function Validation callback for show_next.
DateRecurBasicFormatter::viewElements public function Overrides DateTimeRangeTrait::viewElements
DateRecurBasicFormatter::viewItem protected function Generate the output appropriate for a field item.
DateRecurBasicFormatter::__construct public function Constructs a new DateRecurBasicFormatter. Overrides DateTimeFormatterBase::__construct
DateTimeFormatterBase::$dateFormatStorage protected property The date format entity storage.
DateTimeFormatterBase::$dateFormatter protected property The date formatter service.
DateTimeFormatterBase::buildDate protected function Creates a render array from a date object.
DateTimeFormatterBase::buildDateWithIsoAttribute protected function Creates a render array from a date object with ISO date attribute.
DateTimeFormatterBase::getFormatSettings protected function Gets a settings array suitable for DrupalDateTime::format().
DateTimeFormatterBase::setTimeZone protected function Sets the proper time zone on a DrupalDateTime object for the current user.
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function 2
DependencyTrait::$dependencies protected property The object's dependencies.
DependencyTrait::addDependencies protected function Adds multiple dependencies.
DependencyTrait::addDependency protected function Adds a dependency.
FormatterBase::$fieldDefinition protected property The field definition.
FormatterBase::$label protected property The label display setting.
FormatterBase::$settings protected property The formatter settings. Overrides PluginSettingsBase::$settings
FormatterBase::$viewMode protected property The view mode.
FormatterBase::getFieldSetting protected function Returns the value of a field setting.
FormatterBase::getFieldSettings protected function Returns the array of field settings.
FormatterBase::isApplicable public static function Returns if the formatter can be used for the provided field. Overrides FormatterInterface::isApplicable 14
FormatterBase::prepareView public function Allows formatters to load information for field values being displayed. Overrides FormatterInterface::prepareView 2
FormatterBase::view public function Builds a renderable array for a fully themed field. Overrides FormatterInterface::view 1
MessengerTrait::$messenger protected property The messenger. 27
MessengerTrait::messenger public function Gets the messenger. 27
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 2
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginSettingsBase::$defaultSettingsMerged protected property Whether default settings have been merged into the current $settings.
PluginSettingsBase::$thirdPartySettings protected property The plugin settings injected by third party modules.
PluginSettingsBase::getSetting public function Returns the value of a setting, or its default value if absent. Overrides PluginSettingsInterface::getSetting
PluginSettingsBase::getSettings public function Returns the array of settings, including defaults for missing settings. Overrides PluginSettingsInterface::getSettings
PluginSettingsBase::getThirdPartyProviders public function Gets the list of third parties that store information. Overrides ThirdPartySettingsInterface::getThirdPartyProviders
PluginSettingsBase::getThirdPartySetting public function Gets the value of a third-party setting. Overrides ThirdPartySettingsInterface::getThirdPartySetting
PluginSettingsBase::getThirdPartySettings public function Gets all third-party settings of a given module. Overrides ThirdPartySettingsInterface::getThirdPartySettings
PluginSettingsBase::mergeDefaults protected function Merges default settings values into $settings.
PluginSettingsBase::onDependencyRemoval public function Informs the plugin that some configuration it depends on will be deleted. Overrides PluginSettingsInterface::onDependencyRemoval 3
PluginSettingsBase::setSetting public function Sets the value of a setting for the plugin. Overrides PluginSettingsInterface::setSetting
PluginSettingsBase::setSettings public function Sets the settings for the plugin. Overrides PluginSettingsInterface::setSettings
PluginSettingsBase::setThirdPartySetting public function Sets the value of a third-party setting. Overrides ThirdPartySettingsInterface::setThirdPartySetting
PluginSettingsBase::unsetThirdPartySetting public function Unsets a third-party setting. Overrides ThirdPartySettingsInterface::unsetThirdPartySetting
StringTranslationTrait::$stringTranslation protected property The string translation service. 4
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.