Partial caching and <% require %>

Silverstripe Version: 3.7.3

Question:

Hi all, it seems to me that partial caching does not cache requirements. Consider the following code:

Page.ss, somewhere in HTML body:

<% cached 'cache key here' %>
    <% require javascript('themes/mytheme/javascript/test.js') %>
<% end_cached %>

test.js:

alert('TESTING');

Now if you test this in browser, you will notice that you will get an alert popup box saying ‘TESTING’. Then try to reload the same page again and it does not alert anything. This is because the JavaScript file is not included anymore, because SilverStripe caching does not store requirements - if I have understood correctly.

Does this same happen on SilverStripe 4? I haven’t tested it, but I’m asking if someone else knows.

Sorry, I’m in a little bit hurry, so I have to leave now. I will submit more information later if needed or edit this post is it’s unclear.

Thank you for your support!

1 Like

That makes an amount of sense to me. The cache only contains the actual content generated between the tags. The <% require %> is calling a method which adds the JS file to the assets, but that doesn’t actually make any difference to the generated content.

In reality, the requirements call needs to be outside the cache block so it it still processed by the viewer when the page is rendered.

Yes, it kind of makes sense. But as template structures and includes can get complex, it’s sometimes hard to see what requirements are needed by a specific cached block. If a cached block is big and contains many includes, it’s hard to keep track of the requirements.

I’m not necessarily suggesting any modifications to the core behaviour of the partial caching mechanism. I’m rather asking if anyone knows of any possible hooks in the partial caching system that would let me to program my own “requirements collector” that would keep track of require calls in cached blocks and reapply those when content is loaded from cache.

You should just be able to wrap your require tag in an <uncached> block… I can’t quite remember if that works for nested templates though. SS3 use to have some issues with that kind of thing - I recall that if $Layout was in a cache block (even if wrapped in an uncached block) things didn’t work as expected, so I always ended cache blocks at $Layout and started a new one after it. Not sure if SS4 has the same issues.

Thanks, I need to try that :).

This feels like a bug to me. Is this functionality still carried over to SS 4 and beyond? Sadly I’m still stuck on v3 and <% cached %> blocks will not repeat requirements that were called inside of them.

It’s too bad since the syntax is far more elegant than what I was doing before (in PHP instead of the template). Sadly, I wanted to start standardizing on this syntax since it’s much more succinct and easier to remember rather than having to workaround it with hacks. Unfortunately in my case <% uncached %> will not work for anything nested deeper that was also cached.

To me an intelligent solution would be to tokenize this in the HTML and then rewrite that HTML during final template rendering, but I can see how that might require a lot of work to revise how requirements are processed under the hood. :frowning:

I actually found a solution to this that works well with partial template caching. Essentially, it is my idea above of tokenizing the requirement and embedding it as a special comment in resulting HTML that then gets actually included as a call to Requirements:: during final processing.

Basically:

  • A custom instance of SSTemplateParser that is setup via Injector and overrides ->compileString(). I use that to find and then tokenize calls like <% require javascript(...) %> or <% require css(...) %> and converts them to something like <!-- requirements:javascript:path/to/file.js -->
  • A custom instance of DBHTMLText (for SSv4, or HTMLText for SSv3) which overrides ->forTemplate() to “hydrate” (so to speak) those tokenizes requirements references. This replaces the comments with an empty string on that last round (the final one where the </html> tag is found, for example) so that all that’s left are just the actual <link> and <script> references inserted by the Requirements backend in its ->includeInHTML() method.

I can provide code if desired, but this is here in case anyone else encounters this issue in the future. :slight_smile: :rocket: