commercetools-8.x-1.2-alpha1/src/EventSubscriber/CommercetoolsGraphQlCacheSubscriber.php

src/EventSubscriber/CommercetoolsGraphQlCacheSubscriber.php
<?php

declare(strict_types=1);

namespace Drupal\commercetools\EventSubscriber;

use Drupal\commercetools\Cache\CacheableCommercetoolsGraphQlResponse;
use Drupal\commercetools\Event\CommercetoolsGraphQlOperationEvent;
use Drupal\commercetools\Event\CommercetoolsGraphQlOperationResultEvent;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\Parser;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Replace tokens in the variable array on values.
 */
class CommercetoolsGraphQlCacheSubscriber implements EventSubscriberInterface {

  /**
   * Array of parsed query objects.
   *
   * @var array
   */
  protected array $queryObjects;

  /**
   * CommercetoolsGraphQlCacheSubscriber constructor.
   *
   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cacheTagsInvalidator
   *   The cache tags invalidator.
   */
  public function __construct(
    protected CacheTagsInvalidatorInterface $cacheTagsInvalidator,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      CommercetoolsGraphQlOperationEvent::class => ['onExecute'],
      CommercetoolsGraphQlOperationResultEvent::class => ['afterExecute'],
    ];
  }

  /**
   * Before GraphQl execution event handler.
   *
   * @throws \GraphQL\Error\SyntaxError
   */
  public function onExecute(CommercetoolsGraphQlOperationEvent $event): void {
    $query = $event->query;
    $variables = $event->variables;
    $cacheableMetadata = $event->cacheableMetadata;

    $queryObject = $this->getQueryObject($query);
    $queryRootNodes = $queryObject->toArray()['definitions'];

    $cacheableMetadata->addCacheTags([
      // @todo Maybe get rid of the general tag and keep only configuration.
      CacheableCommercetoolsGraphQlResponse::CACHE_TAG_GENERAL,
      CacheableCommercetoolsGraphQlResponse::CACHE_TAG_CONFIGURATION,
      CacheableCommercetoolsGraphQlResponse::CACHE_TAG_API_CONFIGURATION,
    ]);

    for ($i = 0; $i < $queryRootNodes->count(); $i++) {
      $node = $queryRootNodes->offsetGet($i);
      // Getting the operation name.
      $operation = $node->selectionSet->selections->offsetGet(0)->name->value;
      switch ($operation) {
        case 'productProjectionSearch':
        case 'products':
          // @todo Rework this to proper parsing of where and use the list tag
          // for wide searches.
          if (isset($variables['limit']) && $variables['limit'] > 1) {
            // Search for multiple products, so use cache by product list.
            $cacheableMetadata->addCacheTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_PRODUCT_LIST]);
          }
          break;

        case 'cart':
          $cacheableMetadata->addCacheTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_CART_PREFIX . $variables['id']]);
          break;

        case 'customers':
          // Cache load by customer ID queries.
          if (isset($variables['id'])) {
            $cacheableMetadata->addCacheTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_CUSTOMER_PREFIX . $variables['id']]);
          }
          else {
            // Disable caching for other queries.
            $cacheableMetadata->setCacheMaxAge(0);
          }
          break;

        default:
          // Disabling cache for not-known operations.
          $cacheableMetadata->setCacheMaxAge(0);
      }

    }
  }

  /**
   * After GraphQl execution event handler.
   *
   * @throws \GraphQL\Error\SyntaxError
   */
  public function afterExecute(CommercetoolsGraphQlOperationResultEvent $event): void {
    $result = &$event->result;
    $cacheableMetadata = $event->cacheableMetadata;
    $query = $event->query;

    $queryObject = $this->getQueryObject($query);
    $queryRootNodes = $queryObject->toArray()['definitions'];

    for ($i = 0; $i < $queryRootNodes->count(); $i++) {
      $node = $queryRootNodes->offsetGet($i);

      // Getting the operation name.
      $operation = $node->selectionSet->selections->offsetGet(0)->name->value;
      switch ($operation) {
        case 'productProjectionSearch':
        case 'products':
          foreach ($result[$operation]['results'] as $product) {
            $cacheableMetadata->addCacheTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_PRODUCT_PREFIX . $product['id']]);
          }
          break;

        case 'orders':
          foreach ($result['orders']['results'] as $order) {
            $cacheableMetadata->addCacheTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_ORDERS_PREFIX . $order['id']]);
          }
          break;

        case 'customers':
          foreach ($result['customers']['results'] as $customer) {
            $cacheableMetadata->addCacheTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_CUSTOMER_PREFIX . $customer['id']]);
          }
          break;

        // Invalidate cache related to a mutation request.
        case 'updateCart':
        case 'deleteCart':
          $this->cacheTagsInvalidator->invalidateTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_CART_PREFIX . $result[$operation]['id']]);
          break;

        case 'updateCustomer':
        case 'deleteCustomer':
          $this->cacheTagsInvalidator->invalidateTags([CacheableCommercetoolsGraphQlResponse::CACHE_TAG_CUSTOMER_PREFIX . $result[$operation]['id']]);
          break;

        default:
          continue 2;
      }
    }
  }

  /**
   * Get parsed query object.
   *
   * @param string $query
   *   A GraphQL query.
   *
   * @return \GraphQL\Language\AST\DocumentNode
   *   Parsed GraphQL object.
   *
   * @throws \GraphQL\Error\SyntaxError
   */
  protected function getQueryObject(string $query): DocumentNode {
    // @todo Find a more lightweight library to parse the query.
    $this->queryObjects[$query] ??= Parser::parse($query);
    return $this->queryObjects[$query];
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc