OfficeHoursItem.php 9.24 KB
Newer Older
1 2 3 4 5 6
<?php

namespace Drupal\office_hours\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
7
use Drupal\Core\Form\FormStateInterface;
8
use Drupal\Core\TypedData\DataDefinition;
9
use Drupal\office_hours\Element\OfficeHoursDatetime;
10
use Drupal\office_hours\OfficeHoursDateHelper;
11 12 13 14 15 16 17 18

/**
 * Plugin implementation of the 'office_hours' field type.
 *
 * @FieldType(
 *   id = "office_hours",
 *   label = @Translation("Office hours"),
 *   description = @Translation("This field stores weekly 'office hours' or 'opening hours' in the database."),
19
 *   default_widget = "office_hours_default",
20
 *   default_formatter = "office_hours",
21
 *   list_class = "\Drupal\office_hours\Plugin\Field\FieldType\OfficeHoursItemList",
22 23
 * )
 */
24
class OfficeHoursItem extends FieldItemBase {
25 26 27 28 29

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
30 31 32
    return [
      'columns' => [
        'day' => [
33 34
          'type' => 'int',
          'not null' => FALSE,
35 36
        ],
        'starthours' => [
37 38
          'type' => 'int',
          'not null' => FALSE,
39 40
        ],
        'endhours' => [
41 42
          'type' => 'int',
          'not null' => FALSE,
43 44
        ],
        'comment' => [
45 46 47
          'type' => 'varchar',
          'length' => 255,
          'not null' => FALSE,
48 49 50
        ],
      ],
    ];
51 52 53 54 55 56 57
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    //    $properties['value'] = DataDefinition::create('string')
58
    //      ->setLabel( $this->t('Office hours'));
59
    $properties['day'] = DataDefinition::create('integer')
60
      ->setLabel(t('Day'))
johnv's avatar
johnv committed
61
      ->setDescription("Stores the day of the week's numeric representation (0-6)");
62
    $properties['starthours'] = DataDefinition::create('integer')
63
      ->setLabel(t('Start hours'))
johnv's avatar
johnv committed
64
      ->setDescription("Stores the start hours value");
65
    $properties['endhours'] = DataDefinition::create('integer')
66
      ->setLabel(t('End hours'))
johnv's avatar
johnv committed
67
      ->setDescription("Stores the end hours value");
68 69
    $properties['comment'] = DataDefinition::create('string')
      ->setLabel(t('Comment'))
70
      ->addConstraint('Length', ['max' => 255])
71
      ->setDescription("Stores the comment");
72 73 74 75 76 77 78 79

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultStorageSettings() {
80
    $defaultStorageSettings = [
81
        'time_format' => 'G',
82
        'element_type' => 'office_hours_datelist',
83 84 85
        'increment' => 30,
        'limit_start' => '',
        'limit_end' => '',
86 87
        'comment' => TRUE,
        'valhrs' => FALSE,
johnv's avatar
johnv committed
88
        'cardinality_per_day' => 2,
89
      ] + parent::defaultStorageSettings();
90 91 92 93 94 95 96 97

    return $defaultStorageSettings;
  }

  /**
   * {@inheritdoc}
   */
  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
98
    $element = parent::storageSettingsForm($form, $form_state, $has_data);
99 100 101 102 103

    $settings = $this->getFieldDefinition()
      ->getFieldStorageDefinition()
      ->getSettings();

104
    // Get a formatted list of valid hours values.
105 106 107 108 109 110 111 112
    $hours = OfficeHoursDateHelper::hours('H', FALSE);
    foreach ($hours as $key => &$hour) {
      if (!empty($hour)) {
        $hrs = OfficeHoursDateHelper::format($hour . '00', 'H:i');
        $ampm = OfficeHoursDateHelper::format($hour . '00', 'g:i a');
        $hour = "$hrs ($ampm)";
      }
    }
113

114
    $element['#element_validate'] = [[$this, 'validateOfficeHours']];
115
    $description = $this->t(
116
      'The maximum number of time slots, that are allowed per day.
117 118 119 120
      <br/><strong> Warning! Lowering this setting after data has been created
      could result in the loss of data! </strong><br/> Be careful when using
      more then 2 slots per day, since not all external services (like Google
      Places) support this.');
121
    $element['cardinality_per_day'] = [
122
      '#type' => 'select',
123
      '#title' => $this->t('Number of time slots per day'),
johnv's avatar
johnv committed
124
      // @todo For 'multiple slots per day': add support for FIELD_CARDINALITY_UNLIMITED.
125
      // '#options' => [FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED => $this->t('unlimited')) + drupal_map_assoc(range(1, 10)],
126
      '#options' => array_combine(range(1, 12), range(1, 12)),
johnv's avatar
johnv committed
127
      '#default_value' => $settings['cardinality_per_day'],
128 129
      '#description' => $description,
      // '#disabled' => $has_data,
130
    ];
131

johnv's avatar
johnv committed
132
    // @todo D8 Align with DateTimeDatelistWidget.
133
    $element['time_format'] = [
134
      '#type' => 'select',
135
      '#title' => $this->t('Time notation'),
136
      '#options' => [
137 138 139 140
        'G' => $this->t('24 hour time') . ' (9:00)', // D7: key = 0
        'H' => $this->t('24 hour time') . ' (09:00)', // D7: key = 2
        'g' => $this->t('12 hour time') . ' (9:00 am)', // D7: key = 1
        'h' => $this->t('12 hour time') . ' (09:00 am)', // D7: key = 1
141
      ],
142
      '#default_value' => $settings['time_format'],
143
      '#required' => FALSE,
144
      '#description' => $this->t('Format of the time in the widget.'),
145
    ];
146 147 148
    $element['element_type'] = [
      '#type' => 'select',
      '#title' => $this->t('Time element type'),
149
      '#description' => $this->t('Select the widget type for selecting the time.'),
150 151 152 153 154 155
      '#options' => [
        'office_hours_datelist' => 'Select list',
        'office_hours_datetime' => 'HTML5 time input',
      ],
      '#default_value' => $this->getSetting('element_type'),
    ];
johnv's avatar
johnv committed
156
    // @todo D8 Align with DateTimeDatelistWidget.
157
    $element['increment'] = [
158
      '#type' => 'select',
159
      '#title' => $this->t('Time increments'),
160
      '#default_value' => $settings['increment'],
161
      '#options' => [
162 163 164 165 166
        1 => $this->t('1 minute'),
        5 => $this->t('5 minute'),
        15 => $this->t('15 minute'),
        30 => $this->t('30 minute'),
        60 => $this->t('60 minute')
167
      ],
168
      '#required' => FALSE,
169
      '#description' => $this->t('Restrict the input to fixed fractions of an hour.'),
170
    ];
171

172
    $element['comment'] = [
173
      '#type' => 'checkbox',
174
      '#title' => $this->t('Allow a comment per time slot'),
175 176
      '#required' => FALSE,
      '#default_value' => $settings['comment'],
177
    ];
178
    $element['valhrs'] = [
179
      '#type' => 'checkbox',
180
      '#title' => $this->t('Validate hours'),
181 182
      '#required' => FALSE,
      '#default_value' => $settings['valhrs'],
183 184 185
      '#description' => $this->t('Assure that endhours are later then starthours.
        Please note that this will work as long as both hours are set and
        the opening hours are not through midnight.'),
186 187
    ];
    $element['limit_start'] = [
188
      '#type' => 'select',
189 190
      '#title' => $this->t('Limit widget hours - from'),
      '#description' => $this->t('Restrict the hours available - select options will start from this hour.'),
191
      '#default_value' => $settings['limit_start'],
192
      '#options' => $hours,
193 194
    ];
    $element['limit_end'] = [
195
      '#type' => 'select',
196
      '#title' => $this->t('Limit widget hours - until'),
197 198 199
      '#description' => $this->t('Restrict the hours available - select options
         will end at this hour. You may leave \'until\' time empty.
         Use \'00:00\' for closing at midnight.'),
200
      '#default_value' => $settings['limit_end'],
201
      '#options' => $hours,
202
    ];
203 204 205 206 207 208 209 210

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty() {
211
    // Note: in Week-widget, day is <> '', in List-widget, day can be ''.
212 213 214 215
    // Note: test every change with Week/List widget and Select/HTML5 element!
    if (OfficeHoursDatetime::isEmpty($this->get('starthours')->getValue())
      && OfficeHoursDatetime::isEmpty($this->get('endhours')->getValue())
      && empty($this->get('comment')->getValue())) {
216 217 218 219 220
      return TRUE;
    }
    return FALSE;
  }

221 222 223
  /**
   * {@inheritdoc}
   */
224
  public function getConstraints() {
225
    $constraints = [];
johnv's avatar
johnv committed
226
    // @todo When adding parent::getConstraints(), only English is allowed...
227
    // $constraints = parent::getConstraints();
228 229
    $max_length = $this->getSetting('max_length');
    if ($max_length) {
230 231
      $constraint_manager = \Drupal::typedDataManager()
        ->getValidationConstraintManager();
232 233 234
      $constraints[] = $constraint_manager->create('ComplexData', [
        'value' => [
          'Length' => [
235
            'max' => $max_length,
236
            'maxMessage' => $this->t('%name: may not be longer than @max characters.', [
237 238
              '%name' => $this->getFieldDefinition()->getLabel(),
              '@max' => $max_length,
239 240 241 242
            ]),
          ],
        ],
      ]);
243 244 245 246 247 248 249 250 251
    }
    return $constraints;
  }

  /**
   * Implements the #element_validate callback for storageSettingsForm().
   *
   * Verifies the office hours limits.
   * "Please note that this will work as long as the opening hours are not through midnight."
252
   * "You may leave 'until' time empty. Use '00:00' for closing at midnight."
johnv's avatar
johnv committed
253 254 255
   *
   * @param array $element
   * @param \Drupal\Core\Form\FormStateInterface $form_state
256
   */
257
  public function validateOfficeHours(array $element, FormStateInterface &$form_state) {
258 259
    if (!empty($element['limit_end']['#value']) &&
      $element['limit_end']['#value'] < $element['limit_start']['#value']) {
260
      $form_state->setError($element['limit_start'], $this->t('%start is later then %end.', [
261 262
        '%start' => $element['limit_start']['#title'],
        '%end' => $element['limit_end']['#title'],
263
      ]));
264 265 266 267
    }
  }

}