contacts_events-8.x-1.x-dev/src/Entity/EventClass.php
src/Entity/EventClass.php
<?php
namespace Drupal\contacts_events\Entity;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\Component\Datetime\DateTimePlus;
use Drupal\Core\Config\Entity\ConfigEntityBase;
/**
* Defines the Class entity.
*
* @ConfigEntityType(
* id = "contacts_events_class",
* label = @Translation("Class"),
* handlers = {
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\contacts_events\EventClassListBuilder",
* "form" = {
* "add" = "Drupal\contacts_events\Form\EventClassForm",
* "edit" = "Drupal\contacts_events\Form\EventClassForm",
* "delete" = "Drupal\contacts_events\Form\EventClassDeleteForm"
* },
* "route_provider" = {
* "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
* },
* },
* config_prefix = "contacts_events_class",
* admin_permission = "administer site configuration",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid",
* "weight" = "weight"
* },
* config_export = {
* "id",
* "label",
* "type",
* "weight",
* "selectable",
* "min_age",
* "max_age",
* "bypass_age_check_on_empty_dob"
* },
* links = {
* "canonical" = "/admin/structure/event-classes/{contacts_events_class}",
* "add-form" = "/admin/structure/event-classes/add",
* "edit-form" = "/admin/structure/event-classes/{contacts_events_class}/edit",
* "delete-form" = "/admin/structure/event-classes/{contacts_events_class}/delete",
* "collection" = "/admin/structure/event-classes"
* }
* )
*/
class EventClass extends ConfigEntityBase implements EventClassInterface {
/**
* The unique ID of the event class.
*
* @var string
*/
protected $id;
/**
* The label of the event class.
*
* @var string
*/
protected $label;
/**
* The type this class is for.
*
* @var string
*/
protected $type;
/**
* The weight of this class.
*
* @var int
*/
protected $weight;
/**
* Whether this class is selectable.
*
* @var bool
*/
protected $selectable;
/**
* Minimum age for calculation (inclusive).
*
* @var bool
*/
protected $min_age;
/**
* Maximum age for calculation (inclusive).
*
* @var bool
*/
protected $max_age;
/**
* Bypass the age check when date of birth is empty.
*
* @var bool
*/
protected $bypass_age_check_on_empty_dob;
/**
* {@inheritdoc}
*/
public function evaluate(OrderItemInterface $order_item) {
// Check that we are using the right order type.
if ($this->type != 'global' && $this->type != $order_item->bundle()) {
return FALSE;
}
$implementations = \Drupal::moduleHandler()
->getImplementations('contacts_events_class_evaluate');
foreach ($implementations as $module) {
$function = $module . '_contacts_events_class_evaluate';
$result = $function($order_item, $this);
if ($result === TRUE || $result === FALSE) {
return $result;
}
}
if ($this->type == 'contacts_ticket') {
return $this->evaluateTicketHolderAge($order_item);
}
return TRUE;
}
/**
* Evaluates whether the event class applies based on age calculation.
*
* @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
* The current order item.
*
* @return bool
* True if the ticketholder is within the class's age range, otherwise
* False.
*/
protected function evaluateTicketHolderAge(OrderItemInterface $order_item) : bool {
// If no min/max age specified, immediately allow.
if ($this->min_age === NULL && $this->max_age === NULL) {
return TRUE;
}
/** @var \Drupal\contacts_events\Entity\Ticket $ticket */
// Don't use $order_item->getPurchasedEntity() or it can end up with a
// cached version of the ticket, rather than the updated version.
$ticket = $order_item->get('purchased_entity')->entity;
if (!$ticket) {
throw new \Exception("Order item {$order_item->id()} has no ticket.");
}
/** @var \Drupal\contacts_events\Entity\Event $event */
$event = $ticket->getEvent();
/** @var \Drupal\Core\Datetime\DrupalDateTime $comparison_date */
$comparison_date = NULL;
// If the event has a class_calculation_date, use that.
if ($event->hasField('class_calculation_date')) {
if (!$event->get('class_calculation_date')->isEmpty()) {
$comparison_date = $event->get('class_calculation_date')->date;
}
}
// Otherwise use the event start date.
if ($comparison_date === NULL) {
$comparison_date = $event->get('date')->start_date;
}
// Check the ticket holder has a DOB.
$dob = $ticket->get('date_of_birth')->date;
if (!$dob) {
if ($this->bypass_age_check_on_empty_dob) {
return TRUE;
}
return FALSE;
}
// Calculate age as of the comparison date.
$age = static::calculateAge($dob, $comparison_date);
// Only do the min age comparison if it's been specified.
// Age range is inclusive.
if ($this->min_age !== NULL && $age < $this->min_age) {
return FALSE;
}
// Only do the max age comparison if it's been specified.
// Age range is inclusive.
if ($this->max_age !== NULL && $age > $this->max_age) {
return FALSE;
}
return TRUE;
}
/**
* Calculates age.
*
* @param \Drupal\Component\Datetime\DateTimePlus $dob
* Date of birth.
* @param \Drupal\Component\Datetime\DateTimePlus $comparison_date
* Date to calculate age at.
*
* @return int
* Age in integer years.
*/
public static function calculateAge(DateTimePlus $dob, DateTimePlus $comparison_date): int {
// Clone the dates so we don't modify the originals as DateTimePlus
// uses \DateTime rather than \DateTimeImmutable under the covers.
$dob = clone $dob;
$comparison_date = clone $comparison_date;
// Set both times to midday.
$dob->setDefaultDateTime();
$comparison_date->setDefaultDateTime();
$diff = $dob->diff($comparison_date);
return $diff->y;
}
/**
* Find the appropriate class for an order item.
*
* @param \Drupal\contacts_events\Entity\EventClassInterface[] $classes
* An array of classes.
* @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
* The order item to check.
*
* @return \Drupal\contacts_events\Entity\EventClassInterface|null
* The matching class or NULL if there is no match.
*/
public static function findClass(array $classes, OrderItemInterface $order_item) {
// Ensure our items are sorted.
uasort($classes, [static::class, 'sort']);
// Loop over until we find a match.
foreach ($classes as $class) {
if ($class->evaluate($order_item)) {
return $class;
}
}
}
}
