1

I have for example Node A with alias node-a and Node B with alias node-a/node-b so on my Node B i have this breadcrumb Home > Node A > Node B

GraphQl result:

"breadcrumb": [
                {
                    "text": "Home",
                    "url": {
                        "path": "/",
                        "routed": true
                    }
                },
                {
                    "text": "Node A",
                    "url": {
                        "path": "http://example.com/node-a",
                        "routed": true
                    }
                },
                {
                    "text": "Node B",
                    "url": {
                        "path": "",
                        "routed": true
                    }
                }
            ]

But when i change Node A title i get always the same result (breadcrumbs not changed), but when i re-save the node A or clear cache the graphQl result change.

Here is what i have tried:

  1. Clear graphQl cache (results and definitions)on node update:

     /**
     * Implements hook_ENTITY_TYPE_update().
     */
    function MYMODULE_node_update(EntityInterface $entity) {
      // Clear graphql cache.
      /** @var \Drupal\Core\Cache\CacheBackendInterface $graphql_results_cache */
      $graphql_results_cache = \Drupal::service('cache.graphql.results');
      $graphql_results_cache->invalidateAll();
      /** @var \Drupal\Core\Cache\CacheBackendInterface $graphql_definitions_cache */
      $graphql_definitions_cache = \Drupal::service('cache.graphql.definitions');
      $graphql_definitions_cache->invalidateAll();
    }

it doesn't work !

  1. Clear cache of all child node on node update like this:

    /**
     * Implements hook_ENTITY_TYPE_update().
     */
    function MYMODULE_node_update(EntityInterface $entity) {
      $database = \Drupal::database();
      // Get the current node path alias.
      $alias = \Drupal::service('path_alias.manager')
        ->getAliasByPath('/node/' . $entity->id());
      // Get all node child of the current node.
      $child_nodes_alias = $database->select('path_alias', 'pa')->fields('pa', [
        'path',
        'alias',
      ])->condition('path', '/node/%', 'LIKE')
        ->condition('alias', $alias . '%', 'LIKE')
        ->execute()
        ->fetchAll();
      // Invalidate cache for each node child of current node.
      foreach ($child_nodes_alias as $alias_data) {
        $nid = explode('/', $alias_data->path)[2] ?? NULL;
        if ($nid && $nid != $entity->id()) {
          $tags = ['node:' . $nid];
          Cache::invalidateTags($tags);
          \Drupal::entityTypeManager()->getStorage('node')->resetCache([$nid]);
        }
      }
    }

it doesn’t work also

  1. I combined the two previous ways it doesn't work too.

When i clear all caches on node update it works, but it's not the best way it impact site cache.


/**
 * Implements hook_ENTITY_TYPE_update().
 */
function MYMODULE_node_update(EntityInterface $entity) {
  drupal_flush_all_caches();
}

What is the best way to clear breadcrumbs cache, or child nodes without clear all site cache ?

Edit: Thank you @4K4 for your comment, i did tried to add CacheTags too breadcrumb but i didn’t work either !


/**
 * Implements hook_system_breadcrumb_alter().
 */
function MYMODULE_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context) {
  // Append the current page title to the breadcrumb for non-admin routes.
  if ($breadcrumb && !\Drupal::service('router.admin_context')->isAdminRoute()) {
    $links = $breadcrumb->getLinks();
    foreach ($links as $link) {
      $parameters = $link->getUrl()->getRouteParameters();
      if ($parameters) {
        // Make sure the breadcrumb is updated when node title changes.
        $breadcrumb->addCacheTags(['node:' . $parameters['node']]);
        $breadcrumb->addCacheContexts(['url']);
      }
    }
  }
}

Note: Easy Breadcrumb module installed.

9
  • Are they actually linked in the menu? How are they connected otherwise? Aliases don't imply cache dependencies.
    – Kevin
    Aug 24, 2021 at 15:24
  • Normally you don't solve caching issues in hook_node_update(). The GraphQL plugin building the breadcrumb has to add the cache tags and then they are invalidated automatically when a node is saved.
    – 4uk4
    Aug 24, 2021 at 15:37
  • i did tried to add node cachetag to breadcrumbs as i mentioned in my edit, and it didn’t work, maybe i should add another cacheTag, what i'm missing ?
    – berramou
    Aug 24, 2021 at 17:36
  • @Kevin no they are not linked in the menu, auto generated breadcrumbs using Easy Breadcrumb module
    – berramou
    Aug 24, 2021 at 17:40
  • OK, provided that the breadcrumbs have all necessary cache tags, it's still unclear which code is building the GraphQL result. Couldn't find such a plugin in the main module. Do you have an extra module for that?
    – 4uk4
    Aug 24, 2021 at 18:41

1 Answer 1

1

I don't know the caching implementation of GraphQL 3.x field plugins, but as far as I can tell this code (even after the latest update from 2018) doesn't collect the breadcrumb metadata because it only gets the links:

protected function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) {
    if ($value instanceof Url) {
      $resolve = $this->subRequestBuffer->add($value, function () {
        $links = $this->breadcrumbManager->build($this->routeMatch)->getLinks();
        return $links;
      });

      return function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($resolve) {
        /** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
        $response = $resolve();
        $links = $response->getValue();

        foreach ($links as $link) {
          yield new CacheableValue($link, [$response]);
        }
      };
    }
  }

Shouldn't it get the breadcrumb object to add its cacheable metadata?

protected function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) {
    if ($value instanceof Url) {
      $resolve = $this->subRequestBuffer->add($value, function () {
        $breadcrumb = $this->breadcrumbManager->build($this->routeMatch);
        return $breadcrumb;
      });

      return function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($resolve) {
        /** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
        $response = $resolve();
        $breadcrumb = $response->getValue();
        $links = $breadcrumb->getLinks();

        foreach ($links as $link) {
          yield new CacheableValue($link, [$response, $breadcrumb]);
        }
      };
    }
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.