class DateRecurOccurrences in Recurring Dates Field 3.0.x
Same name and namespace in other branches
- 8.2 src/DateRecurOccurrences.php \Drupal\date_recur\DateRecurOccurrences
- 3.x src/DateRecurOccurrences.php \Drupal\date_recur\DateRecurOccurrences
- 3.1.x src/DateRecurOccurrences.php \Drupal\date_recur\DateRecurOccurrences
Manages occurrences tables and the data that populates them.
- Generates occurrences for tables when entities are modified.
- Manages tables when base or attached date recur fields are created, modified or deleted.
Hierarchy
- class \Drupal\date_recur\DateRecurOccurrences implements \Symfony\Component\EventDispatcher\EventSubscriberInterface, EntityTypeListenerInterface, FieldStorageDefinitionListenerInterface uses EntityTypeEventSubscriberTrait, FieldStorageDefinitionEventSubscriberTrait
Expanded class hierarchy of DateRecurOccurrences
5 files declare their use of DateRecurOccurrences
- DateRecurFilter.php in src/
Plugin/ views/ filter/ DateRecurFilter.php - DateRecurOccurrenceTableSchemaTest.php in tests/
src/ Kernel/ DateRecurOccurrenceTableSchemaTest.php - DateRecurOccurrenceTableTest.php in tests/
src/ Kernel/ DateRecurOccurrenceTableTest.php - DateRecurSubFieldTest.php in tests/
src/ Kernel/ DateRecurSubFieldTest.php - DateRecurViewsFieldTest.php in tests/
src/ Kernel/ DateRecurViewsFieldTest.php
1 string reference to 'DateRecurOccurrences'
1 service uses DateRecurOccurrences
File
- src/
DateRecurOccurrences.php, line 32
Namespace
Drupal\date_recurView source
class DateRecurOccurrences implements EventSubscriberInterface, EntityTypeListenerInterface, FieldStorageDefinitionListenerInterface {
use EntityTypeEventSubscriberTrait;
use FieldStorageDefinitionEventSubscriberTrait;
/**
* The key in field definitions indicating whether field is date recur like.
*/
public const IS_DATE_RECUR = 'is_date_recur';
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* Manages data type plugins.
*
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
*/
protected $typedDataManager;
/**
* DateRecurOccurrences constructor.
*
* @param \Drupal\Core\Database\Connection $database
* The database connection.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
* The entity field manager.
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typedDataManager
* Manages data type plugins.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
*/
public function __construct(Connection $database, EntityFieldManagerInterface $entityFieldManager, TypedDataManagerInterface $typedDataManager, EntityTypeManagerInterface $entityTypeManager) {
$this->database = $database;
$this->entityTypeManager = $entityTypeManager;
$this->entityFieldManager = $entityFieldManager;
$this->typedDataManager = $typedDataManager;
}
/**
* Respond to a field value insertion or update.
*
* @param \Drupal\date_recur\Event\DateRecurValueEvent $event
* The date recur event.
*/
public function onSave(DateRecurValueEvent $event) : void {
/** @var \Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem[]|\Drupal\date_recur\Plugin\Field\FieldType\DateRecurFieldItemList $list */
$list = $event
->getField();
$fieldDefinition = $list
->getFieldDefinition();
$tableName = static::getOccurrenceCacheStorageTableName($fieldDefinition
->getFieldStorageDefinition());
$isInsert = $event
->isInsert();
if (!$isInsert) {
// Delete all existing values for entity and field combination.
/** @var string|int $entityId */
$entityId = $list
->getEntity()
->id();
$this->database
->delete($tableName)
->condition('entity_id', (string) $entityId)
->execute();
}
foreach ($list as $item) {
$this
->saveItem($item, $tableName);
}
}
/**
* Create table rows from occurrences for a single field value.
*
* @param \Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem $item
* Date recur field item.
* @param string $tableName
* The name of table to store occurrences.
*/
protected function saveItem(DateRecurItem $item, string $tableName) : void {
// Type suggested, see https://www.drupal.org/project/drupal/issues/3094067.
/** @var string|int $fieldDelta */
$fieldDelta = $item
->getName();
assert(is_int($fieldDelta));
$fieldName = $item
->getFieldDefinition()
->getName();
$entity = $item
->getEntity();
$fields = [
'entity_id',
'field_delta',
'delta',
$fieldName . '_value',
$fieldName . '_end_value',
];
$baseRow = [
'entity_id' => $entity
->id(),
'field_delta' => $fieldDelta,
];
if ($entity
->getEntityType()
->isRevisionable() && $entity instanceof RevisionableInterface) {
$fields[] = 'revision_id';
$baseRow['revision_id'] = $entity
->getRevisionId();
}
$occurrences = $this
->getOccurrencesForCacheStorage($item);
$rows = array_map(function (DateRange $occurrence, $delta) use ($baseRow, $fieldName, $item) : array {
$row = $baseRow;
$row['delta'] = $delta;
$row[$fieldName . '_value'] = $this
->massageDateValueForStorage($occurrence
->getStart(), $item);
$row[$fieldName . '_end_value'] = $this
->massageDateValueForStorage($occurrence
->getEnd(), $item);
return $row;
}, $occurrences, array_keys($occurrences));
$insert = $this->database
->insert($tableName)
->fields($fields);
foreach ($rows as $row) {
$insert
->values($row);
}
$insert
->execute();
}
/**
* Respond to a entity deletion.
*
* @param \Drupal\date_recur\Event\DateRecurValueEvent $event
* The date recur event.
*/
public function onEntityDelete(DateRecurValueEvent $event) : void {
$list = $event
->getField();
$fieldDefinition = $list
->getFieldDefinition();
$tableName = static::getOccurrenceCacheStorageTableName($fieldDefinition
->getFieldStorageDefinition());
$delete = $this->database
->delete($tableName);
/** @var string|int $entityId */
$entityId = $list
->getEntity()
->id();
$delete
->condition('entity_id', (string) $entityId);
$delete
->execute();
}
/**
* Respond to a entity revision deletion.
*
* @param \Drupal\date_recur\Event\DateRecurValueEvent $event
* The date recur event.
*/
public function onEntityRevisionDelete(DateRecurValueEvent $event) : void {
$list = $event
->getField();
$entity = $list
->getEntity();
$fieldDefinition = $list
->getFieldDefinition();
$tableName = static::getOccurrenceCacheStorageTableName($fieldDefinition
->getFieldStorageDefinition());
$delete = $this->database
->delete($tableName);
/** @var string|int $entityId */
$entityId = $list
->getEntity()
->id();
$delete
->condition('entity_id', (string) $entityId);
if ($entity
->getEntityType()
->isRevisionable() && $entity instanceof RevisionableInterface) {
$delete
->condition('revision_id', $entity
->getRevisionId());
}
$delete
->execute();
}
/**
* {@inheritdoc}
*/
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $fieldStorageConfig) : void {
if ($this
->isDateRecur($fieldStorageConfig)) {
$this
->fieldStorageCreate($fieldStorageConfig);
}
}
/**
* {@inheritdoc}
*/
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $fieldStorageConfig) : void {
if ($this
->isDateRecur($fieldStorageConfig)) {
$this
->fieldStorageDelete($fieldStorageConfig);
}
}
/**
* {@inheritdoc}
*/
public function onEntityTypeCreate(EntityTypeInterface $entity_type) : void {
if (!$entity_type instanceof ContentEntityTypeInterface) {
// Only add field for content entity types.
return;
}
foreach ($this
->getBaseFieldStorages($entity_type) as $baseFieldStorage) {
$this
->fieldStorageCreate($baseFieldStorage);
}
}
/**
* {@inheritdoc}
*/
public function onEntityTypeDelete(EntityTypeInterface $entity_type) : void {
if (!$entity_type instanceof ContentEntityTypeInterface) {
// Only delete field for content entity types.
return;
}
foreach ($this
->getBaseFieldStorages($entity_type) as $baseFieldStorage) {
$this
->fieldStorageDelete($baseFieldStorage);
}
}
/**
* Reacts to field creation.
*/
protected function fieldStorageCreate(FieldStorageDefinitionInterface $fieldDefinition) : void {
$this
->createOccurrenceTable($fieldDefinition);
}
/**
* Reacts to field deletion.
*/
protected function fieldStorageDelete(FieldStorageDefinitionInterface $fieldDefinition) : void {
$tableName = static::getOccurrenceCacheStorageTableName($fieldDefinition);
$this->database
->schema()
->dropTable($tableName);
}
/**
* Get all occurrences needing to be stored.
*
* @param \Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem $item
* The date recur field item.
*
* @return \Drupal\date_recur\DateRange[]
* Date range objects for storage.
*/
protected function getOccurrencesForCacheStorage(DateRecurItem $item) : array {
$until = NULL;
if ($item
->getHelper()
->isInfinite()) {
$until = (new \DateTime('now'))
->add(new \DateInterval($item
->getFieldDefinition()
->getSetting('precreate')));
}
return $item
->getHelper()
->getOccurrences(NULL, $until);
}
/**
* Creates an occurrence table.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $fieldDefinition
* The field definition.
*/
protected function createOccurrenceTable(FieldStorageDefinitionInterface $fieldDefinition) : void {
$entityTypeId = $fieldDefinition
->getTargetEntityTypeId();
$entityType = $this->entityTypeManager
->getDefinition($entityTypeId);
$fieldName = $fieldDefinition
->getName();
$entityFieldDefinitions = $this->entityFieldManager
->getFieldStorageDefinitions($entityTypeId);
// Logic taken from field tables: see \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getDedicatedTableSchema.
$idDefinition = $entityFieldDefinitions[$entityType
->getKey('id')];
if ($idDefinition
->getType() === 'integer') {
$fields['entity_id'] = [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity id this data is attached to',
];
}
else {
$fields['entity_id'] = [
'type' => 'varchar_ascii',
'length' => 128,
'not null' => TRUE,
'description' => 'The entity id this data is attached to',
];
}
if ($entityType
->isRevisionable()) {
$revisionDefinition = $entityFieldDefinitions[$entityType
->getKey('revision')];
if ($revisionDefinition
->getType() === 'integer') {
$fields['revision_id'] = [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The entity revision id this data is attached to',
];
}
else {
$fields['revision_id'] = [
'type' => 'varchar_ascii',
'length' => 128,
'not null' => TRUE,
'description' => 'The entity revision id this data is attached to',
];
}
}
$fields['field_delta'] = [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The sequence number for this data item, used for multi-value fields',
];
$fields['delta'] = [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'The sequence number in generated occurrences for the RRULE',
];
$fieldSchema = $fieldDefinition
->getSchema();
$fields[$fieldName . '_value'] = $fieldSchema['columns']['value'];
$fields[$fieldName . '_end_value'] = $fieldSchema['columns']['end_value'];
$schema = [
'description' => sprintf('Occurrences cache for %s.%s', $fieldDefinition
->getTargetEntityTypeId(), $fieldName),
'fields' => $fields,
'indexes' => [
'value' => [
'entity_id',
$fieldName . '_value',
],
],
];
$tableName = DateRecurOccurrences::getOccurrenceCacheStorageTableName($fieldDefinition);
$this->database
->schema()
->createTable($tableName, $schema);
}
/**
* Convert date ready to be inserted into database column.
*
* @param \DateTimeInterface $date
* A date time object.
* @param \Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem $item
* The date recur field item.
*
* @return string
* The date value for storage.
*/
protected function massageDateValueForStorage(\DateTimeInterface $date, DateRecurItem $item) : string {
// Convert native timezone to UTC.
$date
->setTimezone(new \DateTimeZone(DateRecurItem::STORAGE_TIMEZONE));
// If storage does not allow time, then reset to midday.
$storageFormat = $item
->getDateStorageFormat();
if ($storageFormat == DateRecurItem::DATE_STORAGE_FORMAT) {
$date
->setTime(12, 0, 0);
}
return $date
->format($storageFormat);
}
/**
* Determines if a field is date recur or subclasses date recur.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $fieldDefinition
* A field definition.
*
* @return bool
* Whether field is date recur or subclasses date recur.
*/
protected function isDateRecur(FieldStorageDefinitionInterface $fieldDefinition) : bool {
$typeDefinition = $this->typedDataManager
->getDefinition('field_item:' . $fieldDefinition
->getType());
// @see \Drupal\date_recur\DateRecurCachedHooks::fieldInfoAlter
return isset($typeDefinition[DateRecurOccurrences::IS_DATE_RECUR]);
}
/**
* Get field storage for date recur base fields for an entity type.
*
* @param \Drupal\Core\Entity\ContentEntityTypeInterface $entityType
* An entity type.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
* An array of storage definitions for base fields for an entity type.
*/
protected function getBaseFieldStorages(ContentEntityTypeInterface $entityType) : array {
$baseFields = $this->entityFieldManager
->getBaseFieldDefinitions($entityType
->id());
$baseFields = array_filter($baseFields, function (FieldDefinitionInterface $fieldDefinition) : bool {
return $this
->isDateRecur($fieldDefinition
->getFieldStorageDefinition());
});
return array_map(function (FieldDefinitionInterface $baseField) : FieldStorageDefinitionInterface {
return $baseField
->getFieldStorageDefinition();
}, $baseFields);
}
/**
* Get the name of the table containing occurrences for a field.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $fieldDefinition
* The field definition.
*
* @return string
* A table name.
*/
public static function getOccurrenceCacheStorageTableName(FieldStorageDefinitionInterface $fieldDefinition) : string {
return sprintf('date_recur__%s__%s', $fieldDefinition
->getTargetEntityTypeId(), $fieldDefinition
->getName());
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() : array {
return [
DateRecurEvents::FIELD_VALUE_SAVE => [
'onSave',
],
DateRecurEvents::FIELD_ENTITY_DELETE => [
'onEntityDelete',
],
DateRecurEvents::FIELD_REVISION_DELETE => [
'onEntityRevisionDelete',
],
] + static::getEntityTypeEvents() + static::getFieldStorageDefinitionEvents();
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DateRecurOccurrences:: |
protected | property | The database connection. | |
DateRecurOccurrences:: |
protected | property | The entity field manager. | |
DateRecurOccurrences:: |
protected | property | The entity type manager. | |
DateRecurOccurrences:: |
protected | property | Manages data type plugins. | |
DateRecurOccurrences:: |
protected | function | Creates an occurrence table. | |
DateRecurOccurrences:: |
protected | function | Reacts to field creation. | |
DateRecurOccurrences:: |
protected | function | Reacts to field deletion. | |
DateRecurOccurrences:: |
protected | function | Get field storage for date recur base fields for an entity type. | |
DateRecurOccurrences:: |
public static | function | Get the name of the table containing occurrences for a field. | |
DateRecurOccurrences:: |
protected | function | Get all occurrences needing to be stored. | |
DateRecurOccurrences:: |
public static | function | ||
DateRecurOccurrences:: |
protected | function | Determines if a field is date recur or subclasses date recur. | |
DateRecurOccurrences:: |
public | constant | The key in field definitions indicating whether field is date recur like. | |
DateRecurOccurrences:: |
protected | function | Convert date ready to be inserted into database column. | |
DateRecurOccurrences:: |
public | function | Respond to a entity deletion. | |
DateRecurOccurrences:: |
public | function | Respond to a entity revision deletion. | |
DateRecurOccurrences:: |
public | function |
Reacts to the creation of the entity type. Overrides EntityTypeEventSubscriberTrait:: |
|
DateRecurOccurrences:: |
public | function |
Reacts to the deletion of the entity type. Overrides EntityTypeEventSubscriberTrait:: |
|
DateRecurOccurrences:: |
public | function |
Reacts to the creation of a field storage definition. Overrides FieldStorageDefinitionEventSubscriberTrait:: |
|
DateRecurOccurrences:: |
public | function |
Reacts to the deletion of a field storage definition. Overrides FieldStorageDefinitionEventSubscriberTrait:: |
|
DateRecurOccurrences:: |
public | function | Respond to a field value insertion or update. | |
DateRecurOccurrences:: |
protected | function | Create table rows from occurrences for a single field value. | |
DateRecurOccurrences:: |
public | function | DateRecurOccurrences constructor. | |
EntityTypeEventSubscriberTrait:: |
public static | function | Gets the subscribed events. | |
EntityTypeEventSubscriberTrait:: |
public | function | Listener method for any entity type definition event. | |
EntityTypeEventSubscriberTrait:: |
public | function | 4 | |
EntityTypeEventSubscriberTrait:: |
public | function | 2 | |
EntityTypeEventSubscriberTrait:: |
public | function | 2 | |
FieldStorageDefinitionEventSubscriberTrait:: |
public static | function | Returns the subscribed events. | |
FieldStorageDefinitionEventSubscriberTrait:: |
public | function | Listener method for any field storage definition event. | |
FieldStorageDefinitionEventSubscriberTrait:: |
public | function | 1 |