Browse Source

added caching

main
astanley 1 week ago
parent
commit
077f42a5a5
  1. 4
      README.md
  2. 96
      src/Controller/ZombieReferenceHunterController.php
  3. 78
      src/Service/ZombieReferenceHunterQuery.php
  4. 6
      zombie_reference_hunter.links.menu.yml
  5. 3
      zombie_reference_hunter.routing.yml
  6. 1
      zombie_reference_hunter.services.yml

4
README.md

@ -12,8 +12,8 @@ it's easy to forget that they [don't work with Term Merge](https://www.drupal.or
# Usage
1. install
2. Go to `/admin/zombies` to see the list of problems.
3. Fix the broken links manually.
2. Go to `/admin/reports` and select `Zombie Reference Hunter`.
3. Click on the Source ID to navigate to the edit page.
# Who to Blame
Rosie Le Faive (rlefaive@upei.ca) and the human behind / the AI Bot that made the original.

96
src/Controller/ZombieReferenceHunterController.php

@ -8,6 +8,10 @@ use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Session\AccountInterface;
use Drupal\zombie_reference_hunter\Service\ZombieReferenceHunterQuery;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Creating a controller.
@ -42,30 +46,108 @@ class ZombieReferenceHunterController extends ControllerBase
* Returns the Report page.
*
*/
public function view(): array
public function view(Request $request): array|RedirectResponse
{
$issues = $this->query->scan();
// Force a new scan when requested.
if ($request->query->getBoolean('rescan')) {
$this->query->rebuildCache();
// Redirect back to the clean report URL so refreshing the browser
// doesn't perform another scan.
return new RedirectResponse(
Url::fromRoute('zombie_reference_hunter.prepare')->toString()
);
}
// This now returns cached results unless no cache exists yet.
$issues = $this->query->getIssues();
$count = count($issues);
$build = [
'#cache' => ['max-age' => 0],
];
$build['summary'] = [
'#markup' => $this->t('Found @count broken reference(s).', ['@count' => $count]),
'#type' => 'container',
];
$build['summary']['count'] = [
'#markup' => $this->formatPlural(
$count,
'Found 1 broken reference.',
'Found @count broken references.',
),
];
// Rescan button.
$build['summary']['rescan'] = [
'#type' => 'link',
'#title' => $this->t('Rescan'),
'#url' => Url::fromRoute(
'zombie_reference_hunter.prepare',
[],
[
'query' => [
'rescan' => 1,
],
]
),
'#attributes' => [
'class' => [
'button',
'button--primary',
],
'style' => 'margin-left: 1em;',
],
];
if ($count === 0) {
$build['empty'] = [
'#markup' => $this->t('No broken references found.'),
];
return $build;
}
// Pager.
$limit = 50;
$pager = \Drupal::service('pager.manager')
->createPager($count, $limit);
$current_page = $pager->getCurrentPage();
// Only build rows for the current page.
$issues = array_slice(
$issues,
$current_page * $limit,
$limit
);
$rows = [];
foreach ($issues as $issue) {
$entity_type_id = $issue['source_entity_type'];
$entity_type = $this->entityTypeManager()
->getDefinition($entity_type_id);
$source_id = $issue['source_id'];
if ($entity_type->hasLinkTemplate('edit-form')) {
$source_id = Link::fromTextAndUrl(
(string) $issue['source_id'],
Url::fromRoute(
'entity.' . $entity_type_id . '.edit_form',
[
$entity_type_id => $issue['source_id'],
]
)
);
}
$rows[] = [
$issue['source_entity_type'],
$issue['source_id'],
$entity_type_id,
$source_id,
$issue['field_name'],
$issue['field_type'],
$issue['target_type'],
@ -87,6 +169,10 @@ class ZombieReferenceHunterController extends ControllerBase
'#empty' => $this->t('No broken references found.'),
];
$build['pager'] = [
'#type' => 'pager',
];
return $build;
}

78
src/Service/ZombieReferenceHunterQuery.php

@ -7,13 +7,26 @@ use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
class ZombieReferenceHunterQuery {
class ZombieReferenceHunterQuery
{
/**
* Default number of entities loaded per batch.
*/
const BATCH_SIZE = 200;
/**
* Cache ID for scan results.
*/
const CACHE_ID = 'zombie_reference_hunter.issues';
/**
* Cached scan results.
*/
protected CacheBackendInterface $cache;
/**
* The entity type manager.
*
@ -48,11 +61,13 @@ class ZombieReferenceHunterQuery
public function __construct(
EntityTypeManagerInterface $entityTypeManager,
EntityFieldManagerInterface $entityFieldManager,
LoggerChannelInterface $logger
LoggerChannelInterface $logger,
CacheBackendInterface $cache
) {
$this->entityTypeManager = $entityTypeManager;
$this->entityFieldManager = $entityFieldManager;
$this->logger = $logger;
$this->cache = $cache;
}
/**
@ -63,8 +78,8 @@ class ZombieReferenceHunterQuery
*/
protected function getFieldMaps(): array {
return [
// 'entity_reference' => $this->entityFieldManager->getFieldMapByFieldType('entity_reference'),
// 'entity_reference_revisions' => $this->entityFieldManager->getFieldMapByFieldType('entity_reference_revisions'),
// 'entity_reference' => $this->entityFieldManager->getFieldMapByFieldType('entity_reference'),
// 'entity_reference_revisions' => $this->entityFieldManager->getFieldMapByFieldType('entity_reference_revisions'),
'typed_relation' => $this->entityFieldManager->getFieldMapByFieldType('typed_relation'),
];
}
@ -90,7 +105,7 @@ class ZombieReferenceHunterQuery
$source_storage = $this->entityTypeManager->getStorage($entity_type_id);
$storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id);
}
catch (PluginNotFoundException | InvalidPluginDefinitionException $exception) {
catch (PluginNotFoundException|InvalidPluginDefinitionException $exception) {
$this->logger->warning('Skipping entity type %type due to storage error: %message', [
'%type' => $entity_type_id,
'%message' => $exception->getMessage(),
@ -104,9 +119,9 @@ class ZombieReferenceHunterQuery
}
$field_storage = $storage_definitions[$field_name];
// $field_storage->isComputed() || !$field_storage->isQueryable()) {
// continue;
// }
// $field_storage->isComputed() || !$field_storage->isQueryable()) {
// continue;
// }
$target_type = $field_storage->getSetting('target_type');
if (empty($target_type)) {
@ -116,7 +131,7 @@ class ZombieReferenceHunterQuery
try {
$target_storage = $this->entityTypeManager->getStorage($target_type);
}
catch (PluginNotFoundException | InvalidPluginDefinitionException $exception) {
catch (PluginNotFoundException|InvalidPluginDefinitionException $exception) {
$this->logger->warning('Skipping target type %type due to storage error: %message', [
'%type' => $target_type,
'%message' => $exception->getMessage(),
@ -187,4 +202,47 @@ class ZombieReferenceHunterQuery
return $issues;
}
/**
* Gets the current report results.
*
* Uses cached results when available. If there is no cached scan,
* a new scan is performed automatically.
*
* @return array
* Broken reference records.
*/
public function getIssues(): array {
if ($cache = $this->cache->get(self::CACHE_ID)) {
return $cache->data;
}
return $this->rebuildCache();
}
/**
* Performs a fresh scan and caches the results.
*
* @return array
* Broken reference records.
*/
public function rebuildCache(): array {
$issues = $this->scan();
$this->cache->set(
self::CACHE_ID,
$issues,
Cache::PERMANENT
);
return $issues;
}
/**
* Clears the cached scan.
*/
public function clearCache(): void {
$this->cache->delete(self::CACHE_ID);
}
}

6
zombie_reference_hunter.links.menu.yml

@ -0,0 +1,6 @@
zombie_reference_hunter.prepare:
title: 'Zombie Reference Hunter'
description: 'Find and repair zombie entity references.'
route_name: zombie_reference_hunter.prepare
parent: system.admin_reports
weight: 10

3
zombie_reference_hunter.routing.yml

@ -1,6 +1,7 @@
zombie_reference_hunter.prepare:
path: '/admin/zombies'
path: '/admin/reports/zombies'
defaults:
_controller: '\Drupal\zombie_reference_hunter\Controller\ZombieReferenceHunterController::view'
_title: 'Zombie Reference Hunter'
requirements:
_custom_access: '\Drupal\zombie_reference_hunter\Controller\ZombieReferenceHunterController::access'

1
zombie_reference_hunter.services.yml

@ -6,6 +6,7 @@ services:
- '@entity_type.manager'
- '@entity_field.manager'
- '@logger.channel.zombie_reference_hunter'
- '@cache.data'
logger.channel.zombie_reference_hunter:
parent: logger.channel_base

Loading…
Cancel
Save