Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cache_tools
Manage
Activity
Members
Labels
Plan
Wiki
Custom issue tracker
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Model registry
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
project
cache_tools
Commits
0eb49149
There was an error fetching the commit references. Please try again later.
Commit
0eb49149
authored
6 years ago
by
Antal Ludescher-Tyukodi
Committed by
Wolfgang Ziegler
6 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Issue
#3049056
by aludescher: Implement cache tags based on field values
parent
0f814e37
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
README.md
+24
-40
24 additions, 40 deletions
README.md
cache_tools.module
+27
-9
27 additions, 9 deletions
cache_tools.module
src/Service/CacheSanitizer.php
+134
-10
134 additions, 10 deletions
src/Service/CacheSanitizer.php
with
185 additions
and
59 deletions
README.md
+
24
−
40
View file @
0eb49149
...
...
@@ -14,7 +14,7 @@ It's using custom BlockViewBuilder to sanitize tags and contexts of blocks.
By default it's stripping several contexts (route, url, url.query_args)
and couple of tags (node_list, taxonomy_term_list). You can also decide
which additional tags and contexts you would like to strip. Everything
is configured in cache_tools.services.yml under paramter section.
is configured in cache_tools.services.yml under param
e
ter section.
You can override parameters by introducing custom services.yml
### 2. Custom cache tag for views.
...
...
@@ -22,7 +22,7 @@ You can override parameters by introducing custom services.yml
Module introduces also custom cache tag for the views. It's optimizing
to place more precise tags for the views. Namely, it places
`{entity}_{bundle}_pub`
(like
`node_article_pub`
or
`node_recipe_pub`
)
instead of too gener
e
al
`node_list`
. By doing so it will handle invalidation
instead of too general
`node_list`
. By doing so it will handle invalidation
of published nodes only respecting the bundle of the node. This tag is placed
out-of-the-box based on the view configuration. Module is auto extracting
the filter and arguments handler settings for particular view. If there is
...
...
@@ -30,51 +30,35 @@ such handler it will auto identify entity and bundle and set\
`{entity}_{bundle}_pub`
tag. Invalidation is handled automatically during
entity_insert and entity_update events.
### 2. Custom cache tag
for view
s based on field.
### 2. Custom cache tags based on field
values
.
You can also use extended version of the cache tag behavior, namely you can
additionally set the field to be included in cache tag. Along the entity id.
You can achieve this by using cache tag for views based on field, where
you can select which field will be contained in that cache tags. As an example,
let's say you'll choose the field
`field_author`
, then instead of regular
`node_article_pub`
you'll get
`node_article_pub:field_author:123`
. Where 123 is
the node id of currently previewed entity.
Field-based cache tags of the following format
`entitytype_entitybundle_pub:field_name:value`
can be configured, e.g.:
```
yml
invalidate
:
node
:
-
article:field_author
```
will produce the cache tag
`node_article_pub:field_author:123`
where 123 is
the value of the field_author field (author id).
Invalidation is handled automatically during entity operations:
1.
Insert published: all non-empty field values.
2.
Delete published: all non-empty field values.
3.
Update published: only the modified field values.
4.
Update and publish: all non-empty field values.
5.
Update and unpublish: all non-empty original field values.
Use case for this:
A block or view listing nodes filtered by bundle and a given field value. We
want the cache to be invalidated only when nodes having that bundle and field
value are added, deleted or modified. E.g.
1.
You have author/user entity
2.
On the author/user page you'll see list of content of this author/user
3.
List (or view) should be invalidated only when this author/user is assigned
to some new published content. In case of updating the published content,
both previous and current author/user page should be invalidated.
4.
Placing tags like
`node_article_pub:field_author:{author_id}`
is excellent
way to deal with the problem. Everytime the new content is added which contain
this author,
you invalidate
`node_article_pub:field_author:123`
so only page
way to deal with the problem. Every
time the new content is added which contain
this author,
`node_article_pub:field_author:123`
is invalidated
so only page
of author/user 123 will be invalidated.
**NOTE:**
Module is only placing the tag in this case. The invalidation
behavior needs to be handled in custom module. Module cannot know how you
field is behaving and when you really want to invalidate such a page.
Example of custom hook on
`entity_insert`
:
```
php
/**
* Implements hook_entity_insert().
*
* Invalidates by field author. Only if field is set and node is published.
*/
function
module_entity_insert
(
EntityInterface
$entity
)
{
// If new entity us upublished there is nothing to invalidate.
if
(
empty
(
$entity
->
status
->
value
))
{
return
;
}
// If author is not filled there is nothing to invalidate.
if
(
empty
(
$entity
->
field_author
->
entity
))
{
return
;
}
$tag
=
'node_'
.
$entity
->
bundle
()
.
'_pub:field_author:'
.
$entity
->
field_author
->
entity
->
id
();
/** @var \Drupal\Core\Cache\CacheTagsInvalidator $tags_invalidator */
$tags_invalidator
=
\Drupal
::
service
(
'cache_tags.invalidator'
);
$tags_invalidator
->
invalidateTags
([
$tag
]);
}
```
This diff is collapsed.
Click to expand it.
cache_tools.module
+
27
−
9
View file @
0eb49149
...
...
@@ -19,23 +19,41 @@ function cache_tools_entity_type_alter(array &$entity_types) {
/**
* Implements hook_entity_insert().
*
* Invalidates `entitytype_entitybundle_pub` if entity is going to be published.
* Entity type needs to be allowed for invalidation.
* Invalidates `entitytype_entitybundle_pub` and
* `entitytype_entitybundle_pub:field_name:value` if entity is going to be
* published. Entity type or a field needs to be allowed for invalidation.
*/
function
cache_tools_entity_insert
(
EntityInterface
$entity
)
{
/** @var \Drupal\cache_tools\Service\CacheSanitizer $cacheSanitizer */
$cacheSanitizer
=
\Drupal
::
service
(
'cache_tools.cache.sanitizer'
);
$cacheSanitizer
->
invalidatePublishedEntity
(
$entity
);
/** @var \Drupal\cache_tools\Service\CacheSanitizer $cache_sanitizer */
$cache_sanitizer
=
\Drupal
::
service
(
'cache_tools.cache.sanitizer'
);
$cache_sanitizer
->
invalidatePublishedEntity
(
$entity
);
$cache_sanitizer
->
invalidatePublishedEntityFields
(
$entity
);
}
/**
* Implements hook_entity_update().
*
* Invalidates `entitytype_entitybundle_pub` if entity is going from unpublished
* state to published. Entity type needs to be allowed for invalidation.
* state to published and `entitytype_entitybundle_pub:field_name:value` if
* a published state and/or a configured field value is changed. Entity type or
* a field needs to be allowed for invalidation.
*/
function
cache_tools_entity_update
(
EntityInterface
$entity
)
{
/** @var \Drupal\cache_tools\Service\CacheSanitizer $cacheSanitizer */
$cacheSanitizer
=
\Drupal
::
service
(
'cache_tools.cache.sanitizer'
);
$cacheSanitizer
->
invalidatePublishedEntity
(
$entity
);
/** @var \Drupal\cache_tools\Service\CacheSanitizer $cache_sanitizer */
$cache_sanitizer
=
\Drupal
::
service
(
'cache_tools.cache.sanitizer'
);
$cache_sanitizer
->
invalidatePublishedEntity
(
$entity
);
$cache_sanitizer
->
invalidatePublishedEntityFields
(
$entity
);
}
/**
* Implements hook_entity_delete().
*
* Invalidates `entitytype_entitybundle_pub:field_name:value` if deleting
* published entity.
* Entity type or a field needs to be allowed for invalidation.
*/
function
cache_tools_entity_delete
(
EntityInterface
$entity
)
{
/** @var \Drupal\cache_tools\Service\CacheSanitizer $cache_sanitizer */
$cache_sanitizer
=
\Drupal
::
service
(
'cache_tools.cache.sanitizer'
);
$cache_sanitizer
->
invalidatePublishedEntityFields
(
$entity
);
}
This diff is collapsed.
Click to expand it.
src/Service/CacheSanitizer.php
+
134
−
10
View file @
0eb49149
...
...
@@ -3,8 +3,11 @@
namespace
Drupal\cache_tools\Service
;
use
Drupal\Core\Cache\CacheTagsInvalidatorInterface
;
use
Drupal\Core\Entity\EntityPublishedInterface
;
use
Drupal\Core\Entity\EntityInterface
;
use
Drupal\Core\Entity\EntityTypeInterface
;
use
Drupal\Core\Entity\FieldableEntityInterface
;
use
InvalidArgumentException
;
/**
* Sanitize cache tags.
...
...
@@ -140,29 +143,29 @@ class CacheSanitizer {
}
/**
* Get published cache tag in format `entitytype_
entitybundle_
pub`.
* Get published cache tag in format `entitytype_pub`.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* @param \Drupal\Core\Entity\Entity
Type
Interface $entity
Type
* Entity.
*
* @return string
* Published ache tag.
* Published
c
ache tag.
*/
public
function
getPublishedEntityCacheTag
(
EntityInterface
$entity
)
{
return
$entity
->
getEntityTypeId
()
.
'_'
.
$entity
->
bundle
()
.
'_pub'
;
public
function
getPublishedEntity
Type
CacheTag
(
Entity
Type
Interface
$entity
Type
)
{
return
$entity
Type
->
id
()
.
'_pub'
;
}
/**
* Get published cache tag in format `entitytype_pub`.
* Get published cache tag in format `entitytype_
entitybundle_
pub`.
*
* @param \Drupal\Core\Entity\Entity
Type
Interface $entity
Type
* @param \Drupal\Core\Entity\EntityInterface $entity
* Entity.
*
* @return string
* Published
c
ache tag.
* Published ache tag.
*/
public
function
getPublishedEntity
Type
CacheTag
(
Entity
Type
Interface
$entity
Type
)
{
return
$entity
Type
->
id
()
.
'_pub'
;
public
function
getPublishedEntityCacheTag
(
EntityInterface
$entity
)
{
return
$entity
->
getEntityTypeId
()
.
'_'
.
$entity
->
bundle
()
.
'_pub'
;
}
/**
...
...
@@ -214,4 +217,125 @@ class CacheSanitizer {
return
FALSE
;
}
/**
* Get field cache tags for configured fields having (modified) values.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* Entity.
* @param \Drupal\Core\Entity\FieldableEntityInterface|null $entity_compare
* (optional) An entity to compare field values with. When provided only
* non-equal field values will be considered.
*
* @return string[]
* The custom cache tags `entitytype_entitybundle_pub:field_name:value`.
*/
public
function
getPublishedEntityFieldsCacheTags
(
FieldableEntityInterface
$entity
,
FieldableEntityInterface
$entity_compare
=
NULL
)
{
$tags
=
[];
$entity_type
=
$entity
->
getEntityTypeId
();
// Get field-based tags configured for current entity bundle.
$bundle
=
$entity
->
bundle
();
$tag_prefix
=
$this
->
getPublishedEntityCacheTag
(
$entity
)
.
':'
;
foreach
(
$this
->
settings
[
'invalidate'
][
$entity_type
]
as
$cache_parameter
)
{
$parts
=
explode
(
':'
,
$cache_parameter
);
if
(
count
(
$parts
)
!=
2
||
$parts
[
0
]
!=
$bundle
)
{
// This setting is not for the current bundle or not field-based.
continue
;
}
$field_name
=
$parts
[
1
];
if
(
$entity
->
hasField
(
$field_name
))
{
// The name of the value property, e.g. 'value' or 'target_id'.
$key
=
$entity
->
getFieldDefinition
(
$field_name
)
->
getFieldStorageDefinition
()
->
getMainPropertyName
();
if
(
is_null
(
$key
))
{
// The field has no main value property.
continue
;
}
$tag_prefix_field
=
$tag_prefix
.
$field_name
.
':'
;
if
(
isset
(
$entity_compare
))
{
if
(
$entity
->
get
(
$field_name
)
->
getValue
()
===
$entity_compare
->
get
(
$field_name
)
->
getValue
())
{
// Skip unmodified field.
continue
;
}
if
(
!
$entity_compare
->
get
(
$field_name
)
->
isEmpty
())
{
// Add tag for the original field value.
/** @var \Drupal\Core\Field\EntityReferenceFieldItemList $field_items */
foreach
(
$entity_compare
->
get
(
$field_name
)
->
getValue
()
as
$value
)
{
$tags
[]
=
$tag_prefix_field
.
$value
[
$key
];
}
}
}
if
(
!
$entity
->
get
(
$field_name
)
->
isEmpty
())
{
// Add tag for the new field value.
/** @var \Drupal\Core\Field\EntityReferenceFieldItemList $field_items */
foreach
(
$entity
->
get
(
$field_name
)
->
getValue
()
as
$value
)
{
$tags
[]
=
$tag_prefix_field
.
$value
[
$key
];
}
}
}
}
return
$tags
;
}
/**
* Invalidates published entity field-based cache tags.
*
* Invalidates cache tags of the following format
* `entitytype_entitybundle_pub:field_name:value` during:
* 1. Insert published: all non-empty field values.
* 2. Delete published: all non-empty field values.
* 3. Update published: only the modified field values.
* 4. Update and publish: all non-empty field values.
* 5. Update and unpublish: all non-empty original field values.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* Entity.
*
* @return bool
* True if successful invalidation. False otherwise.
*/
public
function
invalidatePublishedEntityFields
(
EntityInterface
$entity
)
{
// Skip if entity type is not fieldable or not configured.
if
(
!
$entity
instanceof
FieldableEntityInterface
)
{
return
FALSE
;
}
$entity_type
=
$entity
->
getEntityTypeId
();
if
(
!
isset
(
$this
->
settings
[
'invalidate'
][
$entity_type
])
||
!
is_iterable
(
$this
->
settings
[
'invalidate'
][
$entity_type
]))
{
return
FALSE
;
}
// Determine published status and assume the entity is published if it
// doesn't implement the interface.
$is_published
=
$entity
instanceof
EntityPublishedInterface
?
$entity
->
isPublished
()
:
TRUE
;
if
(
isset
(
$entity
->
original
))
{
$was_published
=
$entity
->
original
instanceof
EntityPublishedInterface
?
$entity
->
original
->
isPublished
()
:
TRUE
;
}
else
{
$was_published
=
FALSE
;
}
// Get the cache tags depending on operation and published status.
$tags
=
[];
if
(
$is_published
&&
$was_published
)
{
// Update published: only the modified field values.
$tags
=
$this
->
getPublishedEntityFieldsCacheTags
(
$entity
,
$entity
->
original
);
}
elseif
(
$was_published
)
{
// Update and unpublish: all non-empty original field values.
$tags
=
$this
->
getPublishedEntityFieldsCacheTags
(
$entity
->
original
);
}
elseif
(
$is_published
)
{
// Insert or delete published, update and publish: all non-empty field
// values.
$entities
[]
=
$entity
;
$tags
=
$this
->
getPublishedEntityFieldsCacheTags
(
$entity
);
}
if
(
!
empty
(
$tags
))
{
// Invalidate all the selected tags.
$this
->
cacheTagInvalidator
->
invalidateTags
(
$tags
);
return
TRUE
;
}
return
FALSE
;
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment