Browse Source

Add View::cache()

This new method will allow arbitrary blocks of view code to be cached.
This allows some refactoring of element() and will also power cached
cells. Lastly, it gives developers a flexible way to incrementally add
caching to their responses if they have expensive to build page
fragments.

I've repurposed View::_elementCache() as its original purpose was a bit
silly. I've also removed the elementCacheSettings property which should
have never existed/been public in the first place.
Mark Story 11 years ago
parent
commit
e57c380568
2 changed files with 50 additions and 29 deletions
  1. 49 28
      src/View/View.php
  2. 1 1
      tests/TestCase/View/ViewTest.php

+ 49 - 28
src/View/View.php

@@ -207,15 +207,6 @@ class View {
 	public $elementCache = 'default';
 
 /**
- * Element cache settings
- *
- * @var array
- * @see View::_elementCache();
- * @see View::_renderElement
- */
-	public $elementCacheSettings = array();
-
-/**
  * List of variables to collect from the associated controller.
  *
  * @var array
@@ -360,18 +351,17 @@ class View {
  *   is false.
  */
 	public function element($name, array $data = array(), array $options = array()) {
-		if (!isset($options['callbacks'])) {
-			$options['callbacks'] = false;
-		}
-
+		$options += ['callbacks' => false, 'cache' => null];
 		if (isset($options['cache'])) {
-			$contents = $this->_elementCache($name, $data, $options);
-			if ($contents !== false) {
-				return $contents;
-			}
+			$options['cache'] = $this->_elementCache($name, $data, $options);
 		}
 
 		$file = $this->_getElementFilename($name);
+		if ($file && $options['cache']) {
+			return $this->cache(function() use ($file, $data, $options) {
+				echo $this->_renderElement($file, $data, $options);
+			}, $options['cache']);
+		}
 		if ($file) {
 			return $this->_renderElement($file, $data, $options);
 		}
@@ -385,6 +375,36 @@ class View {
 	}
 
 /**
+ * Create a cached block of view logic.
+ *
+ * This allows you to cache a block of view output into the cache
+ * defined in `elementCache`.
+ *
+ * This method will attempt to read the cache first. If the cache
+ * is empty, the $block will be run and the output stored.
+ *
+ * @param callable $block The block of code that you want to cache the output of.
+ * @param array $options The options defining the cache key etc.
+ * @return string The rendered content.
+ */
+	public function cache(callable $block, array $options = []) {
+		$options += ['key' => '', 'config' => $this->elementCache];
+		if (empty($options['key'])) {
+			throw new \RuntimeException('Cannot cache content with an empty key');
+		}
+		$result = Cache::read($options['key'], $options['config']);
+		if ($result) {
+			return $result;
+		}
+		ob_start();
+		$block();
+		$result = ob_get_clean();
+
+		Cache::write($options['key'], $result, $options['config']);
+		return $result;
+	}
+
+/**
  * Checks if an element exists
  *
  * @param string $name Name of template file in the /app/Template/Element/ folder,
@@ -1004,12 +1024,12 @@ class View {
 	}
 
 /**
- * Checks if an element is cached and returns the cached data if present
+ * Generate the cache configuration options for an element.
  *
  * @param string $name Element name
  * @param array $data Data
  * @param array $options Element options
- * @return string|null
+ * @return array Element Cache configuration.
  */
 	protected function _elementCache($name, $data, $options) {
 		$plugin = null;
@@ -1019,20 +1039,24 @@ class View {
 		if ($plugin) {
 			$underscored = Inflector::underscore($plugin);
 		}
-		$keys = array_merge(array($underscored, $name), array_keys($options), array_keys($data));
-		$this->elementCacheSettings = array(
+		$keys = array_merge(
+			array($underscored, $name),
+			array_keys($options),
+			array_keys($data)
+		);
+		$config = array(
 			'config' => $this->elementCache,
 			'key' => implode('_', $keys)
 		);
 		if (is_array($options['cache'])) {
 			$defaults = array(
 				'config' => $this->elementCache,
-				'key' => $this->elementCacheSettings['key']
+				'key' => $config['key']
 			);
-			$this->elementCacheSettings = $options['cache'] + $defaults;
+			$config = $options['cache'] + $defaults;
 		}
-		$this->elementCacheSettings['key'] = 'element_' . $this->elementCacheSettings['key'];
-		return Cache::read($this->elementCacheSettings['key'], $this->elementCacheSettings['config']);
+		$config['key'] = 'element_' . $config['key'];
+		return $config;
 	}
 
 /**
@@ -1062,9 +1086,6 @@ class View {
 		$this->_currentType = $restore;
 		$this->_current = $current;
 
-		if (isset($options['cache'])) {
-			Cache::write($this->elementCacheSettings['key'], $element, $this->elementCacheSettings['config']);
-		}
 		return $element;
 	}
 

+ 1 - 1
tests/TestCase/View/ViewTest.php

@@ -861,7 +861,7 @@ class ViewTest extends TestCase {
 			'path' => CACHE . 'views/',
 			'prefix' => ''
 		]);
-		Cache::clear(true, 'test_view');
+		Cache::clear(false, 'test_view');
 
 		$View = $this->PostsController->createView();
 		$View->elementCache = 'test_view';