Skip to content

Commit

Permalink
Add support for Drupal cache tags
Browse files Browse the repository at this point in the history
  • Loading branch information
Bojan Bogdanovic committed Sep 7, 2024
1 parent c06b861 commit baf390c
Show file tree
Hide file tree
Showing 17 changed files with 1,590 additions and 0 deletions.
1 change: 1 addition & 0 deletions modules/next/config/install/next.settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ preview_url_generator: simple_oauth
preview_url_generator_configuration:
secret_expiration: 30
debug: false
queue_size: 10
3 changes: 3 additions & 0 deletions modules/next/config/schema/next.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ next.settings:
type: next.preview_url_generator.configuration.[%parent.preview_url_generator]
debug:
type: boolean
queue_size:
type: integer
label: 'Queue size'

next.site_previewer.configuration.iframe:
type: mapping
Expand Down
93 changes: 93 additions & 0 deletions modules/next/next.install
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,85 @@
*/

use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Language\LanguageInterface;
use Drupal\next\CacheTagNodeMapperInterface;
use Drupal\next\CacheTagRevalidatorTaskStoreInterface;

/**
* Implements hook_schema().
*/
function next_schema() {
$schema = [];

$schema[CacheTagNodeMapperInterface::TABLE] = [
'description' => 'Cache tags mapped to associated node.',
'fields' => [
'tag' => [
'description' => 'Cache tag.',
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
'nid' => [
'description' => 'The node id using the cache tag.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
],
'langcode' => [
'description' => 'The language code of the node.',
'type' => 'varchar_ascii',
'length' => 12,
'not null' => TRUE,
'default' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
'next_site' => [
'description' => 'The associated next site.',
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
],
'primary key' => ['tag', 'nid', 'langcode', 'next_site'],
'indexes' => [
'tag_nid_langcode_next_site' => ['tag', 'nid', 'langcode', 'next_site'],
],
];

$schema[CacheTagRevalidatorTaskStoreInterface::TABLE] = [
'description' => 'Cache tag revalidator task storage.',
'fields' => [
'nid' => [
'description' => 'The node id that needs to be revalidated.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
],
'langcode' => [
'description' => 'The language code of the node.',
'type' => 'varchar_ascii',
'length' => 12,
'not null' => TRUE,
'default' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
'next_site' => [
'description' => 'The associated next site.',
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
],
'primary key' => ['nid', 'langcode', 'next_site'],
'indexes' => [
'nid_langcode_next_site' => ['nid', 'langcode', 'next_site'],
],
];

return $schema;
}

/**
* Set simple_oauth as the preview url generator.
Expand Down Expand Up @@ -104,3 +183,17 @@ function next_update_9106() {
$config->set('debug', FALSE)
->save();
}

/**
* Install tables if module is already installed.
*/
function next_update_9107() {
$schema = \Drupal::database()->schema();
$tables = next_schema();
if (!$schema->tableExists(CacheTagNodeMapperInterface::TABLE)) {
$schema->createTable(CacheTagNodeMapperInterface::TABLE, $tables[CacheTagNodeMapperInterface::TABLE]);
}
if (!$schema->tableExists(CacheTagRevalidatorTaskStoreInterface::TABLE)) {
$schema->createTable(CacheTagRevalidatorTaskStoreInterface::TABLE, $tables[CacheTagRevalidatorTaskStoreInterface::TABLE]);
}
}
21 changes: 21 additions & 0 deletions modules/next/next.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,24 @@ services:
arguments: ['@event_dispatcher']
tags:
- { name: needs_destruction }
next.resource_response.subscriber:
class: Drupal\next\EventSubscriber\ResourceResponseSubscriber
arguments:
- '@entity_type.manager'
- '@language_manager'
- '@next.cache_tag_node_mapper'
tags:
- { name: event_subscriber }
next.cache_tag_node_mapper:
class: Drupal\next\CacheTagNodeMapper
arguments: [ '@database' ]
next.cache_tag_revalidator_task_store:
class: Drupal\next\CacheTagRevalidatorTaskStore
arguments: [ '@database' ]
next.path_revalidor_helper:
class: Drupal\next\PathRevalidatorHelper
arguments:
- '@entity_type.manager'
- '@next.settings.manager'
- '@http_client'
- '@logger.channel.next'
92 changes: 92 additions & 0 deletions modules/next/src/CacheTagNodeMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace Drupal\next;

use Drupal\Core\Database\Connection;

/**
* Defines the cache tag node mapper service.
*/
class CacheTagNodeMapper implements CacheTagNodeMapperInterface {

/**
* Database Service Object.
*
* @var \Drupal\Core\Database\Connection
*/
private Connection $connection;

/**
* Construct the CacheTagNodeMapper.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(Connection $connection) {
$this->connection = $connection;
}

/**
* {@inheritdoc}
*/
public function getCacheTagsByNid(int $nid, string $langcode, string $next_site): array {
return $this->connection->select(self::TABLE, 'c')
->fields('c', ['tag'])
->condition('c.nid', $nid)
->condition('c.langcode', $langcode)
->condition('c.next_site', $next_site)
->execute()
->fetchCol();
}

/**
* {@inheritdoc}
*/
public function getNidsByCacheTag(string $cache_tag, string $langcode, $next_site): array {
return $this->connection->select(self::TABLE, 'c')
->fields('c', ['nid'])
->condition('c.tag', $cache_tag)
->condition('c.langcode', $langcode)
->condition('c.next_site', $next_site)
->distinct()
->execute()
->fetchCol();
}

/**
* {@inheritdoc}
*/
public function delete(array $cache_tags, string $langcode, ?int $nid = NULL, ?string $next_site = NULL): void {
$query = $this->connection->delete(self::TABLE)
->condition('langcode', $langcode);

if (count($cache_tags) > 1) {
$query->condition('tag', $cache_tags, 'IN');
}
else {
$query->condition('tag', reset($cache_tags));
}

if ($nid) {
$query->condition('nid', $nid);
}
if ($next_site) {
$query->condition('next_site', $next_site);
}

$query->execute();
}

/**
* {@inheritdoc}
*/
public function add(array $values): void {
$query = $this->connection->insert(self::TABLE)
->fields(['tag', 'nid', 'langcode', 'next_site']);
foreach ($values as $row) {
$query->values($row);
}
$query->execute();
}

}
75 changes: 75 additions & 0 deletions modules/next/src/CacheTagNodeMapperInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace Drupal\next;

/**
* Cache tag node mapper interface.
*/
interface CacheTagNodeMapperInterface {

/**
* The table name.
*/
const TABLE = 'cachetag_node_map';

/**
* Get cache tags by node id.
*
* @param int $nid
* The node id.
* @param string $langcode
* The language code.
* @param string $next_site
* The next site id.
*
* @return array
* Returns array with cache tags.
*/
public function getCacheTagsByNid(int $nid, string $langcode, string $next_site): array;

/**
* Get node id's by cache tag.
*
* @param string $cache_tag
* The cache tag.
* @param string $langcode
* The language code.
* @param string $next_site
* The next site id.
*
* @return array
* Returns array with node id's.
*/
public function getNidsByCacheTag(string $cache_tag, string $langcode, string $next_site): array;

/**
* Delete cache tags.
*
* @param array $cache_tags
* The cache tags.
* @param string $langcode
* The language code.
* @param int|null $nid
* Optional node id.
* @param string|null $next_site
* Optional next site id.
*/
public function delete(array $cache_tags, string $langcode, ?int $nid = NULL, ?string $next_site = NULL): void;

/**
* Add cache tags.
*
* @param array $values
* Cache tags values to add.
* E.g: [
* [
* 'tag' => 'node:1',
* 'nid' => '1',
* 'langcode' => 'en',
* 'next_site' => example,
* ]
* ].
*/
public function add(array $values): void;

}
69 changes: 69 additions & 0 deletions modules/next/src/CacheTagRevalidatorTaskStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Drupal\next;

use Drupal\Core\Database\Connection;

/**
* Defines the cache tag revalidator task store service.
*/
class CacheTagRevalidatorTaskStore implements CacheTagRevalidatorTaskStoreInterface {

/**
* Database Service Object.
*
* @var \Drupal\Core\Database\Connection
*/
private Connection $connection;

/**
* Construct the CacheTagRevalidatorTaskStore.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(Connection $connection) {
$this->connection = $connection;
}

/**
* {@inheritdoc}
*/
public function set(array $nids, string $langcode, string $next_site): void {
$query = $this->connection->insert(self::TABLE)
->fields(['nid', 'langcode', 'next_site']);
foreach ($nids as $nid) {
$query->values([
'nid' => $nid,
'langcode' => $langcode,
'next_site' => $next_site,
]);
}
$query->execute();
}

/**
* {@inheritdoc}
*/
public function has(int $nid, string $langcode, string $next_site): bool {
return (bool) $this->connection->select(self::TABLE, 'c')
->fields('c', ['nid'])
->condition('c.nid', $nid)
->condition('c.langcode', $langcode)
->condition('c.next_site', $next_site)
->execute()
->fetchField();
}

/**
* {@inheritdoc}
*/
public function delete(array $nids, string $langcode, string $next_site): void {
$this->connection->delete(self::TABLE)
->condition('nid', $nids, 'IN')
->condition('langcode', $langcode)
->condition('next_site', $next_site)
->execute();
}

}
Loading

0 comments on commit baf390c

Please sign in to comment.