SU-SWS/stanford_actions

View on GitHub
src/Plugin/Action/FieldClone/DateClone.php

Summary

Maintainability
A
0 mins
Test Coverage
A
98%
<?php

namespace Drupal\stanford_actions\Plugin\Action\FieldClone;

use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;

/**
 * Class Date to increment date fields.
 *
 * @FieldClone(
 *   id = "date",
 *   label = @Translation("Date"),
 *   description = @Translation("Incrementally increase the date on the field for every cloned item."),
 *   fieldTypes = {
 *     "datetime",
 *     "datetime_range",
 *     "daterange"
 *   }
 * )
 */
class DateClone extends FieldCloneBase {

  /**
   * Keyed array to count how many times to clone the entity id (key)
   *
   * @var array
   */
  protected $entityIds = [];

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form = parent::buildConfigurationForm($form, $form_state);

    $increment = range(0, 12);
    unset($increment[0]);

    $form['unit'] = [
      '#type' => 'select',
      '#title' => $this->t('Units'),
      '#options' => [
        'year' => $this->t('Year'),
        'month' => $this->t('Month'),
        'week' => $this->t('Week'),
        'day' => $this->t('Day'),
        'hour' => $this->t('Hour'),
        'minute' => $this->t('Minute'),
        'second' => $this->t('Second'),
      ],
    ];
    $form['increment'] = [
      '#type' => 'select',
      '#title' => $this->t('Increment Amount'),
      '#options' => $increment,
      '#empty_option' => $this->t('- Do Not Change -'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function alterFieldValue(FieldableEntityInterface $original_entity, FieldableEntityInterface $new_entity, $field_name) {
    if (!$new_entity->hasField($field_name) || empty($this->configuration['increment'])) {
      return;
    }

    // To allow us to increase the date value for each subsequent clone, keep
    // track of how many times we've seen this original entity.
    if (!isset($this->entityIds[$original_entity->id()])) {
      $this->entityIds[$original_entity->id()] = 0;
    }
    $this->entityIds[$original_entity->id()]++;

    // Use the multiple to multiply how much to increment from the original
    // entity.
    $this->configuration['multiple'] = $this->entityIds[$original_entity->id()];

    // Loop through all field values and increment them, then set the new values
    // back to the cloned entity.
    $this->incrementFieldValues($new_entity->get($field_name));
  }

  /**
   * Loop through the field values and increase them the configured amount.
   *
   * @param \Drupal\Core\Field\FieldItemListInterface $field_values
   *   The field values object from the entity.
   *
   * @return \Drupal\Core\Field\FieldItemListInterface
   *   Modified field values object.
   *
   * @throws \Exception
   */
  protected function incrementFieldValues(FieldItemListInterface $field_values): FieldItemListInterface {
    /** @var \Drupal\Core\Field\FieldItemInterface $field_value */
    foreach ($field_values as $field_value) {
      $new_value = $field_value->getValue();

      $properties = array_keys($field_value->getProperties());
      $value_properties = array_filter($properties, function ($key) {
        return in_array($key, ['value', 'end_value']);
      });

      foreach ($value_properties as $property) {
        $string_value = $field_value->get($property)->getString();
        $timezone = 'UTC';
        if (in_array('timezone', $properties)) {
          $timezone = $field_value->get('timezone')->getString();
        }
        $new_value[$property] = $this->incrementDateValue($string_value, $timezone);
      }
      $field_value->setValue($new_value);
    }
    return $field_values;
  }

  /**
   * Increase the given date value by the configured amount.
   *
   * @param string $value
   *   Original date value.
   * @param string $timezone
   *   PHP Timezone String.
   *
   * @return string
   *   The new increased value.
   *
   * @throws \Exception
   */
  protected function incrementDateValue(string $value, string $timezone = 'UTC'): string {
    $increment = $this->configuration['multiple'] * $this->configuration['increment'];

    $new_value = new \DateTime($value, new \DateTimeZone($timezone));
    $daylight_savings = date('I', $new_value->getTimestamp());

    // Add the interval that is in the form of "2 days" or "6 hours".
    $interval = \DateInterval::createFromDateString($increment . ' ' . $this->configuration['unit']);
    $new_value->add($interval);

    // Date fields that don't collect the time use a different date format. We
    // check if the date length is the same length as an example format.
    if (strlen($value) == strlen('2019-02-21')) {
      return $new_value->format('Y-m-d');
    }

    // Adjust the time of the string if the new value skips over the daylight
    // savings time.
    if (date('I', $new_value->getTimestamp()) != $daylight_savings) {
      // Accommodate both going into and out of daylight savings time.
      $interval = $daylight_savings ? '1 hour' : '-1 hour';
      $interval = \DateInterval::createFromDateString($interval);
      $new_value->add($interval);
    }

    return $new_value->format('Y-m-d\TH:i:s');
  }

}