web-dev-qa-db-fra.com

Comment configurer correctement la mise en cache pour mon routage d'API personnalisé?

J'ai un module personnalisé avec quelques routes qui retournent JSON, par exemple les termes d'un vocabulaire.

routing.yml

mymodule.terms:
  path: '/api/terms/{vid}'
  defaults:
    _controller: '\Drupal\mymodule\Controller\MyModuleController::getTerms'
    _title: 'Terms'
  requirements:
    _permission: 'access content'
    vid: '^(foobar|barfoo)$'

Manette:

public function getTerms($vid) {
  $response = $this->jsonifyVocabulary($vid);
  return $response;
}

private function jsonifyVocabulary($vid) {
  $request = \Drupal::request();
  $lang = $request->query->get('lang', 'en');
  $output = [];
  $taxonomyManager = \Drupal::entityManager()->getStorage('taxonomy_term');
  $terms = $taxonomyManager->loadTree($vid, 0, 1, false);
  $response = CacheableJsonResponse::create($output);

  debug(1);

  foreach ($terms as $term) {
    $term = Term::load($term->tid);
    if ($term->hasTranslation($lang) && $lang !== $term->language()) {
      $term = $term->getTranslation($lang);
    }
    $entry = [
      'tid' => (int) $term->id(),
      'label' => $term->getName(),
    ];

    // $response->addCacheableDependency($term);
    $output[$vid][] = $entry;
  }

  $response->setData($output);

  $cacheMeta = (new CacheableMetadata())->addCacheContexts(['url.query_args:lang']);
  $response->addCacheableDependency($cacheMeta);

  return $response;
}

Merci à cette réponse Je sais comment ajouter les paramètres d'URL en tant que contexte de cache, mais le

debug(1);

est toujours appelé, je suppose que je dois en quelque sorte dire à Drupal que le cache de cette réponse JSON devrait être lié à cette route ...

Je n'utilise pas le module REST du noyau à dessein car je trouve difficile de configurer pour produire exactement ce dont j'ai besoin.

5
Alex

Voici tout ce dont vous avez besoin pour configurer la mise en cache d'une réponse:

  $response = CacheableJsonResponse::create($output);
  return $response;

Si vous utilisez une réponse qui implémente CacheableResponseInterface, elle sera mise en cache indéfiniment.

Balises de cache

Lorsque vous ajoutez une liste de termes de taxonomie, vous devez ajouter ces balises:

  $list_tags = $this->entityTypeManager()->getDefinition('taxonomy_term')->getListCacheTags();
  $cache_metadata->addCacheTags($list_tags);

  foreach ($terms as $term) {
    $cache_metadata->addCacheableDependency($term);
  }

Cela invalidera l'entrée de cache lorsque les termes sont modifiés dans la base de données.

Contextes de cache

Lorsque vous utilisez un paramètre de requête, ajoutez ce contexte:

  $lang = $request->query->get('lang', 'en');
  $cache_metadata->addCacheContexts(['url.query_args:lang']);

Cela fera varier le cache selon les arguments de la requête.

métadonnées pouvant être mises en cache dans une réponse

Pour mettre tout cela ensemble, créez au début du contrôleur un objet CacheableMetadata vide:

  $cache_metadata = new CacheableMetadata();

Lors de la création du contenu, collectez les métadonnées du cache pour toutes les dépendances que vous utilisez, comme décrit dans les deux exemples précédents, puis ajoutez les métadonnées à la réponse à la fin du contrôleur:

$response->addCacheableDependency($cache_metadata);
9
4k4

..Je suppose que je dois dire en quelque sorte Drupal que le cache de cette réponse json devrait se rapporter à cette route ...

Regarder les "Contextes de cache" documentation sous "Débogage":

"Toutes les informations ci-dessus sont utiles lors du débogage de quelque chose qui est mis en cache. Mais, il y a encore une chose: disons que quelque chose est mis en cache avec les clés de cache [" foo "," bar " ] et les contextes de cache ['languages: language_interface', 'user.permissions', 'route']. Ensuite, l'élément de cache correspondant sera mis en cache dans une case de cache particulière avec un CID (ID de cache) de:

foo:bar:[languages:language_interface]=en:[user.permissions]=A_QUITE_LONG_HASH:[route]=myroute.ROUTE_PARAMS_HASH "

Il semble que si vous définissez les contextes de cache dans la réponse, il doit être mis en cache dans le bac de cache qui a un CID pour ce contexte de route et de cache.

Je n'utilise pas le module de repos du noyau à dessein car j'ai du mal à configurer pour produire exactement ce dont j'ai besoin.

Drupal core REST inclut un "vocabulaire de taxonomie" REST qui prend l'ID de vocabulaire comme argument: enter image description here

Par défaut, il renvoie les informations du vocabulaire comme:

{
  "uuid": "46fb0c72-4570-4c7f-b90d-faa69f2c5e7a",
  "langcode": "en",
  "status": true,
  "dependencies": [],
  "name": "Vocabulary Name",
  "vid": "vocabulary_vid",
  "description": "Some vocabulary description.",
  "hierarchy": 0,
  "weight": 0
}

Cependant, pour obtenir les termes d'un vocabulaire particulier à afficher dans la réponse JSON, vous pouvez créer un normaliseur personnalisé qui peut modifier la sortie de cette ressource REST à votre convenance).

Voir:

1
edwardchiapet

Je ne sais pas si vous avez déjà trouvé une réponse à votre question. J'ai passé littéralement une demi-journée à essayer de découvrir ce qui ne va pas ... Eh bien - la réponse est très simple: assurez-vous que le module de cache de page dynamique interne est activé. (Cette façon la plus simple: drush en -y dynamic_page_cache)

Lorsque le module est désactivé, RIEN n'est extrait du cache. Le module lui-même a un EventSubscriber, déclenché lors de la correspondance de route (voir dynamic_page_cache/src/EventSubscriber/DynamicPageCacheSubscriber.php):

/**
 * Sets a response in case of a Dynamic Page Cache hit.
 *
 * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
 *   The event to process.
 */
public function onRouteMatch(GetResponseEvent $event) {
  // Don't cache the response if the Dynamic Page Cache request policies are
  // not met. Store the result in a static keyed by current request, so that
  // onResponse() does not have to redo the request policy check.
  $request = $event->getRequest();
  $request_policy_result = $this->requestPolicy->check($request);
  $this->requestPolicyResults[$request] = $request_policy_result;
  if ($request_policy_result === RequestPolicyInterface::DENY) {
    return;
  }

  // Sets the response for the current route, if cached.
  $cached = $this->renderCache->get($this->dynamicPageCacheRedirectRenderArray);
  if ($cached) {
    $response = $this->renderArrayToResponse($cached);
    $response->headers->set(self::HEADER, 'HIT');
    $event->setResponse($response);
  }
}

qui s'occupe en fait de la lecture entière du cache et du retour d'une réponse json mise en cache. Sans le module, l'ensemble CachedJsonResponse est inutile.

NOTE: Je voudrais donner tout le crédit de cette réponse à Monika From the Forests près de Rzeszów. C'est son dévouement et son engagement à trouver ce problème qui m'ont donné cette magnifique occasion de relever le défi. Je vous remercie. Dieu te bénisse.

1
Tomasz Trzciński