Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions src/Plugin/TypedDataFormWidget/DateTimeRangeWidget.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

namespace Drupal\typed_data\Plugin\TypedDataFormWidget;

use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformStateInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\Type\DateTimeInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\typed_data\Form\SubformState;
use Drupal\typed_data\Widget\FormWidgetBase;
use Symfony\Component\Validator\ConstraintViolationListInterface;

/**
* @TypedDataFormWidget(
* id = "datetime_range",
* label = @Translation("DateTime Range"),
* description = @Translation("A datetime range input widget."),
* )
*/
class DateTimeRangeWidget extends FormWidgetBase {

/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'label' => NULL,
'description' => NULL,
];
}

/**
* {@inheritdoc}
*/
public function isApplicable(DataDefinitionInterface $definition) {
return is_subclass_of($definition->getClass(), DateTimeInterface::class);
}

/**
* {@inheritdoc}
*/
public function form(TypedDataInterface $data, SubformStateInterface $form_state) {
$value = $data->getValue();

$form = SubformState::getNewSubForm();
$form['#theme_wrappers'][] = 'fieldset';

$form['#title'] = $this->configuration['label'] ?: $data->getDataDefinition()->getLabel();
$form['#description'] = $this->configuration['description'] ?: $data->getDataDefinition()->getDescription();

$form['#element_validate'][] = [$this, 'validateStartEnd'];

$form['value'] = [
'#type' => 'datetime',
'#title' => $this->t('Start date'),
'#default_value' => (isset($value['value'])) ? new DrupalDateTime($value['value']) : '',
];
$form['end_value'] = [
'#type' => 'datetime',
'#title' => $this->t('End date'),
'#default_value' => (isset($value['end_value'])) ? new DrupalDateTime($value['end_value']) : '',
];
return $form;
}

/**
* {@inheritdoc}
*/
public function extractFormValues(TypedDataInterface $data, SubformStateInterface $form_state) {
$value = $form_state->getValue('value');
$end_value = $form_state->getValue('end_value');
if ($value instanceof DrupalDateTime) {
$value = $value->format('Y-m-d H:i:s');
}
if ($end_value instanceof DrupalDateTime) {
$end_value = $end_value->format('Y-m-d H:i:s');
}
$data->setValue([
'value' => $value,
'end_value' => $end_value,
]);
}

/**
* {@inheritdoc}
*/
public function flagViolations(TypedDataInterface $data, ConstraintViolationListInterface $violations, SubformStateInterface $formState) {
foreach ($violations as $violation) {
/** @var ConstraintViolationInterface $violation */
$formState->setErrorByName('value', $violation->getMessage());
}
}

/**
* {@inheritdoc}
*/
public function getConfigurationDefinitions(DataDefinitionInterface $definition) {
return [
'label' => DataDefinition::create('string')
->setLabel($this->t('Label')),
'description' => DataDefinition::create('string')
->setLabel($this->t('Description')),
];
}

/**
* Ensure that the start date <= the end date.
*
* @param array $element
* An associative array containing the properties and children of the
* generic form element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*/
public function validateStartEnd(array &$element, FormStateInterface $form_state, array &$complete_form) {
$start_date = $element['value']['#value']['object'];
$end_date = $element['end_value']['#value']['object'];

if ($start_date instanceof DrupalDateTime && $end_date instanceof DrupalDateTime) {
if ($start_date->getTimestamp() !== $end_date->getTimestamp()) {
$interval = $start_date->diff($end_date);
if ($interval->invert === 1) {
$form_state->setError($element, $this->t('The @title end date cannot be before the start date', ['@title' => $element['#title']]));
}
}
}
}

}
88 changes: 88 additions & 0 deletions src/Plugin/TypedDataFormWidget/DateTimeWidget.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Drupal\typed_data\Plugin\TypedDataFormWidget;

use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Form\SubformStateInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\Type\DateTimeInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\typed_data\Form\SubformState;
use Drupal\typed_data\Widget\FormWidgetBase;
use Symfony\Component\Validator\ConstraintViolationListInterface;

/**
* @TypedDataFormWidget(
* id = "datetime",
* label = @Translation("DateTime"),
* description = @Translation("A datetime input widget."),
* )
*/
class DateTimeWidget extends FormWidgetBase {

/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'label' => NULL,
'description' => NULL,
];
}

/**
* {@inheritdoc}
*/
public function isApplicable(DataDefinitionInterface $definition) {
return is_subclass_of($definition->getClass(), DateTimeInterface::class);
}

/**
* {@inheritdoc}
*/
public function form(TypedDataInterface $data, SubformStateInterface $form_state) {
$form = SubformState::getNewSubForm();
$form['value'] = [
'#type' => 'datetime',
'#title' => $this->configuration['label'] ?: $data->getDataDefinition()->getLabel(),
'#description' => $this->configuration['description'] ?: $data->getDataDefinition()->getDescription(),
'#default_value' => (!empty($data->getValue())) ? new DrupalDateTime($data->getValue()) : '',
];
return $form;
}

/**
* {@inheritdoc}
*/
public function extractFormValues(TypedDataInterface $data, SubformStateInterface $form_state) {
$value = $form_state->getValue('value');
if ($value instanceof DrupalDateTime) {
$value = $value->format('Y-m-d H:i:s');
}
$data->setValue($value);
}

/**
* {@inheritdoc}
*/
public function flagViolations(TypedDataInterface $data, ConstraintViolationListInterface $violations, SubformStateInterface $formState) {
foreach ($violations as $violation) {
/** @var ConstraintViolationInterface $violation */
$formState->setErrorByName('value', $violation->getMessage());
}
}

/**
* {@inheritdoc}
*/
public function getConfigurationDefinitions(DataDefinitionInterface $definition) {
return [
'label' => DataDefinition::create('string')
->setLabel($this->t('Label')),
'description' => DataDefinition::create('string')
->setLabel($this->t('Description')),
];
}

}
11 changes: 11 additions & 0 deletions tests/modules/typed_data_widget_test/src/FormWidgetExampleForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,21 @@ public function getExampleContextDefinition($widget_id) {
->setDescription('Some example string with max. 8 characters.')
->setDefaultValue('default')
->addConstraint('Length', ['max' => 8]);

case 'select':
return ContextDefinition::create('filter_format')
->setLabel('Filter format')
->setDescription('Some example selection.');

case 'datetime':
return ContextDefinition::create('datetime_iso8601')
->setLabel('Example datetime')
->setDescription('Some example datetime.');

case 'datetime_range':
return ContextDefinition::create('any')
->setLabel('Example datetime range')
->setDescription('Some example datetime range.');
}

}
Expand Down