CacheableDependencyInterface
Para simplificar el trabajo con los metadatos de cacheabilidad (cache tags, cache contexts y max-age), Drupal 8 incluye la interfaz CacheableDependencyInterface
.
¿Por qué?
Imagínese tener que crear manualmente las etiquetas de caché de cada objeto individual y objeto de configuración que usa en un array de renderizado (o en otros cálculos), y en un sitio multilingüe también tener que añadir manualmente los contextos de caché necesarios (ya sea para la entidad traducida o para la anulación del idioma para el objeto de configuración).
No solo entidades y configuración, sino también resultados de acceso, plugins de bloque, enlaces de menú, plugins contextuales, plugins de condición, etc., porque todos terminan en renderizado (un tipo concreto de cálculo) que queremos cachear.
En los primeros días del desarrollo de Drupal 8 esto ocurría. Obviamente, era poco confiable y muy propenso a errores.
Por eso se introdujo CacheableDependencyInterface
. Como indica su nombre, los objetos que implementan esta interfaz pueden automáticamente ser dependencias cacheables.
Por ejemplo, al construir un array de renderizado para <p>¡Hola, %usuario, bienvenido a %sitio!</p>, usted depende tanto de la entidad Usuario del usuario actual como de la configuración system.site
. Cuando este array de renderizado es cacheado, tendrá tanto esta entidad de usuario como este objeto de configuración como sus dependencias cacheables.
CacheableDependencyInterface
puede ser implementado por cualquier objeto valor (es decir, un objeto que representa una unidad lógica de datos). Si revisa su documentación API, verá que está implementado por muchos objetos significativos en el núcleo de Drupal 8. De hecho, se puede afirmar con confianza que está implementado por la mayoría de los objetos con los que interactúa al escribir código en Drupal 8.
Hay dos casos extremos frecuentes para los que Drupal tiene rasgos convenientes: el caso de un objeto inmutable y por tanto cacheable para siempre (UnchangingCacheableDependencyTrait, que siempre retorna max-age === permanente), y el caso de un objeto siempre calculado dinámicamente y por lo tanto nunca cacheable (UncacheableDependencyTrait, que siempre retorna max-age === 0).
RefinableCacheableDependencyInterface
Pero CacheableDependencyInterface
solo puede trabajar con metadatos cacheables "intrínsecos" o "canónicos" del objeto. A veces existen varias variantes de un objeto.
Los ejemplos más claros son las traducciones de entidades (la misma entidad con el mismo ID de entidad, pero en otra traducción) y las traducciones de configuración (el mismo objeto de configuración con el mismo nombre, pero con anulación de idioma).
En ambos casos, los metadatos de cacheabilidad ya presentes en el objeto original (no traducido) siguen siendo aplicables. Por ejemplo, node:5 (etiquetas de caché). Pero — en el caso de una entidad — se necesita el contexto de caché del idioma del contenido ('languages:'. LanguageInterface::TYPE_CONTENT, véase Cache contexts) para indicar que esta entidad es una variante de la entidad original que varía según el contexto del idioma de contenido que se haya negociado. De igual modo, en el caso de un objeto de configuración, se requiere el contexto de caché del idioma de la interfaz ('languages:'. LanguageInterface::TYPE_INTERFACE) para indicar que este objeto de configuración es una variante del objeto original que varía según el idioma de la interfaz negociado.
Un ejemplo fuera del ámbito de la traducción podría ser un módulo "Día del pirata" que tenga una anulación de configuración que solo se aplique en el día del pirata, añadiendo "yar", "har" y loros aleatorios a la configuración; entonces los objetos de configuración tendrían el contexto de caché pirate_day.
En todos los ejemplos anteriores, necesitamos refinar los metadatos cacheables de nuestros objetos para indicar la variante cargada. Por esta razón se añadió RefinableCacheableDependencyInterface, que permite precisamente esto: tiene la capacidad de añadir etiquetas de caché, contextos y actualizar la edad máxima.
Para facilitar la implementación de esta interfaz, existe además el práctico rasgo RefinableCacheableDependencyTrait.
Sobre entidades y objetos de configuración
Todas las entidades en Drupal 8 (core, contrib y personalizadas) implementan la interfaz EntityInterface, que extiende tanto CacheableDependencyInterface como RefinableCacheableDependencyInterface. Además, todas las entidades del núcleo de Drupal 8 extienden la clase base abstracta Entity, y se recomienda que las contrib y personalizadas hagan lo mismo. Esto significa que cada entidad individual con la que interactúa en Drupal 8 tiene automáticamente etiquetas de caché consistentes (<tipo de entidad>:<ID de entidad>, por ejemplo node:5 y user:3) y contextos de caché que reflejan la traducción.
Todos los objetos de configuración en el núcleo de Drupal 8 extienden la clase base abstracta ConfigBase, que implementa tanto CacheableDependencyInterface como RefinableCacheableDependencyInterface. Esto significa que cada objeto de configuración con el que interactúa en Drupal 8 tiene automáticamente etiquetas de caché consistentes (en forma de config:<nombre de configuración>, por ejemplo config:system.performance) y contextos de caché que reflejan anulaciones de configuración (cuya traducción es el único ejemplo en el núcleo).
Finalmente, todas las entidades y objetos de configuración en Drupal 8 tienen automáticamente contextos de caché del idioma de contenido/interfaz (respectivamente) gracias a EntityManager::getTranslationFromContext() y LanguageConfigFactoryOverride::getCacheableMetadata($name).
Uso de objetos que son dependencias cacheables
El renderizado es el ejemplo más común de dependencia de un objeto que es una dependencia cacheable. Para facilitar esto, disponemos de RendererInterface::addCacheableDependency($build, $dependency), donde $build
es el array de renderizado que depende del objeto $dependency
; los metadatos cacheables del objeto se “absorberán” automáticamente en el array de renderizado. Esto significa que el array de renderizado se invalidará siempre que una etiqueta de caché inválida del objeto provoque que se almacene en caché una versión diferente, si se usa otra traducción (es decir, el contexto de caché del idioma de contenido se traduce a otro idioma), y expirará automáticamente si la dependencia tiene una edad máxima que no sea permanente (infinita).
Consulte Cacheabilidad de arrays de renderizado – ejemplo concreto para un ejemplo completo.
Otro buen ejemplo son las verificaciones de acceso que retornan objetos AccessResult, los cuales también tienen el método AccessResult::addCacheableDependency($dependency). Tenga en cuenta que aquí solo tenemos el parámetro $dependency
, porque podemos almacenar metadatos de cacheabilidad de las dependencias pasadas en el propio objeto resultado de acceso. (El renderizado, con sus arrays de renderizado, es la excepción.)