generated from SU-SWS/stanford_module_example
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- HSD8-1046 Invalidate entities whos date fields have recently passed (#16)
- Loading branch information
Showing
8 changed files
with
306 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?php | ||
|
||
namespace Drupal\stanford_fields\Service; | ||
|
||
use Drupal\Core\Cache\Cache; | ||
use Drupal\Core\Datetime\DrupalDateTime; | ||
use Drupal\Core\Entity\EntityFieldManagerInterface; | ||
use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
use Drupal\Core\State\StateInterface; | ||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; | ||
|
||
/** | ||
* Cache invalidator based on field values. | ||
* | ||
* @package Drupal\stanford_fields\Service | ||
*/ | ||
class FieldCache implements FieldCacheInterface { | ||
|
||
/** | ||
* Drupal core field manager service. | ||
* | ||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface | ||
*/ | ||
protected $fieldManager; | ||
|
||
/** | ||
* Drupal core entity manager service. | ||
* | ||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||
*/ | ||
protected $entityTypeManager; | ||
|
||
/** | ||
* Drupal core state service. | ||
* | ||
* @var \Drupal\Core\State\StateInterface | ||
*/ | ||
protected $state; | ||
|
||
/** | ||
* FieldCron constructor. | ||
* | ||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager | ||
* Drupal core field manager service. | ||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
* Drupal core entity manager service. | ||
* @param \Drupal\Core\State\StateInterface $state | ||
* Drupal core state service. | ||
*/ | ||
public function __construct(EntityFieldManagerInterface $field_manager, EntityTypeManagerInterface $entity_type_manager, StateInterface $state) { | ||
$this->fieldManager = $field_manager; | ||
$this->entityTypeManager = $entity_type_manager; | ||
$this->state = $state; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function invalidateDateFieldsCache() { | ||
$date_field_types = ['datetime', 'daterange', 'smartdate']; | ||
$cache_tags = []; | ||
|
||
// Loop through all fields to invalidate entities on date fields. | ||
foreach ($this->fieldManager->getFieldMap() as $entity_type => $fields) { | ||
foreach ($fields as $field_name => $field_info) { | ||
|
||
// Only invalidate desired date fields. | ||
if (in_array($field_info['type'], $date_field_types)) { | ||
$tags = $this->getExpiredDateCacheTags($entity_type, $field_name, $field_info['bundles']); | ||
$cache_tags = array_merge($cache_tags, $tags); | ||
} | ||
} | ||
} | ||
|
||
Cache::invalidateTags(array_unique($cache_tags)); | ||
} | ||
|
||
/** | ||
* Use an entity query to check for date fields that have recently passed. | ||
* | ||
* @param string $entity_type | ||
* Entity type machine name. | ||
* @param string $field_name | ||
* Field definition name. | ||
* @param array $bundles | ||
* Array of entity bundle names. | ||
* | ||
* @return int[] | ||
* Keyed array of entity ids. | ||
* | ||
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException | ||
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException | ||
*/ | ||
protected function getExpiredDateCacheTags($entity_type, $field_name, array $bundles = []) { | ||
$entity_storage = $this->entityTypeManager->getStorage($entity_type); | ||
$bundle_key = $this->entityTypeManager->getDefinition($entity_type) | ||
->getKey('bundle'); | ||
|
||
$field_storage = $this->entityTypeManager->getStorage('field_storage_config'); | ||
$now = new DrupalDateTime(); | ||
$last_ran = DrupalDateTime::createFromTimestamp($this->state->get('stanford_fields.dates_cleared', 0)); | ||
|
||
/** @var \Drupal\field\FieldStorageConfigInterface $field_definition */ | ||
$field_definition = $field_storage->load("$entity_type.$field_name"); | ||
$field_date_format = $field_definition->getSetting('datetime_type') == 'date' ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT; | ||
|
||
// Smartdate fields are formatted differently. | ||
if ($field_definition->getType() == 'smartdate') { | ||
$field_date_format = 'U'; | ||
} | ||
|
||
// Query all entities for the given date field that is between the last ran | ||
// time and the current time. | ||
$query = $entity_storage->getQuery() | ||
->accessCheck(FALSE) | ||
->exists($field_name) | ||
->condition($field_name, $now->format($field_date_format), '<=') | ||
->condition($field_name, $last_ran->format($field_date_format), '>='); | ||
|
||
// Some entity types don't have bundles, so don't add the condition if not | ||
// applicable. | ||
if ($bundle_key) { | ||
$query->condition($bundle_key, $bundles, 'IN'); | ||
} | ||
$tags = []; | ||
|
||
// If no entity ids were found, no tags should be invalidated. | ||
if ($entity_ids = $query->execute()) { | ||
$entities = $entity_storage->loadMultiple($entity_ids); | ||
foreach ($entities as $entity) { | ||
$tags = array_merge($tags, $entity->getCacheTagsToInvalidate()); | ||
} | ||
} | ||
return $tags; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace Drupal\stanford_fields\Service; | ||
|
||
/** | ||
* Interface FieldCacheInterface. | ||
* | ||
* @package Drupal\stanford_fields\Service | ||
*/ | ||
interface FieldCacheInterface { | ||
|
||
/** | ||
* Invalidate entities that have date field values that recently passed by. | ||
*/ | ||
public function invalidateDateFieldsCache(); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* stanford_fields.install | ||
*/ | ||
|
||
/** | ||
* Implements hook_uninstall(). | ||
*/ | ||
function stanford_fields_uninstall() { | ||
\Drupal::state()->delete('stanford_fields.dates_cleared'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
services: | ||
stanford_fields.field_cache: | ||
class: 'Drupal\stanford_fields\Service\FieldCache' | ||
arguments: ['@entity_field.manager', '@entity_type.manager', '@state'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?php | ||
|
||
namespace Drupal\Tests\stanford_fields\Kernel\Service; | ||
|
||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface; | ||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; | ||
use Drupal\field\Entity\FieldConfig; | ||
use Drupal\field\Entity\FieldStorageConfig; | ||
use Drupal\KernelTests\KernelTestBase; | ||
use Drupal\node\Entity\Node; | ||
use Drupal\node\Entity\NodeType; | ||
|
||
/** | ||
* Class FieldCacheTest | ||
* | ||
* @package Drupal\Tests\stanford_fields\Kernel\Service | ||
*/ | ||
class FieldCacheTest extends KernelTestBase { | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
protected static $modules = [ | ||
'system', | ||
'stanford_fields', | ||
'node', | ||
'user', | ||
'datetime', | ||
'field', | ||
]; | ||
|
||
/** | ||
* Array of cache tags scheduled for invalidation. | ||
* | ||
* @var array | ||
*/ | ||
protected $invalidatedTags = []; | ||
|
||
/** | ||
* Date field format to save the date as. | ||
* | ||
* @var string | ||
*/ | ||
protected $dateFieldFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT; | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
protected function setUp(): void { | ||
parent::setUp(); | ||
$this->installEntitySchema('user'); | ||
$this->installEntitySchema('node'); | ||
$this->installSchema('node', ['node_access']); | ||
|
||
$cache_invalidator = $this->createMock(CacheTagsInvalidatorInterface::class); | ||
$cache_invalidator->method('invalidateTags') | ||
->will($this->returnCallback([$this, 'invalidateTagsCallback'])); | ||
|
||
NodeType::create(['type' => 'page', 'name' => 'Page'])->save(); | ||
// Create a comment field attached to a host 'entity_test' entity. | ||
FieldStorageConfig::create([ | ||
'entity_type' => 'node', | ||
'type' => 'datetime', | ||
'field_name' => 'field_date', | ||
])->save(); | ||
FieldConfig::create([ | ||
'entity_type' => 'node', | ||
'bundle' => 'page', | ||
'field_name' => 'field_date', | ||
])->save(); | ||
\Drupal::getContainer()->set('cache_tags.invalidator', $cache_invalidator); | ||
} | ||
|
||
/** | ||
* Test that the field value will trigger an invalidation. | ||
*/ | ||
public function testDateInvalidation() { | ||
\Drupal::service('stanford_fields.field_cache') | ||
->invalidateDateFieldsCache(); | ||
$this->assertEmpty($this->invalidatedTags); | ||
|
||
$node = Node::create([ | ||
'type' => 'page', | ||
'title' => 'foo', | ||
'field_date' => date($this->dateFieldFormat, time() + 60 * 60 * 24 * 3), | ||
]); | ||
$node->save(); | ||
|
||
$this->invalidatedTags = []; | ||
\Drupal::service('stanford_fields.field_cache') | ||
->invalidateDateFieldsCache(); | ||
$this->assertEmpty($this->invalidatedTags); | ||
|
||
$node->set('field_date', date($this->dateFieldFormat, time() - 60 * 60 * 24 * 3)) | ||
->save(); | ||
$this->invalidatedTags = []; | ||
\Drupal::service('stanford_fields.field_cache') | ||
->invalidateDateFieldsCache(); | ||
$this->assertTrue(in_array('node:' . $node->id(), $this->invalidatedTags)); | ||
} | ||
|
||
/** | ||
* Run the same tests as above, but with a date time storage. | ||
*/ | ||
public function testDateTimeInvalidation() { | ||
$this->dateFieldFormat = DateTimeItemInterface::DATETIME_STORAGE_FORMAT; | ||
FieldStorageConfig::load('node.field_date') | ||
->setSetting('datetime_type', 'datetime') | ||
->save(); | ||
$this->testDateInvalidation(); | ||
} | ||
|
||
/** | ||
* Cache invalidation callback. | ||
*/ | ||
public function invalidateTagsCallback($tags) { | ||
$this->invalidatedTags = $tags; | ||
} | ||
|
||
} |