Skip to content

Instantly share code, notes, and snippets.

@bwaidelich
Created November 13, 2013 11:43
Show Gist options
  • Select an option

  • Save bwaidelich/7447752 to your computer and use it in GitHub Desktop.

Select an option

Save bwaidelich/7447752 to your computer and use it in GitHub Desktop.
A simple TYPO3 Fluid ViewHelper that caches its content based on some variables
Your_Package_ViewHelpers_CacheViewHelper_CacheFrontend:
frontend: TYPO3\Flow\Cache\Frontend\StringFrontend
backend: TYPO3\Flow\Cache\Backend\SimpleFileBackend
<?php
namespace Your\Package\ViewHelpers;
/* *
* This script belongs to the TYPO3 Flow package "Your.Package". *
* */
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Cache\Frontend\StringFrontend;
use TYPO3\Flow\Persistence\PersistenceManagerInterface;
use TYPO3\Flow\Utility\Arrays;
use TYPO3\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\Fluid\Core\ViewHelper\Exception;
use TYPO3\Fluid\Core\ViewHelper\Facets\ChildNodeAccessInterface;
/**
* View Helper that caches all its contents based on some (optional) variables
*
* = Examples =
*
* <code title="independent cache w/o variables">
* <x:cache>This is cached</x:cache>
* </code>
* <output>
* This is cached
* // will be cached until the caches are flushed manually
* </output>
*
* <code title="Cache based on a variable">
* <x:cache variables="{user: user}">This is cached: {user.name}</x:cache>
* </code>
* <output>
* This is cached: <bar>
* // depending on the {user} identity but NOT depending on the users name! To achieve this see next example
* </output>
*
* <code title="Cache based on multiple variables">
* <x:cache variables="{user: user, name: user.name}">This is cached: {user.name}</x:cache>
* </code>
* <output>
* This is cached: <bar>
* // depending on the {user} identity *and* it's name. If you only specify the name, all occurrences with the same name variable will create the same cache identifier
* </output>
*
* <code title="Inline syntax">
* {user.name -> x:cache(variables: '{user: user}')}
* </code>
* <output>
* John Doe
* // depending on the {user} identity
* </output>
*/
class CacheViewHelper extends AbstractViewHelper implements ChildNodeAccessInterface {
/**
* @Flow\Inject
* @var PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @Flow\Inject
* @var StringFrontend
*/
protected $cacheFrontend;
/**
* @var array<AbstractNode>
*/
private $childNodes = array();
/**
* Renders the child nodes of this ViewHelper or fetches those from the cache, if found
*
* @param array $variables variables that affect the cache identifier
* @param string $value optional content that will be used instead of child nodes if specified
* @return string
*/
public function render(array $variables, $value = NULL) {
$cacheIdentifier = $this->buildCacheIdentifier();
if (!$this->cacheFrontend->has($cacheIdentifier)) {
if ($value === NULL) {
$value = $this->renderChildren();
}
$this->cacheFrontend->set($cacheIdentifier, $value);
return $value;
}
return $this->cacheFrontend->get($cacheIdentifier);
}
/**
* Sets the direct child nodes of the current syntax tree node.
*
* @param array<AbstractNode> $childNodes
* @return void
*/
public function setChildNodes(array $childNodes) {
$this->childNodes = $childNodes;
}
/**
* @return string
*/
protected function buildCacheIdentifier() {
$variables = $this->convertObjectsToHashes($this->arguments['variables']);
Arrays::sortKeysRecursively($variables);
return md5(http_build_query($variables));
}
/**
* Recursively converts objects in an array to their identifiers
*
* @param array $values the array to be processed
* @return array the modified array
* @throws Exception if $values contain an object and its identifier could not be determined
*/
protected function convertObjectsToHashes(array $values) {
foreach ($values as &$value) {
if (is_object($value)) {
$identifier = $this->persistenceManager->getIdentifierByObject($value);
if ($identifier === NULL) {
throw new Exception(sprintf('The identifier of an object of type "%s" could not be determined', get_class($value)), 1384341157);
}
$value = $identifier;
} elseif (is_array($value)) {
$value = $this->convertObjectsToHashes($value);
}
}
return $values;
}
}
Your\Package\ViewHelpers\CacheViewHelper:
properties:
cacheFrontend:
object:
factoryObjectName: 'TYPO3\Flow\Cache\CacheManager'
factoryMethodName: 'getCache'
arguments:
1:
value: 'Your_Package_ViewHelpers_CacheViewHelper_CacheFrontend'
@bwaidelich
Copy link
Author

Usage:

  {namespace x=Your\Package\ViewHelpers}
  <x:cache variables="{post: post, title: post.title}">
    <h3>{post.title}</h3>
    <p>more complex content that takes time to render</p>
  </x:cache>

Disclaimer: Only use this as preliminary to increase performance - it is error prone and to be replaced with proper caching mechanisms!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment