Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Drupal cache tags #797

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_revalidator_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