Skip to content

Commit

Permalink
Entity Cleanup Cron Queue
Browse files Browse the repository at this point in the history
  • Loading branch information
pookmish committed Nov 30, 2023
1 parent 399a2d3 commit 7efc1f1
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 78 deletions.
6 changes: 6 additions & 0 deletions docroot/modules/humsci/hs_field_helpers/drush.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
hs_field_helpers.commands:
class: \Drupal\hs_field_helpers\Commands\HsFieldHelpersCommands
arguments: ['@queue', '@entity_type.manager']
tags:
- { name: drush.command }
78 changes: 0 additions & 78 deletions docroot/modules/humsci/hs_field_helpers/hs_field_helpers.module
Original file line number Diff line number Diff line change
Expand Up @@ -570,84 +570,6 @@ function _hs_field_helpers_get_view_mode_component(FieldableEntityInterface $ent
}
}

/**
* Implements hook_cron().
*/
function hs_field_helpers_cron() {
if (!Settings::get('hs_field_helpers_cron', FALSE)) {
return;
}
$deleted = _he_field_helpers_clean_eck();
$logger = \Drupal::logger('hs_field_helpers');
foreach ($deleted as $entity_type => $ids) {
$logger->info(t('Deleted %count entities due to no usages. %type: %ids'), [
'%count' => count($ids),
'%type' => $entity_type,
'%ids' => implode(', ', $ids),
]);
}
}

/**
* Deletes any ECK entities that don't have any usage of them anywhere.
*
* With entity reference fields, entity_usage module tracks the use of ECK
* entities. Because of that we can safely delete these entities that have no
* usage statitistics of them. Mostly this is from doing a migration rollback.
* Deleting a node manually seems to clean the ECK entities.
*
* @return array
* Keyed array of entity type to entity ids that were deleted.
*/
function _he_field_helpers_clean_eck() {
/** @var \Drupal\entity_usage\EntityUsage $entity_usage */
$entity_usage = \Drupal::service('entity_usage.usage');
$deleted = [];
foreach (_hs_field_helpers_get_cleanup_entity_types() as $entity_type) {
$entity_storage = \Drupal::entityTypeManager()
->getStorage($entity_type);
$entity_ids = $entity_storage->getQuery()
->accessCheck(FALSE)
->execute();

// Chunk it up so that we don't try to load every single entity at once.
$chuncked_ids = array_chunk($entity_ids, 100);

foreach ($chuncked_ids as $entity_ids) {
$entities = $entity_storage->loadMultiple($entity_ids);

// Delete all ECK entities that aren't being used. Some ECK's don't get
// deleted if they were imported by a migration.
foreach ($entities as $entity) {
if (empty($entity_usage->listSources($entity))) {
$deleted[$entity_type][] = $entity->id();
$entity->delete();
}
}
}
}
return $deleted;
}

/**
* Get the entity types to clean if there are no usages.
*
* @return string[]
* Entity type ids.
*/
function _hs_field_helpers_get_cleanup_entity_types(): array {
$cleanup_types = ['paragraph'];
$definitions = \Drupal::entityTypeManager()->getDefinitions();
/** @var \Drupal\Core\Entity\ContentEntityType $entity_type */
foreach ($definitions as $entity_type_id => $entity_type) {
// Find all the entity types ECK has available.
if ($entity_type->getOriginalClass() == EckEntity::class) {
$cleanup_types[] = $entity_type_id;
}
}
return $cleanup_types;
}

/**
* Implements hook_ENTITY_TYPE_presave().
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Drupal\hs_field_helpers\Commands;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Queue\QueueFactory;
use Drush\Commands\DrushCommands;

/**
* A Drush commandfile.
*/
class HsFieldHelpersCommands extends DrushCommands {

/**
* Command constructor.
*
* @param \Drupal\Core\Queue\QueueFactory $queueFactory
* Queue factory service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* Entity type manager service.
*/
public function __construct(protected QueueFactory $queueFactory, protected EntityTypeManagerInterface $entityTypeManager) {}

/**
* Queues up all entities to delete unusued items.
*
* @command humsci:queue-entity-cleanup
*/
public function queueEntityCleanup() {
$queue = $this->queueFactory->get('entity_cleanup');

foreach ($this->getEntityTypes() as $entity_type) {
$entity_storage = $this->entityTypeManager->getStorage($entity_type);

$entity_ids = $entity_storage->getQuery()
->accessCheck(FALSE)
->execute();

foreach ($entity_ids as $id) {
$queue->createItem([
'entity_type' => $entity_type,
'id' => $id,
]);
}
}
}

/**
* Get the entity types to clean if there are no usages.
*
* @return string[]
* Entity type ids.
*/
function getEntityTypes(): array {
$cleanup_types = ['paragraph', 'hs_entity'];
$definitions = $this->entityTypeManager->getDefinitions();

/** @var \Drupal\Core\Entity\ContentEntityType $entity_type */
foreach ($definitions as $entity_type_id => $entity_type) {
// Find all the entity types ECK has available.
if ($entity_type->getOriginalClass() == 'Drupal\eck\Entity\EckEntity') {
$cleanup_types[] = $entity_type_id;
}
}
return $cleanup_types;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Drupal\hs_field_helpers\Plugin\QueueWorker;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\entity_usage\EntityUsageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Entity clean up queue worker.
*
* @QueueWorker(
* id = "entity_cleanup",
* title = @Translation("Entity Clean Up"),
* cron = {"time" = 60}
* )
*/
class EntityCleanupQueueWorker extends QueueWorkerBase implements ContainerFactoryPluginInterface {

/**
* {@inheritDoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('entity_usage.usage')
);
}

/**
* {@inheritDoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, protected EntityTypeManagerInterface $entityTypeManager, protected EntityUsageInterface $entityUsage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}

/**
* {@inheritDoc}
*/
public function processItem($data) {
if (!$this->entityTypeManager->hasDefinition($data['entity_type'])) {
return;
}
$entity_storage = $this->entityTypeManager->getStorage($data['entity_type']);
$entity = $entity_storage->load($data['id']);

if ($entity && empty($this->entityUsage->listSources($entity))) {
$entity->delete();
}
}

}

0 comments on commit 7efc1f1

Please sign in to comment.