mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
DOC Update Partial Template Cache documentation
- create a new documentation section explaining the "cached" block - clean up the Performance documentation section, moving explanations about "cached" block away, adding new warnings and recommendations
This commit is contained in:
parent
1eb66e2258
commit
e632ee3e52
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Caching
|
||||
summary: Reduce rendering time with cached templates and understand the limitations of the ViewableData object caching.
|
||||
summary: How template variables are cached.
|
||||
icon: rocket
|
||||
---
|
||||
|
||||
@ -35,13 +35,11 @@ When we render `$Counter` to the template we would expect the value to increase
|
||||
|
||||
## Partial caching
|
||||
|
||||
Partial caching is a feature that allows the caching of just a portion of a page. Instead of fetching the required data
|
||||
from the database to display, the contents of the area are fetched from a [cache backend](../performance/caching).
|
||||
Partial caching is a feature that allows caching of a portion of a page as a single string value. For more details read [its own documentation](partial_template_caching).
|
||||
|
||||
Example:
|
||||
```ss
|
||||
<% cached 'MyCachedContent', LastEdited %>
|
||||
$Title
|
||||
<% cached $CacheKey if $CacheCondition %>
|
||||
$CacheableContent
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
|
||||
|
@ -0,0 +1,335 @@
|
||||
---
|
||||
title: Partial Template Caching
|
||||
summary: Cache a section of a template Reduce rendering time with cached templates and understand the limitations of the ViewableData object caching.
|
||||
icon: tags
|
||||
---
|
||||
|
||||
## Partial template caching
|
||||
|
||||
Partial template caching is a feature that allows caching of rendered portions of templates. Cached content
|
||||
is fetched from a [cache backend](../performance/caching), instead of being regenerated repeatedly.
|
||||
|
||||
|
||||
### Base syntax
|
||||
|
||||
```ss
|
||||
<% cached $CacheKey if $CacheCondition %>
|
||||
$CacheableContent
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
This is not a definitive example of the syntax, but it shows the most common use case.
|
||||
|
||||
[note]
|
||||
See also [Complete Syntax definition](#complete-syntax-defintition) section
|
||||
[/note]
|
||||
|
||||
The key parts are `$CacheKey`, `$CacheCondition` and `$CacheableContent`.
|
||||
The following sections explain every one of them in more detail.
|
||||
|
||||
|
||||
#### $CacheKey
|
||||
|
||||
Defines a unique key for the cache storage.
|
||||
|
||||
[warning]
|
||||
Avoid heavy computations in `$CacheKey` as it is evaluated for every template render.
|
||||
[/warning]
|
||||
|
||||
The formal definition is
|
||||
- Optional list of template expressions delimited by comma
|
||||
|
||||
The syntax is
|
||||
|
||||
```ss
|
||||
<% cached [$key1[, $key2[, ...[, $keyN]]]] ... %>
|
||||
```
|
||||
|
||||
The final value is concatenated by the Template Engine into a string. When doing so, Template Engine
|
||||
adds some extra values to the mix to make it more unique and prevent clashing between cache keys from
|
||||
different templates.
|
||||
|
||||
Here is how it works in detail:
|
||||
|
||||
1. `SilverStripe\View\SSViewer::$global_key` hash
|
||||
|
||||
With the current template context, value of the `$global_key` variable is rendered into a string and hashed.
|
||||
|
||||
`$global_key` content is inserted into the template "as is" at the compilation stage. Changing its value
|
||||
won't have any effect until template recompilation (e.g. on cache flush).
|
||||
|
||||
By default it equals to `'$CurrentReadingMode, $CurrentUser.ID'`.
|
||||
This ensures the current [Versioned](api:SilverStripe\Versioned\Versioned) state and user ID are used.
|
||||
At runtime that will become something like `'LIVE, 0'` (for unauthenticated users in live mode).
|
||||
|
||||
As usual, you may override its value via YAML configs. For example:
|
||||
|
||||
```yml
|
||||
# app/_config/view.yml
|
||||
SilverStripe\View\SSViewer:
|
||||
global_key: '$CurrentReadingMode, $CurrentUser.ID, $Locale'
|
||||
```
|
||||
|
||||
2. Block hash
|
||||
|
||||
Everything between the `<% cached %> ... <% end_cached %>` is taken as text (with no rendering) and hashed.
|
||||
|
||||
This is done at the template compilation stage, so
|
||||
the compiled version of the template contains the hash precalculated.
|
||||
|
||||
`Block hash` main purpose is to invalidate cache when template itself changes.
|
||||
|
||||
3. `$CacheKey` hash
|
||||
|
||||
All keys of `$CacheKey` are processed, concatenated and the final value is hashed.
|
||||
If there are no values defined, this step is skipped.
|
||||
|
||||
4. Make the final key vaule
|
||||
|
||||
A string produced by concatenation of all the values mentioned above is used as the final value.
|
||||
|
||||
Even if `$CacheKey` is omitted, `SilverStripe\View\SSViewer::$global_key` and `Block hash` values are still
|
||||
getting used to generate cache key for the caching backend storage.
|
||||
|
||||
[note]
|
||||
##### Cache key calculated in controller
|
||||
|
||||
If your caching logic is complex or re-usable, you can define a method on your controller to generate a cache key
|
||||
fragment.
|
||||
|
||||
For example, a block that shows a collection of rotating slides needs to update whenever the relationship
|
||||
`Page::$many_many = ['Slides' => 'Slide']` changes. In `PageController`:
|
||||
|
||||
|
||||
```php
|
||||
public function SliderCacheKey()
|
||||
{
|
||||
$fragments = [
|
||||
'Page-Slides',
|
||||
$this->ID,
|
||||
// identify which objects are in the list and their sort order
|
||||
implode('-', $this->Slides()->Column('ID')),
|
||||
// works for both has_many and many_many relationships
|
||||
$this->Slides()->max('LastEdited')
|
||||
];
|
||||
return implode('-_-', $fragments);
|
||||
}
|
||||
```
|
||||
|
||||
Then reference that function in the cache key:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached $SliderCacheKey if ... %>
|
||||
```
|
||||
[/note]
|
||||
|
||||
|
||||
#### $CacheCondition
|
||||
|
||||
Defines if caching is required for the block.
|
||||
|
||||
Condition is optional and if omitted, `true` is implied.
|
||||
|
||||
If the value is `false`, the block skips `$CacheKey` evaluation completely, does not lookup
|
||||
the data in the cache storage, neither preserve any data in the storage.
|
||||
The template within the block keeps working as is, same as it would do without
|
||||
`<% cached %>` block surrounding it.
|
||||
|
||||
Although `$CacheCondition` is optional, it is highly recommended. For example,
|
||||
if you use `$DataObject->ID` as your `$CacheKey`, you may use
|
||||
`$DataObject->ID > 0` as the condition.
|
||||
|
||||
Without it:
|
||||
- your cache backend will always be queried for cache (for every template render)
|
||||
- your cache backend may be cluttered with redundant and useless data
|
||||
|
||||
|
||||
[warning]
|
||||
The `$CacheCondition` value is evaluated on every template render and should be as lightweight as possible.
|
||||
[/warning]
|
||||
|
||||
|
||||
#### $CacheableContent
|
||||
|
||||
The content block may contain any usual template syntax.
|
||||
|
||||
|
||||
### Cache storage
|
||||
|
||||
The cache storage may be re-configured via `Psr\SimpleCache\CacheInterface.cacheblock` key for [Injector](../extending/injector).
|
||||
By default, it is initialised by `SilverStripe\Core\Cache\DefaultCacheFactory` with the following parameters:
|
||||
|
||||
- `namespace: "cacheblock"`
|
||||
- `defaultLifetime: 600`
|
||||
|
||||
[note]
|
||||
The defaultLifetime 600 means every cache record expires in 10 minutes.
|
||||
If you have good `$CacheKey` and `$CacheCondition` implementations, you may want to tune these settings to
|
||||
improve performance.
|
||||
[/note]
|
||||
|
||||
|
||||
### Nested cached blocks
|
||||
|
||||
Every nested cache block is processed independently.
|
||||
|
||||
Let's consider the following example:
|
||||
```ss
|
||||
<% cached $PageKey %>
|
||||
<!-- Header -->
|
||||
<% cached $BodyKey %> <!-- Body --> <% end_cached %>
|
||||
<!-- Footer -->
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
The template processor will transparently flatten the structure into something similar to the following pseudo-code:
|
||||
|
||||
```ss
|
||||
<% cached $PageKey %><!-- Header --><% end_block %>
|
||||
<% cached $BodyKey %><!-- Body --><% end_cached %>
|
||||
<% cached $PageKey %><!-- Footer --><% end_cached %>
|
||||
```
|
||||
|
||||
[note]
|
||||
`$PageKey` is used twice, but evaluated only once per render because of [template object caching](caching/#object-caching).
|
||||
[/note]
|
||||
|
||||
|
||||
### Uncached
|
||||
|
||||
The tag `<% uncached %> ... <% end_uncached %>` disables caching for its content.
|
||||
|
||||
```ss
|
||||
<% cached $PageKey %>
|
||||
<!-- Header -->
|
||||
<% uncached %><!-- Body --><% end_uncached %>
|
||||
<!-- Footer -->
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
Because of the nested block flattening (see above), it works seamlessly on any level of depth.
|
||||
|
||||
[warning]
|
||||
The `uncached` block only works on the lexical level.
|
||||
If you have a template that caches content rendering another template with included uncached blocks,
|
||||
those will not have any effect on the parent template caching blocks.
|
||||
[/warning]
|
||||
|
||||
|
||||
### Nesting in FOR and IF blocks
|
||||
|
||||
Currently, a cache block cannot be included in `if` and `loop` blocks.
|
||||
The template engine will throw an error letting you know if you've done this.
|
||||
|
||||
[note]
|
||||
You may often get around this using aggregates or by un-nesting the block.
|
||||
|
||||
E.g.
|
||||
|
||||
```
|
||||
<% cached $LastEdited %>
|
||||
<% loop $Children %>
|
||||
<% cached $LastEdited %>
|
||||
$Name
|
||||
<% end_cached %>
|
||||
<% end_loop %>
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
Might be re-written as something like that:
|
||||
|
||||
```
|
||||
<% cached $LastEdited %>
|
||||
<% cached $AllChildren.max('LastEdited') %>
|
||||
<% loop $Children %>
|
||||
$Name
|
||||
<% end_loop %>
|
||||
<% end_cached %>
|
||||
<% end_cached %>
|
||||
```
|
||||
[/note]
|
||||
|
||||
|
||||
### Unless (syntax sugar)
|
||||
|
||||
`if` keyword may be swapped with keyword `unless`, which inverts the boolean value evaluation.
|
||||
|
||||
The two following forms produce the same result
|
||||
|
||||
```ss
|
||||
<% cached unless $Key %>
|
||||
"unless $Cond" === "if not $Cond"
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached if not $Key %>
|
||||
"unless $Cond" === "if not $Cond"
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
|
||||
### Complete Syntax definition
|
||||
|
||||
```ss
|
||||
<% [un]cached [$CacheKey[, ...]] [(if|unless) $CacheCondition] %>
|
||||
$CacheContent
|
||||
<% end_[un]cached %>
|
||||
```
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
```ss
|
||||
<% cached %>
|
||||
The key is: hash of the template code within the block with $global_key.
|
||||
This content is always cached.
|
||||
<% end_cache %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached $Key %>
|
||||
Cached separately for every distinct $Key value
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached $KeyA, $KeyB %>
|
||||
Cached separately for every combination of $KeyA and $KeyB
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached $Key if $Cond %>
|
||||
Cached only if $Cond == true
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached $Key unless $Cond %>
|
||||
Cached only if $Cond == false
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached $Key if not $Cond %>
|
||||
Cached only if $Cond == false
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
```ss
|
||||
<% cached 'contentblock', $LastEdited, $CurrentMember.ID if $CurrentMember && not $CurrentMember.isAdmin %>
|
||||
<!--
|
||||
Hash of this content block is also included
|
||||
into the final Cache Key value along with
|
||||
SilverStripe\View\SSViewer::$global_key
|
||||
-->
|
||||
<% uncached %>
|
||||
This text is always dynamic (never cached)
|
||||
<% end_uncached %>
|
||||
<!--
|
||||
This bit is cached again
|
||||
-->
|
||||
<% end_cached %>
|
||||
```
|
@ -6,124 +6,107 @@ icon: tachometer-alt
|
||||
|
||||
# Partial Caching
|
||||
|
||||
Partial caching is a feature that allows the caching of just a portion of a page.
|
||||
[Partial template caching](../templates/partial_template_caching) is a feature that allows caching of rendered portions a template.
|
||||
|
||||
|
||||
## Cache block conditionals
|
||||
|
||||
Use conditions whenever possible. The cache tag supports defining conditions via either `if` or `unless` keyword.
|
||||
Those are optional, however is highly recommended.
|
||||
|
||||
[warning]
|
||||
Avoid performing heavy computations in conditionals, as they are evaluated for every template rendering.
|
||||
[/warning]
|
||||
|
||||
If you cache without conditions:
|
||||
- your cache backend will always be queried for the cache block (on every template render)
|
||||
- your cache may be cluttered with heaps of redundant and useless data (especially the default filesystem backend)
|
||||
|
||||
As an example, if you use `$DataObject->ID` as a key for the block, consider adding a condition that ID is greater than zero:
|
||||
|
||||
```ss
|
||||
<% cached 'CacheKey' %>
|
||||
$DataTable
|
||||
...
|
||||
<% end_cached %>
|
||||
<% cached $MenuItem.ID if $MenuItem.ID > 0 %>
|
||||
```
|
||||
|
||||
Each cache block has a cache key. A cache key is an unlimited number of comma separated variables and quoted strings.
|
||||
Every time the cache key returns a different result, the contents of the block are recalculated. If the cache key is
|
||||
the same as a previous render, the cached value stored last time is used.
|
||||
|
||||
Since the above example contains just one argument as the cache key, a string (which will be the same every render) it
|
||||
will invalidate the cache after a given amount of time has expired (default 10 minutes).
|
||||
|
||||
Here are some more complex examples:
|
||||
|
||||
To cache the contents of a page for all anonymous users, but dynamically calculate the contents for logged in members,
|
||||
use something like:
|
||||
|
||||
```ss
|
||||
<% cached 'database', $LastEdited %>
|
||||
<!-- that updates every time the record changes. -->
|
||||
<% end_cached %>
|
||||
|
||||
<% cached 'loginblock', $CurrentMember.ID %>
|
||||
<!-- cached unique to the user. i.e for user 2, they will see a different cache to user 1 -->
|
||||
<% end_cached %>
|
||||
|
||||
<% cached 'loginblock', $LastEdited, $CurrentMember.isAdmin %>
|
||||
<!-- recached when block object changes, and if the user is admin -->
|
||||
<% end_cached %>
|
||||
<% cached unless $CurrentUser %>
|
||||
```
|
||||
|
||||
An additional global key is incorporated in the cache lookup. The default value for this is
|
||||
`$CurrentReadingMode, $CurrentUser.ID`. This ensures that the current [Versioned](api:SilverStripe\Versioned\Versioned) state and user ID are used.
|
||||
This may be configured by changing the config value of `SSViewer.global_key`. It is also necessary to flush the
|
||||
template caching when modifying this config, as this key is cached within the template itself.
|
||||
|
||||
For example, to ensure that the cache is configured to respect another variable, and if the current logged in
|
||||
user does not influence your template content, you can update this key as below;
|
||||
|
||||
**app/_config/app.yml**
|
||||
|
||||
|
||||
```yml
|
||||
SilverStripe\View\SSViewer:
|
||||
global_key: '$CurrentReadingMode, $Locale'
|
||||
```
|
||||
|
||||
## Aggregates
|
||||
|
||||
Often you want to invalidate a cache when any object in a set of objects change, or when the objects in a relationship
|
||||
change. To do this, SilverStripe introduces the concept of Aggregates. These calculate and return SQL aggregates
|
||||
on sets of [DataObject](api:SilverStripe\ORM\DataObject)s - the most useful for us being the `max` aggregate.
|
||||
Sometimes you may want to invalidate cache when any object in a set changes, or when objects in a relationship
|
||||
change. To do this, you may use [DataList](api:SilverStripe\ORM\DataList) aggregate methods (which we call Aggregates).
|
||||
These perform SQL aggregate queries on sets of [DataObject](api:SilverStripe\ORM\DataObject)s.
|
||||
|
||||
For example, if we have a menu, we want that menu to update whenever _any_ page is edited, but would like to cache it
|
||||
Here are some useful methods of the [DataList](api:SilverStripe\ORM\DataList) class:
|
||||
- `int count()` : Return the number of items in this DataList
|
||||
- `mixed max(string $fieldName)` : Return the maximum value of the given field in this DataList
|
||||
- `mixed min(string $fieldName)` : Return the minimum value of the given field in this DataList
|
||||
- `mixed avg(string $fieldName)` : Return the average value of the given field in this DataList
|
||||
- `mixed sum(string $fieldName)` : Return the sum of the values of the given field in this DataList
|
||||
|
||||
To construct a `DataList` over a `DataObject`, we have a global template variable called `$List`.
|
||||
|
||||
For example, if we have a menu, we may want that menu to update whenever _any_ page is edited, but would like to cache it
|
||||
otherwise. By using aggregates, we do that like this:
|
||||
|
||||
```ss
|
||||
<% cached
|
||||
'navigation',
|
||||
$List('SilverStripe\CMS\Model\SiteTree').max('LastEdited'),
|
||||
$List('SilverStripe\CMS\Model\SiteTree').count()
|
||||
%>
|
||||
```
|
||||
|
||||
The cache for this will update whenever a page is added, removed or edited.
|
||||
|
||||
[note]
|
||||
The use of the fully qualified classname is necessary.
|
||||
[/note]
|
||||
|
||||
[note]
|
||||
The use of both `.max('LastEdited')` and `.count()` makes sure we check for any object
|
||||
edited or deleted since the cache was last built.
|
||||
[/note]
|
||||
|
||||
[warning]
|
||||
Be careful using aggregates. Remember that the database is usually one of the performance bottlenecks.
|
||||
Keep in mind that every key of every cached block is recalculated for every template render, regardless of caching
|
||||
result. Aggregating SQL queries are usually produce more load on the database than simple select queries,
|
||||
especially if you query records by Primary Key or join tables using database indices properly.
|
||||
|
||||
Sometimes it may be cheaper to not cache altogether, rather than cache a block using a bunch of heavy aggregating SQL
|
||||
queries.
|
||||
|
||||
Let us consider two versions:
|
||||
|
||||
```ss
|
||||
<% cached 'navigation', $List('SilverStripe\CMS\Model\SiteTree').max('LastEdited'), $List('SilverStripe\CMS\Model\SiteTree').count() %>
|
||||
# Version 1 (bad)
|
||||
|
||||
<% cached
|
||||
$List('SilverStripe\CMS\Model\SiteTree').max('LastEdited'),
|
||||
$List('SilverStripe\CMS\Model\SiteTree').count()
|
||||
%>
|
||||
Parent title is: $Me.Parent.Title
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
The cache for this will update whenever a page is added, removed or edited. (Note: The use of the fully qualified classname is necessary).
|
||||
|
||||
If we have a block that shows a list of categories, we can make sure the cache updates every time a category is added
|
||||
or edited:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached 'categorylist', $List('Category').max('LastEdited'), $List('Category').count() %>
|
||||
# Version 2 (better performance than Version 1)
|
||||
|
||||
Parent title is: $Me.Parent.Title
|
||||
```
|
||||
|
||||
[notice]
|
||||
Note the use of both `.max('LastEdited')` and `.count()` - this takes care of both the case where an object has been
|
||||
edited since the cache was last built, and also when an object has been deleted since the cache was last built.
|
||||
[/notice]
|
||||
`Version 1` always generates two heavy aggregating SQL queries for the database on every
|
||||
template render.
|
||||
`Version 2` always generates a single and more performant SQL query fetching the record by its Primary Key.
|
||||
|
||||
We can also calculate aggregates on relationships. The logic for that can get a bit complex, so we can extract that on
|
||||
to the controller so it's not cluttering up our template.
|
||||
[/warning]
|
||||
|
||||
## Cache key calculated in controller
|
||||
|
||||
If your caching logic is complex or re-usable, you can define a method on your controller to generate a cache key
|
||||
fragment.
|
||||
|
||||
For example, a block that shows a collection of rotating slides needs to update whenever the relationship
|
||||
`Page::$many_many = ['Slides' => 'Slide']` changes. In `PageController`:
|
||||
|
||||
|
||||
```php
|
||||
public function SliderCacheKey()
|
||||
{
|
||||
$fragments = [
|
||||
'Page-Slides',
|
||||
$this->ID,
|
||||
// identify which objects are in the list and their sort order
|
||||
implode('-', $this->Slides()->Column('ID')),
|
||||
$this->Slides()->max('LastEdited')
|
||||
];
|
||||
return implode('-_-', $fragments);
|
||||
}
|
||||
```
|
||||
|
||||
Then reference that function in the cache key:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached $SliderCacheKey %>
|
||||
```
|
||||
|
||||
The example above would work for both a has_many and many_many relationship.
|
||||
|
||||
## Cache blocks and template changes
|
||||
|
||||
In addition to the key elements passed as parameters to the cached control, the system automatically includes the
|
||||
template name and a sha1 hash of the contents of the cache block in the key. This means that any time the template is
|
||||
changed the cached contents will automatically refreshed.
|
||||
|
||||
## Purposely stale data
|
||||
|
||||
@ -157,126 +140,36 @@ and then use it in the cache key
|
||||
<% cached 'blogstatistics', $Blog.ID, $BlogStatisticsCounter %>
|
||||
```
|
||||
|
||||
## Cache block conditionals
|
||||
|
||||
You may wish to conditionally enable or disable caching. To support this, in cached tags you may (after any key
|
||||
arguments) specify 'if' or 'unless' followed by a standard template variable argument. If 'if' is used, the resultant
|
||||
value must be true for that block to be cached. Conversely if 'unless' is used, the result must be false.
|
||||
## Cache backend
|
||||
|
||||
Following on from the previous example, you might wish to only cache slightly-stale data if the server is experiencing
|
||||
heavy load:
|
||||
The template engine uses [Injector](../extending/injector) service `Psr\SimpleCache\CacheInterface.cacheblock` as
|
||||
caching backend. The default definition of that service is very conservative and relies on the server filesystem.
|
||||
This is the most common denominator for most of the applications out there. However,
|
||||
this is not the most robust neither performant cache implementation. If you have a better solution
|
||||
available on your platform, you should consider tuning that setting for your application.
|
||||
All you need to do to swap the cache backend for partial template cache blocks is to redefine this service for the Injector.
|
||||
|
||||
Here's an example of how it could be done:
|
||||
|
||||
```ss
|
||||
<% cached 'blogstatistics', $Blog.ID if $HighLoad %>
|
||||
```yml
|
||||
# app/_config/cache.yml
|
||||
|
||||
---
|
||||
Name: app-cache
|
||||
After:
|
||||
- 'corecache'
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
Psr\SimpleCache\CacheInterface.cacheblock: '%$App\Cache\Service.memcached'
|
||||
```
|
||||
|
||||
By adding a `HighLoad` function to your `PageController`, you could enable or disable caching dynamically.
|
||||
|
||||
To cache the contents of a page for all anonymous users, but dynamically calculate the contents for logged in members,
|
||||
use something like:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached unless $CurrentUser %>
|
||||
```
|
||||
|
||||
## Uncached
|
||||
|
||||
The template tag 'uncached' can be used - it is the exact equivalent of a cached block with an if condition that always
|
||||
returns false. The key and conditionals in an uncached tag are ignored, so you can easily temporarily disable a
|
||||
particular cache block by changing just the tag, leaving the key and conditional intact.
|
||||
|
||||
|
||||
```ss
|
||||
<% uncached %>
|
||||
```
|
||||
|
||||
## Nested cache blocks
|
||||
|
||||
You can also nest independent cache blocks Any nested cache blocks are calculated independently from their containing
|
||||
block, regardless of the cached state of that container.
|
||||
|
||||
This allows you to wrap an entire page in a cache block on the page's LastEdited value, but still keep a member-specific
|
||||
portion dynamic, without having to include any member info in the page's cache key.
|
||||
|
||||
An example:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached $LastEdited %>
|
||||
Our wonderful site
|
||||
|
||||
<% cached $Member.ID %>
|
||||
Welcome $Member.Name
|
||||
<% end_cached %>
|
||||
|
||||
$ASlowCalculation
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
This will cache the entire outer section until the next time the page is edited, but will display a different welcome
|
||||
message depending on the logged in member.
|
||||
|
||||
Cache conditionals and the uncached tag also work in the same nested manner. Since Member.Name is fast to calculate, you
|
||||
could also write the last example as:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached $LastEdited %>
|
||||
Our wonderful site
|
||||
|
||||
<% uncached %>
|
||||
Welcome $Member.Name
|
||||
<% end_uncached %>
|
||||
|
||||
$ASlowCalculation
|
||||
<% end_cached %>
|
||||
```
|
||||
[note]
|
||||
For the above example to work it is necessary to have the Injector service `App\Cache\Service.memcached` defined somewhere in the configs.
|
||||
[/note]
|
||||
|
||||
[warning]
|
||||
Currently a nested cache block can not be contained within an if or loop block. The template engine will throw an error
|
||||
letting you know if you've done this. You can often get around this using aggregates or by un-nesting the block.
|
||||
The default filesystem cache backend does not support auto cleanup of the residual files with expired cache records.
|
||||
If your project relies on Template Caching heavily (e.g. thousands of cache records daily), you may want to keep en eye on the
|
||||
filesystem storage. Sooner or later its capacity may be exhausted.
|
||||
[/warning]
|
||||
|
||||
Failing example:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached $LastEdited %>
|
||||
|
||||
<% loop $Children %>
|
||||
<% cached $LastEdited %>
|
||||
$Name
|
||||
<% end_cached %>
|
||||
<% end_loop %>
|
||||
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
Can be re-written as:
|
||||
|
||||
|
||||
```ss
|
||||
<% cached $LastEdited %>
|
||||
<% cached $AllChildren.max('LastEdited') %>
|
||||
<% loop $Children %>
|
||||
$Name
|
||||
<% end_loop %>
|
||||
<% end_cached %>
|
||||
<% end_cached %>
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```ss
|
||||
<% cached $LastEdited %>
|
||||
(other code)
|
||||
<% end_cached %>
|
||||
|
||||
<% loop $Children %>
|
||||
<% cached $LastEdited %>
|
||||
$Name
|
||||
<% end_cached %>
|
||||
<% end_loop %>
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user