From c2b17faec64aeecc1b41ea957465e1865d6b5054 Mon Sep 17 00:00:00 2001 From: berliner Date: Fri, 28 Jul 2023 13:18:08 +0200 Subject: [PATCH] WIP: Add cache tags to API queries, add cache control section backend page where the API cache for a specific section can be cleared selectively --- config/user.role.administrator.yml | 2 + .../Plugin/EndpointQuery/LocationsQuery.php | 10 +-- .../ghi_plans/src/ApiObjects/Project.php | 2 +- .../EndpointQuery/AttachmentSearchQuery.php | 10 +-- .../src/Plugin/EndpointQuery/ClusterQuery.php | 3 + .../Plugin/EndpointQuery/MeasurementQuery.php | 6 +- .../EndpointQuery/OrganizationQuery.php | 6 +- .../Plugin/EndpointQuery/PlanBasicQuery.php | 7 +- .../EndpointQuery/PlanClusterSummaryQuery.php | 11 ++- .../EndpointQuery/PlanEntitiesQuery.php | 14 ++-- .../EndpointQuery/PlanProjectFundingQuery.php | 4 +- .../EndpointQuery/PlanProjectSearchQuery.php | 38 ++++----- .../EndpointQuery/PlanPrototypeQuery.php | 6 +- .../PlanReportingPeriodsQuery.php | 7 +- .../ghi_sections/ghi_sections.links.task.yml | 4 + .../ghi_sections/ghi_sections.permissions.yml | 4 + .../ghi_sections/ghi_sections.routing.yml | 17 ++++ .../ghi_sections/src/Entity/Section.php | 7 +- .../ghi_sections/src/Form/CacheForm.php | 79 +++++++++++++++++++ .../hpc_api/src/ApiObjects/ApiObjectBase.php | 28 +++++++ .../hpc_api/src/Query/EndpointQuery.php | 16 +++- .../hpc_api/src/Query/EndpointQueryBase.php | 69 +++++++++++++++- .../hpc_api/src/Traits/SimpleCacheTrait.php | 7 +- .../tests/src/Unit/OverrideEndpointQuery.php | 2 +- 24 files changed, 283 insertions(+), 76 deletions(-) create mode 100644 html/modules/custom/ghi_sections/ghi_sections.permissions.yml create mode 100644 html/modules/custom/ghi_sections/src/Form/CacheForm.php diff --git a/config/user.role.administrator.yml b/config/user.role.administrator.yml index dcce1f5d0..171ac8770 100644 --- a/config/user.role.administrator.yml +++ b/config/user.role.administrator.yml @@ -41,6 +41,7 @@ dependencies: - ghi_base_objects - ghi_blocks - ghi_content + - ghi_sections - ghi_teams - ghi_templates - layout_builder @@ -83,6 +84,7 @@ permissions: - 'administer nodes' - 'administer pages' - 'administer permissions' + - 'administer section cache' - 'administer social api authentication' - 'administer social api autoposting' - 'administer social api widgets' diff --git a/html/modules/custom/ghi_base_objects/src/Plugin/EndpointQuery/LocationsQuery.php b/html/modules/custom/ghi_base_objects/src/Plugin/EndpointQuery/LocationsQuery.php index 8c6256a34..890b4fdcd 100644 --- a/html/modules/custom/ghi_base_objects/src/Plugin/EndpointQuery/LocationsQuery.php +++ b/html/modules/custom/ghi_base_objects/src/Plugin/EndpointQuery/LocationsQuery.php @@ -5,7 +5,6 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\ghi_base_objects\ApiObjects\Location; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for locations. @@ -25,7 +24,6 @@ class LocationsQuery extends EndpointQueryBase { use StringTranslationTrait; - use SimpleCacheTrait; const MAX_LEVEL = 5; @@ -71,14 +69,15 @@ public function getCountryLocations($country, $max_level = self::MAX_LEVEL) { 'country_id' => $country->id, 'max_level' => $max_level, ]); - $locations = $this->cache($cache_key); + $locations = $this->getCache($cache_key); if ($locations) { return $locations; } $data = $this->getCountryLocationData($country->id, $max_level); if (empty($data) || empty($data->children) || !is_array($data->children)) { - return $this->cache($cache_key, []); + $this->setCache($cache_key, []); + return []; } // Make it a flat array. @@ -97,7 +96,8 @@ public function getCountryLocations($country, $max_level = self::MAX_LEVEL) { $locations = array_filter($locations, function ($location) { return !empty($location->latLng[0]) && !empty($location->latLng[1]) && $location->admin_level != 0; }); - return $this->cache($cache_key, $locations); + $this->setCache($cache_key, $locations); + return $locations; } /** diff --git a/html/modules/custom/ghi_plans/src/ApiObjects/Project.php b/html/modules/custom/ghi_plans/src/ApiObjects/Project.php index 148f57325..bab473e96 100644 --- a/html/modules/custom/ghi_plans/src/ApiObjects/Project.php +++ b/html/modules/custom/ghi_plans/src/ApiObjects/Project.php @@ -79,7 +79,7 @@ public function getOrganizations() { } $processed_organizations[$organization->id] = new Organization($organization); } - $this->cache($cache_key, $processed_organizations); + $this->cache($cache_key, $processed_organizations, FALSE, NULL, $this->getCacheTags()); return $processed_organizations; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/AttachmentSearchQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/AttachmentSearchQuery.php index 487b9f349..36643e235 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/AttachmentSearchQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/AttachmentSearchQuery.php @@ -6,7 +6,6 @@ use Drupal\ghi_plans\Traits\AttachmentFilterTrait; use Drupal\ghi_plans\Traits\PlanVersionArgument; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for attachment search. @@ -29,7 +28,6 @@ class AttachmentSearchQuery extends EndpointQueryBase { use AttachmentFilterTrait; use PlanVersionArgument; - use SimpleCacheTrait; /** * {@inheritdoc} @@ -61,7 +59,7 @@ public function getAttachmentsById(array $attachment_ids, $disaggregated = FALSE $query_args['disaggregation'] = 'false'; } $cache_key = $this->getCacheKey($query_args); - $attachments = $this->cache($cache_key); + $attachments = $this->getCache($cache_key); if ($attachments) { return $attachments; } @@ -71,7 +69,7 @@ public function getAttachmentsById(array $attachment_ids, $disaggregated = FALSE } $processed_attachments = AttachmentHelper::processAttachments($attachments); - $this->cache($cache_key, $processed_attachments); + $this->setCache($cache_key, $processed_attachments); return $processed_attachments; } @@ -99,7 +97,7 @@ public function getAttachmentsByObject($object_type, $object_ids, array $filter 'object_type' => $object_type, 'object_ids' => $object_ids, ] + (array) $filter); - $attachments = $this->cache($cache_key); + $attachments = $this->getCache($cache_key); if ($attachments) { return $attachments; } @@ -120,7 +118,7 @@ public function getAttachmentsByObject($object_type, $object_ids, array $filter } $processed_attachments = AttachmentHelper::processAttachments($attachments); - $this->cache($cache_key, $processed_attachments); + $this->setCache($cache_key, $processed_attachments); return $processed_attachments; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/ClusterQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/ClusterQuery.php index 8874c0258..e3918dac8 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/ClusterQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/ClusterQuery.php @@ -86,6 +86,9 @@ public function getCluster($plan_id, $cluster_id) { * An array of cluster objects, keyed by the cluster id. */ public function getTaggedClustersForPlan($plan_id, $cluster_tag) { + $this->setCacheTags([ + 'plan_id:' . $plan_id, + ]); $clusters = $this->getData([], [ 'planId' => $plan_id, 'scopes' => 'governingEntityVersion', diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/MeasurementQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/MeasurementQuery.php index 45b458d83..928c7e445 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/MeasurementQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/MeasurementQuery.php @@ -44,8 +44,12 @@ public function getUnprocessedMeasurements(DataAttachment $attachment, $disaggre if (!$disaggregation) { $endpoint_args['disaggregation'] = 'false'; } + $plan_id = $attachment->getPlanId(); + $this->setCacheTags([ + 'plan_id:' . $plan_id, + ]); if ($this->isAutenticatedEndpoint) { - $endpoint_args['version'] = $this->getPlanVersionArgumentForPlanId($attachment->getPlanId()); + $endpoint_args['version'] = $this->getPlanVersionArgumentForPlanId($plan_id); $data = $this->getData([], ['attachmentId' => $attachment->id()] + $endpoint_args); return $data; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/OrganizationQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/OrganizationQuery.php index 7b6638c67..5996d8a33 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/OrganizationQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/OrganizationQuery.php @@ -6,7 +6,6 @@ use Drupal\ghi_plans\ApiObjects\Organization; use Drupal\ghi_plans\Traits\PlanVersionArgument; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for organizations. @@ -23,7 +22,6 @@ class OrganizationQuery extends EndpointQueryBase { use PlanVersionArgument; - use SimpleCacheTrait; use StringTranslationTrait; /** @@ -39,14 +37,14 @@ public function getOrganization($organization_id) { $cache_key = $this->getCacheKey([ 'organization_id' => $organization_id, ]); - $organization = $this->cache($cache_key); + $organization = $this->getCache($cache_key); if ($organization !== NULL) { return $organization; } $data = $this->getData(['organization_id' => $organization_id]); $organization = !empty($data) ? new Organization($data) : NULL; - $this->cache($cache_key, $organization); + $this->setCache($cache_key, $organization); return $organization; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanBasicQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanBasicQuery.php index 86b2dd32e..83df443bf 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanBasicQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanBasicQuery.php @@ -6,7 +6,6 @@ use Drupal\ghi_plans\ApiObjects\Plan; use Drupal\ghi_plans\Traits\PlanVersionArgument; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for basic plan data. @@ -28,7 +27,6 @@ class PlanBasicQuery extends EndpointQueryBase { use PlanVersionArgument; - use SimpleCacheTrait; use StringTranslationTrait; /** @@ -45,14 +43,13 @@ public function getBaseData($plan_id) { 'plan_id' => $plan_id, 'authenticated' => $this->isAutenticatedEndpoint, ]); - $base_data = $this->cache($cache_key); + $base_data = $this->getCache($cache_key); if ($base_data !== NULL) { return $base_data; } $data = $this->getData(['plan_id' => $plan_id], ['version' => $this->getPlanVersionArgumentForPlanId($plan_id)]); $base_data = !empty($data) ? new Plan($data) : FALSE; - - $this->cache($cache_key, $base_data); + $this->setCache($cache_key, $base_data); return $base_data; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanClusterSummaryQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanClusterSummaryQuery.php index 7f0ca3801..60b60c0cd 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanClusterSummaryQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanClusterSummaryQuery.php @@ -3,7 +3,6 @@ namespace Drupal\ghi_plans\Plugin\EndpointQuery; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; use Drupal\hpc_common\Helpers\ArrayHelper; /** @@ -20,19 +19,18 @@ */ class PlanClusterSummaryQuery extends EndpointQueryBase { - use SimpleCacheTrait; - /** * {@inheritdoc} */ public function getData(array $placeholders = [], array $query_args = []) { $cache_key = $this->getCacheKey($this->getPlaceholders() + $placeholders + $query_args); - if ($data = $this->cache($cache_key)) { + if ($data = $this->getCache($cache_key)) { return $data; } $data = parent::getData($placeholders); if (empty($data) || empty($data->objects)) { - return $this->cache($cache_key, []); + $this->setCache($cache_key, []); + return []; } $totals = property_exists($data, 'totals') ? $data->totals : $data; @@ -57,7 +55,8 @@ public function getData(array $placeholders = [], array $query_args = []) { 'total_funding' => $totals->totalFunding, ], ]; - return $this->cache($cache_key, $summary_data); + $this->setCache($cache_key, $summary_data); + return $summary_data; } /** diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanEntitiesQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanEntitiesQuery.php index 0f9a911ea..e5a162d7e 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanEntitiesQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanEntitiesQuery.php @@ -14,7 +14,6 @@ use Drupal\hpc_api\Helpers\ApiEntityHelper; use Drupal\hpc_api\Helpers\ArrayHelper; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for plan entities. @@ -39,7 +38,6 @@ class PlanEntitiesQuery extends EndpointQueryBase { use AttachmentFilterTrait; use PlanVersionArgument; - use SimpleCacheTrait; use StringTranslationTrait; /** @@ -72,7 +70,7 @@ public function getData(array $placeholders = [], array $query_args = []) { */ private function getAttachments(ContentEntityInterface $context_object = NULL, array $filter = []) { $cache_key = $this->getCacheKey(array_filter(['id' => $context_object ? $context_object->id() : NULL] + $filter + $this->getPlaceholders())); - $attachments = $this->cache($cache_key); + $attachments = $this->getCache($cache_key); if ($attachments) { return $attachments; } @@ -165,7 +163,7 @@ private function getAttachments(ContentEntityInterface $context_object = NULL, a if (!empty($filter)) { $attachments = $this->filterAttachments($attachments, $filter); } - $this->cache($cache_key, $attachments); + $this->setCache($cache_key, $attachments); return $attachments; } @@ -291,7 +289,7 @@ public function getPlanEntities(ContentEntityInterface $context_object = NULL, $ 'entity_type' => $entity_type, ] + ($filters ?? []))); - $plan_entities = $this->cache($cache_key); + $plan_entities = $this->getCache($cache_key); if ($plan_entities) { return $plan_entities; } @@ -321,7 +319,7 @@ public function getPlanEntities(ContentEntityInterface $context_object = NULL, $ return $entity->id(); }, $plan_entities); $plan_entities = array_combine($entity_ids, $plan_entities); - $this->cache($cache_key, $plan_entities); + $this->setCache($cache_key, $plan_entities); return $plan_entities; } @@ -336,7 +334,7 @@ public function getPlanEntities(ContentEntityInterface $context_object = NULL, $ */ public function getGoverningEntityIdsForPlanEntityId($plan_entity_id) { $cache_key = $this->getCacheKey(['plan_entity_id' => $plan_entity_id] + $this->getPlaceholders()); - $cluster_ids = $this->cache($cache_key); + $cluster_ids = $this->getCache($cache_key); if ($cluster_ids) { return $cluster_ids; } @@ -362,7 +360,7 @@ public function getGoverningEntityIdsForPlanEntityId($plan_entity_id) { } } } - $this->cache($cache_key, $cluster_ids); + $this->setCache($cache_key, $cluster_ids); return $cluster_ids; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectFundingQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectFundingQuery.php index 5bcb0d5cc..62310f4d0 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectFundingQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectFundingQuery.php @@ -36,7 +36,7 @@ class PlanProjectFundingQuery extends EndpointQueryBase { public function getData(array $placeholders = [], array $query_args = []) { $placeholders = array_merge($placeholders, $this->getPlaceholders()); $cache_key = $this->getCacheKey($placeholders); - if ($cached_data = $this->cache($cache_key)) { + if ($cached_data = $this->getCache($cache_key)) { return $cached_data; } $data = (object) parent::getData($placeholders, $query_args); @@ -65,7 +65,7 @@ public function getData(array $placeholders = [], array $query_args = []) { } $this->data = $funding; - $this->cache($cache_key, $this->data); + $this->setCache($cache_key, $this->data); return $this->data; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectSearchQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectSearchQuery.php index b71ae5680..732101498 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectSearchQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanProjectSearchQuery.php @@ -8,7 +8,6 @@ use Drupal\ghi_plans\Entity\GoverningEntity; use Drupal\ghi_plans\Traits\ProjectTrait; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for project search. @@ -33,7 +32,6 @@ */ class PlanProjectSearchQuery extends EndpointQueryBase { - use SimpleCacheTrait; use ProjectTrait; /** @@ -117,11 +115,11 @@ public function getProjectCount(BaseObjectInterface $base_object = NULL) { $cache_key = $this->getCacheKey(array_filter($this->getCommonCacheKeys() + [ 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', ])); - if ($project_count = $this->cache($cache_key)) { + if ($project_count = $this->getCache($cache_key)) { return $project_count; } $project_count = count($this->getProjects($base_object)); - $this->cache($cache_key, $project_count); + $this->setCache($cache_key, $project_count); return $project_count; } @@ -138,11 +136,11 @@ public function getOrganizationCount(BaseObjectInterface $base_object = NULL) { $cache_key = $this->getCacheKey(array_filter($this->getCommonCacheKeys() + [ 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', ])); - if ($organization_count = $this->cache($cache_key)) { + if ($organization_count = $this->getCache($cache_key)) { return $organization_count; } $organization_count = count($this->getOrganizations($base_object)); - $this->cache($cache_key, $organization_count); + $this->setCache($cache_key, $organization_count); return $organization_count; } @@ -162,7 +160,7 @@ public function getOrganizationProjects(Organization $organization, BaseObjectIn 'organization' => $organization->id(), 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', ])); - if ($organization_projects = $this->cache($cache_key)) { + if ($organization_projects = $this->getCache($cache_key)) { return $organization_projects; } @@ -176,7 +174,7 @@ public function getOrganizationProjects(Organization $organization, BaseObjectIn $organization_projects[$project->id] = $project; } } - $this->cache($cache_key, $organization_projects); + $this->setCache($cache_key, $organization_projects); return $organization_projects; } @@ -196,7 +194,7 @@ public function getOrganizationClusters($organization, BaseObjectInterface $base 'organization' => $organization->id(), 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', ])); - if ($organization_clusters = $this->cache($cache_key)) { + if ($organization_clusters = $this->getCache($cache_key)) { return $organization_clusters; } $projects = $this->getProjects($base_object); @@ -213,7 +211,7 @@ public function getOrganizationClusters($organization, BaseObjectInterface $base $organization_clusters = array_merge($organization_clusters, $project->clusters); } } - $this->cache($cache_key, $organization_clusters); + $this->setCache($cache_key, $organization_clusters); return $organization_clusters; } @@ -230,7 +228,7 @@ public function getOrganizations(BaseObjectInterface $base_object = NULL) { $cache_key = $this->getCacheKey(array_filter($this->getCommonCacheKeys() + [ 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', ])); - if ($organizations = $this->cache($cache_key)) { + if ($organizations = $this->getCache($cache_key)) { return $organizations; } @@ -256,7 +254,7 @@ public function getOrganizations(BaseObjectInterface $base_object = NULL) { } } - $this->cache($cache_key, $organizations); + $this->setCache($cache_key, $organizations); return $organizations; } @@ -276,14 +274,14 @@ public function getProjects(BaseObjectInterface $base_object = NULL, $filter_unp 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', 'filter_unpublished' => $filter_unpublished ? 'true' : 'false', ])); - $projects = $this->cache($cache_key); + $projects = $this->getCache($cache_key); if (is_array($projects)) { return $projects; } $data = $this->getData(); if (empty($data) || !is_object($data)) { - $this->cache($cache_key, []); + $this->setCache($cache_key, []); return []; } @@ -294,7 +292,11 @@ public function getProjects(BaseObjectInterface $base_object = NULL, $filter_unp // Do this early to save ressources. continue; } - $projects[] = new Project($project); + $project_object = new Project($project); + $project_object->setCacheTags([ + 'plan_id:' . $this->getPlaceholder('plan_id'), + ]); + $projects[] = $project_object; } if (!empty($base_object) && $base_object instanceof GoverningEntity) { @@ -310,7 +312,7 @@ public function getProjects(BaseObjectInterface $base_object = NULL, $filter_unp return count(array_intersect($cluster_ids, $item->cluster_ids)); }); } - $this->cache($cache_key, $projects); + $this->setCache($cache_key, $projects); return $projects; } @@ -329,7 +331,7 @@ public function getClustersByOrganization(BaseObjectInterface $base_object = NUL $cache_key = $this->getCacheKey(array_filter($this->getCommonCacheKeys() + [ 'base_object' => $base_object ? $base_object->bundle() . ':' . $base_object->id() : 'none', ])); - if ($clusters = $this->cache($cache_key)) { + if ($clusters = $this->getCache($cache_key)) { return $clusters; } @@ -352,7 +354,7 @@ public function getClustersByOrganization(BaseObjectInterface $base_object = NUL } } } - $this->cache($cache_key, $clusters); + $this->setCache($cache_key, $clusters); return $clusters; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanPrototypeQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanPrototypeQuery.php index 49039b179..8477ced92 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanPrototypeQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanPrototypeQuery.php @@ -5,7 +5,6 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\ghi_plans\ApiObjects\PlanPrototype; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; /** * Provides a query plugin for plan entities. @@ -21,7 +20,6 @@ */ class PlanPrototypeQuery extends EndpointQueryBase { - use SimpleCacheTrait; use StringTranslationTrait; /** @@ -35,7 +33,7 @@ class PlanPrototypeQuery extends EndpointQueryBase { */ public function getPrototype($plan_id) { $cache_key = $this->getCacheKey(['plan_id' => $plan_id]); - $prototype = $this->cache($cache_key); + $prototype = $this->getCache($cache_key); if ($prototype !== NULL) { return $prototype; } @@ -43,7 +41,7 @@ public function getPrototype($plan_id) { $data = $this->getData(); $prototype = !empty($data) ? new PlanPrototype($data) : FALSE; - $this->cache($cache_key, $prototype); + $this->setCache($cache_key, $prototype); return $prototype; } diff --git a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanReportingPeriodsQuery.php b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanReportingPeriodsQuery.php index fbaf1aa93..6768a81f9 100644 --- a/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanReportingPeriodsQuery.php +++ b/html/modules/custom/ghi_plans/src/Plugin/EndpointQuery/PlanReportingPeriodsQuery.php @@ -4,7 +4,6 @@ use Drupal\hpc_api\Query\EndpointQuery; use Drupal\hpc_api\Query\EndpointQueryBase; -use Drupal\hpc_api\Traits\SimpleCacheTrait; use Drupal\hpc_common\Helpers\ArrayHelper; /** @@ -21,8 +20,6 @@ */ class PlanReportingPeriodsQuery extends EndpointQueryBase { - use SimpleCacheTrait; - /** * Get the reporting periods for a plan. * @@ -32,7 +29,7 @@ class PlanReportingPeriodsQuery extends EndpointQueryBase { */ public function getReportingPeriods() { $cache_key = $this->getCacheKey($this->getPlaceholders()); - $periods = $this->cache($cache_key); + $periods = $this->getCache($cache_key); if ($periods) { return $periods; } @@ -47,7 +44,7 @@ public function getReportingPeriods() { $periods = array_combine($period_ids, $data); ArrayHelper::sortObjectsByNumericProperty($data, 'periodNumber', EndpointQuery::SORT_ASC); - $this->cache($cache_key, $periods); + $this->setCache($cache_key, $periods); return $periods; } diff --git a/html/modules/custom/ghi_sections/ghi_sections.links.task.yml b/html/modules/custom/ghi_sections/ghi_sections.links.task.yml index 2623ec956..8eb35e57d 100644 --- a/html/modules/custom/ghi_sections/ghi_sections.links.task.yml +++ b/html/modules/custom/ghi_sections/ghi_sections.links.task.yml @@ -1,3 +1,7 @@ +entity.node.cache_form: + route_name: ghi_sections.node.cache + title: 'Cache' + base_route: entity.node.canonical entity.node.section_navigation_form: route_name: ghi_sections.node.section_navigation title: 'Section navigation' diff --git a/html/modules/custom/ghi_sections/ghi_sections.permissions.yml b/html/modules/custom/ghi_sections/ghi_sections.permissions.yml new file mode 100644 index 000000000..7c8035684 --- /dev/null +++ b/html/modules/custom/ghi_sections/ghi_sections.permissions.yml @@ -0,0 +1,4 @@ +administer section cache: + title: 'Administer section cache' + description: 'Allow to access the cache control page for sections.' + restrict access: true \ No newline at end of file diff --git a/html/modules/custom/ghi_sections/ghi_sections.routing.yml b/html/modules/custom/ghi_sections/ghi_sections.routing.yml index 30773d70a..b05d02d33 100644 --- a/html/modules/custom/ghi_sections/ghi_sections.routing.yml +++ b/html/modules/custom/ghi_sections/ghi_sections.routing.yml @@ -1,3 +1,20 @@ +ghi_sections.node.cache: + path: '/node/{node}/cache' + defaults: + _form: '\Drupal\ghi_sections\Form\CacheForm' + _title_callback: '\Drupal\ghi_sections\Form\CacheForm::getTitle' + options: + parameters: + node: + type: entity:node + bundle: + - section + _admin_route: TRUE + requirements: + _permission: 'administer section cache' + _entity_access: node.update + node: \d+ + ghi_sections.node.section_navigation: path: '/node/{node}/section-navigation' defaults: diff --git a/html/modules/custom/ghi_sections/src/Entity/Section.php b/html/modules/custom/ghi_sections/src/Entity/Section.php index fe98798f3..100f78298 100644 --- a/html/modules/custom/ghi_sections/src/Entity/Section.php +++ b/html/modules/custom/ghi_sections/src/Entity/Section.php @@ -4,7 +4,6 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\ghi_base_objects\Entity\BaseObjectMetaDataInterface; -use Drupal\ghi_base_objects\Helpers\BaseObjectHelper; use Drupal\ghi_base_objects\Traits\ShortNameTrait; use Drupal\node\Entity\Node; @@ -31,10 +30,6 @@ public function label() { * {@inheritdoc} */ public function getPageTitle() { - $base_object = BaseObjectHelper::getBaseObjectFromNode($this); - if (!$base_object->needsYear()) { - return $this->label(); - } return $this->label(); } @@ -42,7 +37,7 @@ public function getPageTitle() { * {@inheritdoc} */ public function getPageTitleMetaData() { - $base_object = BaseObjectHelper::getBaseObjectFromNode($this); + $base_object = $this->getBaseObject(); $meta_data = $base_object instanceof BaseObjectMetaDataInterface ? $base_object->getPageTitleMetaData() : NULL; return $meta_data; } diff --git a/html/modules/custom/ghi_sections/src/Form/CacheForm.php b/html/modules/custom/ghi_sections/src/Form/CacheForm.php new file mode 100644 index 000000000..ea6fe6a60 --- /dev/null +++ b/html/modules/custom/ghi_sections/src/Form/CacheForm.php @@ -0,0 +1,79 @@ +cache = $container->get('cache.default'); + return $instance; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'ghi_sections_cache_form'; + } + + /** + * Title callback for the form route. + * + * @param \Drupal\node\NodeInterface $node + * The node object. + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * The title for the form page. + */ + public function getTitle(NodeInterface $node) { + return $this->t('Cache control for @label', [ + '@label' => $node->label(), + ]); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) { + $form['#node'] = $node; + $form['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Clear API cache'), + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\ghi_sections\Entity\Section $node */ + $node = $form['#node']; + $base_object = $node->getBaseObject(); + Cache::invalidateTags([ + $base_object->bundle() . '_id:' . $base_object->getSourceId(), + 'node:' . $node->id(), + ]); + } + +} diff --git a/html/modules/custom/hpc_api/src/ApiObjects/ApiObjectBase.php b/html/modules/custom/hpc_api/src/ApiObjects/ApiObjectBase.php index e7a7a14f3..290f5e591 100644 --- a/html/modules/custom/hpc_api/src/ApiObjects/ApiObjectBase.php +++ b/html/modules/custom/hpc_api/src/ApiObjects/ApiObjectBase.php @@ -2,6 +2,7 @@ namespace Drupal\hpc_api\ApiObjects; +use Drupal\Core\Cache\Cache; use Drupal\Core\StringTranslation\StringTranslationTrait; /** @@ -25,6 +26,13 @@ abstract class ApiObjectBase implements ApiObjectInterface { */ private $map; + /** + * The cache tags. + * + * @var string[] + */ + protected $cacheTags = []; + /** * {@inheritdoc} */ @@ -108,4 +116,24 @@ public function toArray() { */ abstract protected function map(); + /** + * Set the cache tags for this object. + * + * @param array $cache_tags + * The cache tags for this object. + */ + public function setCacheTags($cache_tags) { + $this->cacheTags = Cache::mergeTags($cache_tags); + } + + /** + * Get the cache tags for this object. + * + * @return array + * The cache tags for this object. + */ + public function getCacheTags() { + return $this->cacheTags; + } + } diff --git a/html/modules/custom/hpc_api/src/Query/EndpointQuery.php b/html/modules/custom/hpc_api/src/Query/EndpointQuery.php index 81323d929..cd73efe6f 100644 --- a/html/modules/custom/hpc_api/src/Query/EndpointQuery.php +++ b/html/modules/custom/hpc_api/src/Query/EndpointQuery.php @@ -241,6 +241,20 @@ public function getCacheBaseTime() { return $this->cacheBaseTime ?? NULL; } + /** + * Get the cache tags for this query. + * + * @return array + * The cache tags for the current query. + */ + public function getCacheTags() { + $cache_tags = []; + foreach ($this->getPlaceholders() as $key => $value) { + $cache_tags[] = $key . ':' . $value; + } + return $cache_tags; + } + /** * Replace placeholders with values in an endpoint. */ @@ -347,7 +361,7 @@ public function query() { if ($result->getStatusCode() == 200) { // Only cache the response, if the call returned successfully. $response = (string) $result->getBody(); - $this->cache($cache_key, $response); + $this->cache($cache_key, $response, FALSE, NULL, $this->getCacheTags()); } } diff --git a/html/modules/custom/hpc_api/src/Query/EndpointQueryBase.php b/html/modules/custom/hpc_api/src/Query/EndpointQueryBase.php index ff3ae44ad..2f1813661 100644 --- a/html/modules/custom/hpc_api/src/Query/EndpointQueryBase.php +++ b/html/modules/custom/hpc_api/src/Query/EndpointQueryBase.php @@ -3,6 +3,7 @@ namespace Drupal\hpc_api\Query; use Drupal\Component\Plugin\PluginBase; +use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; @@ -54,6 +55,13 @@ abstract class EndpointQueryBase extends PluginBase implements EndpointQueryPlug */ public $cache; + /** + * The cache tags. + * + * @var string[] + */ + protected $cacheTags = []; + /** * {@inheritdoc} */ @@ -64,6 +72,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->user = $user; $this->hidUserData = $hid_user_data; $this->cache = $cache; + $this->cacheTags = []; $endpoint_public = $plugin_definition['endpoint']['public'] ?? NULL; $endpoint_authenticated = $plugin_definition['endpoint']['authenticated'] ?? NULL; @@ -149,7 +158,8 @@ public function getData(array $placeholders = [], array $query_args = []) { return $data; } $data = $this->endpointQuery->getData(); - return $this->cache($cache_key, $data); + $this->setCache($cache_key, $data); + return $data; } /** @@ -180,4 +190,61 @@ public function getFullEndpointUrl() { return $this->endpointQuery->getFullEndpointUrl(); } + /** + * Set the cache tags for this query. + * + * @param array $cache_tags + * The cache tags for the current query. + */ + public function setCacheTags($cache_tags = []) { + $this->cacheTags = Cache::mergeTags($this->cacheTags, $cache_tags); + } + + /** + * Get the cache tags for this query. + * + * @return array + * The cache tags for the current query. + */ + public function getCacheTags() { + $cache_tags = $this->cacheTags; + $placeholders = $this->getPlaceholders(); + foreach ($placeholders as $key => $value) { + $cache_tags[] = $key . ':' . $value; + } + if (array_key_exists('plan_id', $placeholders)) { + $cache_tags[] = 'plan_data'; + } + return $cache_tags; + } + + /** + * Get cached data for the given cache key. + * + * @param string $cache_key + * The cache key. + * + * @return mixed + * The cached data if available. + */ + public function getCache($cache_key) { + return $this->cache($cache_key); + } + + /** + * Set the cache for the given cache id. + * + * This will also automatically set the cache tags for the current query. The + * base implementation of this class just takes the placeholders and + * transforms them into cache tags. + * + * @param string $cache_key + * The cache key. + * @param mixed $data + * The data to store for the cache key. + */ + public function setCache($cache_key, $data) { + $this->cache($cache_key, $data, FALSE, NULL, $this->getCacheTags()); + } + } diff --git a/html/modules/custom/hpc_api/src/Traits/SimpleCacheTrait.php b/html/modules/custom/hpc_api/src/Traits/SimpleCacheTrait.php index dfe0be511..45208f32c 100644 --- a/html/modules/custom/hpc_api/src/Traits/SimpleCacheTrait.php +++ b/html/modules/custom/hpc_api/src/Traits/SimpleCacheTrait.php @@ -46,11 +46,14 @@ public static function getCacheKey(array $array) { * can simply use the start time of the migration as the base time, and * only retrieve data once for every API call made during the data migration * process. + * @param array $cache_tags + * The cache tags to associate with this cache entry. Optional and only + * relevant when storing data. * * @return mixed|void * Either the stored data or nothing. */ - public function cache($cache_key, $data = NULL, $reset = FALSE, $cache_base_time = NULL) { + public function cache($cache_key, $data = NULL, $reset = FALSE, $cache_base_time = NULL, $cache_tags = []) { $cache_store = &drupal_static(__FUNCTION__, []); if ($data === NULL && $reset === TRUE) { @@ -76,7 +79,7 @@ public function cache($cache_key, $data = NULL, $reset = FALSE, $cache_base_time // Store data in the cache. $cache_store[$cache_key] = $data; $expiration_time = self::getRequestTime() + self::getCacheLifetime(); - self::cacheBackend()->set($cache_key, $data, $expiration_time); + self::cacheBackend()->set($cache_key, $data, $expiration_time, $cache_tags); return $cache_store[$cache_key]; } diff --git a/html/modules/custom/hpc_api/tests/src/Unit/OverrideEndpointQuery.php b/html/modules/custom/hpc_api/tests/src/Unit/OverrideEndpointQuery.php index ed53f8f03..145422924 100644 --- a/html/modules/custom/hpc_api/tests/src/Unit/OverrideEndpointQuery.php +++ b/html/modules/custom/hpc_api/tests/src/Unit/OverrideEndpointQuery.php @@ -14,7 +14,7 @@ class OverrideEndpointQuery extends EndpointQuery { * * @codeCoverageIgnore */ - public function cache($cache_key, $data = NULL, $reset = FALSE, $cache_base_time = NULL) { + public function cache($cache_key, $data = NULL, $reset = FALSE, $cache_base_time = NULL, $cache_tags = []) { return NULL; }