bee-8.x-1.1/modules/bee_webform/src/Element/WebformBeeReservationForm.php
modules/bee_webform/src/Element/WebformBeeReservationForm.php
<?php
namespace Drupal\bee_webform\Element;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\NodeType;
use Drupal\webform\Element\WebformCompositeBase;
use RRule\RRule;
/**
* BEE reservation form.
*
* @FormElement("webform_bee_reservation_form")
*/
class WebformBeeReservationForm extends WebformCompositeBase {
/**
* {@inheritdoc}
*/
public static function getCompositeElements(array $element) {
$elements = [];
if (isset($element['#content_types'])) {
$bookable_type = FALSE;
$content_type_options = [];
foreach (array_filter($element['#content_types']) as $node_type) {
$node_type = NodeType::load($node_type);
$content_type_options[$node_type->id()] = $node_type->label();
assert($node_type instanceof NodeType);
$bee_settings = $node_type->getThirdPartySetting('bee', 'bee');
if (isset($bee_settings['bookable_type'])) {
$bookable_type = $bee_settings['bookable_type'];
}
}
if ($content_type_options) {
$elements['start_date'] = [
'#type' => ($bookable_type == 'daily') ? 'date' : 'datetime',
'#title' => t('Start date'),
];
$elements['end_date'] = [
'#type' => ($bookable_type == 'daily') ? 'date' : 'datetime',
'#title' => t('End date'),
];
if ($element['#collect_capacity']) {
$elements['capacity'] = [
'#type' => 'number',
'#title' => t('Capacity'),
];
}
$elements['repeat'] = [
'#type' => 'checkbox',
'#title' => t('This booking repeats'),
'#prefix' => '<div class="form-row">',
];
$elements['repeat_frequency'] = [
'#type' => 'select',
'#title' => t('Repeat frequency'),
'#options' => [
'daily' => t('Daily'),
'weekly' => t('Weekly'),
'monthly' => t('Monthly'),
],
'#states' => [
'visible' => [
':input[name="bee[repeat]"]' => ['checked' => TRUE],
],
],
];
$elements['repeat_until'] = [
'#type' => 'date',
'#title' => t('Repeat until'),
'#states' => [
'visible' => [
':input[name="bee[repeat]"]' => ['checked' => TRUE],
],
],
'#suffix' => '</div>',
];
$elements['check_available_nodes'] = [
'#type' => 'submit',
'#value' => t('Check for available nodes'),
'#prefix' => '<div>',
'#suffix' => '</div>',
'#submit' => [get_called_class(), 'ajaxCallback'],
'#limit_validation_errors' => [
[$element['#parents'][0]],
],
'#ajax' => [
'callback' => [get_called_class(), 'ajaxCallback'],
'wrapper' => 'bee-node-wrapper',
],
];
$elements['node'] = [
'#type' => 'select',
'#title' => t('Node'),
'#options' => [],
'#prefix' => '<div id="bee-node-wrapper">',
'#suffix' => '</div>',
'#disabled' => TRUE,
'#empty_option' => '',
];
$elements['#attached']['library'][] = 'bee/bee_form';
}
}
return $elements;
}
/**
* {@inheritdoc}
*/
public static function validateWebformComposite(&$element, FormStateInterface $form_state, &$complete_form) {
$key = $element['#parents'][0];
$triggeringElement = $form_state->getTriggeringElement();
$value = NestedArray::getValue($form_state->getValues(), $element['#parents']);
$capacity = (isset($value['capacity'])) ? $value['capacity'] : 1;
if ($triggeringElement['#parents'][0] == 'submit') {
if (!$value['node']) {
$form_state->setErrorByName($key . '][node', t('Select a Node before submitting'));
}
if ($value['node'] && $value['start_date'] && $value['end_date']) {
$units = bee_webform_get_available_units($value);
$available_units = count($units);
if ($available_units < $capacity) {
$form_state->setErrorByName($key . '][node', t('Unfortunately, not enough units of this type are available'));
}
}
}
elseif ($triggeringElement['#parents'][0] == $key) {
if (!$value['start_date']) {
$form_state->setErrorByName($key . '][start_date', t('Enter a value for the Start date field'));
}
if (!$value['end_date']) {
$form_state->setErrorByName($key . '][end_date', t('Enter a value for the End date field'));
}
if (isset($value['capacity']) && !$value['capacity']) {
$form_state->setErrorByName($key . '][capacity', t('Enter a value for the Capacity field'));
}
if ($value['repeat']) {
if (!$value['repeat_frequency']) {
$form_state->setErrorByName($key . '][repeat_frequency', t('Enter a value for the Repeat frequency field'));
}
if (!$value['repeat_until']) {
$form_state->setErrorByName($key . '][repeat_until', t('Enter a value for the Repeat until field'));
}
}
}
}
/**
* A method for ajax callback.
*/
public static function ajaxCallback($form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
$key = $element['#parents'][0];
$values = $form_state->getValue($key);
if (!empty($values['start_date']) && !empty($values['end_date']) &&
(!isset($values['capacity']) || (isset($values['capacity']) && !empty($values['capacity'])))) {
$content_types = array_filter($form['elements'][$key]['#content_types']);
$capacity = (isset($values['capacity'])) ? $values['capacity'] : 1;
$start_date = new \DateTime($values['start_date']);
$end_date = new \DateTime($values['end_date']);
$end_date->sub(new \DateInterval('PT1M'));
$repeat = $values['repeat'];
$repeat_until = $values['repeat_until'];
$repeat_frequency = $values['repeat_frequency'];
$node_options = self::getAvailableNodes($content_types, $start_date, $end_date, $capacity, $repeat, $repeat_until, $repeat_frequency);
if (empty($node_options)) {
\Drupal::messenger()->addError('There are no available nodes for the selected date/time(s). Please change your selections and try again.');
$form['elements'][$key]['#webform_composite_elements']['node']['#options'] = [];
$form['elements'][$key]['#webform_composite_elements']['node']['#attributes'] = [
'disabled' => TRUE,
];
}
else {
$node_options = ['' => t('- Select -')] + $node_options;
$form['elements'][$key]['#webform_composite_elements']['node']['#options'] = $node_options;
}
}
else {
$form['elements'][$key]['#webform_composite_elements']['node']['#options'] = [];
$form['elements'][$key]['#webform_composite_elements']['node']['#attributes'] = [
'disabled' => TRUE,
];
}
$form['elements'][$key]['#webform_composite_elements']['node']['#prefix'] = '<div id="bee-node-wrapper">';
$form['elements'][$key]['#webform_composite_elements']['node']['#suffix'] = '</div>';
$form['elements'][$key]['#webform_composite_elements']['node']['#name'] = $key . '[node]';
return $form['elements'][$key]['#webform_composite_elements']['node'];
}
/**
* A method to get Available nodes.
*/
private static function getAvailableNodes($content_types, $start_date, $end_date, $capacity, $repeat, $repeat_until, $repeat_frequency) {
$node_options = [];
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
$query = \Drupal::entityQuery('node')
->condition('type', array_keys($content_types), 'IN');
$nids = $query->execute();
foreach ($node_storage->loadMultiple($nids) as $node) {
$node_type = NodeType::load($node->bundle());
assert($node_type instanceof NodeType);
$bee_settings = $node_type->getThirdPartySetting('bee', 'bee');
$drupal_units = [];
foreach ($node->get('field_availability_' . $bee_settings['bookable_type']) as $unit) {
$drupal_units[$unit->entity->id()] = $unit->entity;
}
if ($repeat) {
$rrule = new RRule([
'FREQ' => strtoupper($repeat_frequency),
'UNTIL' => $repeat_until . 'T235959Z',
'DTSTART' => $start_date,
]);
foreach ($rrule as $occurrence) {
$start = clone($occurrence);
$end = clone($occurrence);
if ($bee_settings['bookable_type'] == 'daily') {
$end->add($start_date->diff($end_date));
}
else {
$start->setTime($start_date->format('H'), $start_date->format('i'));
$end->setTime($start_date->format('H'), $start_date->format('i'));
$end->add($start_date->diff($end_date));
}
if ($bee_settings['bookable_type'] == 'daily') {
$available_units_ids = bat_event_get_matching_units($start, $end, ['bee_daily_available'], [$bee_settings['type_id']], 'availability_daily', FALSE, $drupal_units);
}
else {
$available_units_ids = bat_event_get_matching_units($start, $end, ['bee_hourly_available'], [$bee_settings['type_id']], 'availability_hourly', FALSE, $drupal_units);
}
if (count($available_units_ids) < $capacity) {
continue 2;
}
$node_options[$node->id()] = $node->label();
}
}
else {
if ($bee_settings['bookable_type'] == 'daily') {
$available_units_ids = bat_event_get_matching_units($start_date, $end_date, ['bee_daily_available'], [$bee_settings['type_id']], 'availability_daily', FALSE, $drupal_units);
}
else {
$available_units_ids = bat_event_get_matching_units($start_date, $end_date, ['bee_hourly_available'], [$bee_settings['type_id']], 'availability_hourly', FALSE, $drupal_units);
}
if (count($available_units_ids) >= $capacity) {
$node_options[$node->id()] = $node->label();
}
}
}
return $node_options;
}
}
