Browse Source

Extracting reusable logic from Query, QueryCacher and ResultSetDecorator
out into the Datasource namespace

Jose Lorenzo Rodriguez 12 years ago
parent
commit
06d6343a76

+ 7 - 10
src/ORM/QueryCacher.php

@@ -1,7 +1,5 @@
 <?php
 /**
- * PHP Version 5.4
- *
  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  *
@@ -14,13 +12,12 @@
  * @since         CakePHP(tm) v 3.0.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-namespace Cake\ORM;
+namespace Cake\Datasource;
 
 use Cake\Cache\Cache;
 use Cake\Cache\CacheEngine;
-use Cake\ORM\Query;
-use Cake\ORM\ResultSet;
 use RuntimeException;
+use Traversable;
 
 /**
  * Handles caching queries and loading results from the cache.
@@ -53,10 +50,10 @@ class QueryCacher {
 /**
  * Load the cached results from the cache or run the query.
  *
- * @param Query $query The query the cache read is for.
+ * @param object $query The query the cache read is for.
  * @return ResultSet|null Either the cached results or null.
  */
-	public function fetch(Query $query) {
+	public function fetch($query) {
 		$key = $this->_resolveKey($query);
 		$storage = $this->_resolveCacher();
 		$result = $storage->read($key);
@@ -69,11 +66,11 @@ class QueryCacher {
 /**
  * Store the result set into the cache.
  *
- * @param Query $query The query the cache read is for.
- * @param ResultSet The result set to store.
+ * @param object $query The query the cache read is for.
+ * @param \Traversable The result set to store.
  * @return void
  */
-	public function store(Query $query, ResultSet $results) {
+	public function store($query, Traversable $results) {
 		$key = $this->_resolveKey($query);
 		$storage = $this->_resolveCacher();
 		return $storage->write($key, $results);

+ 363 - 0
src/Datasource/QueryTrait.php

@@ -0,0 +1,363 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         CakePHP(tm) v 3.0.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+namespace Cake\Datasource;
+
+use Cake\Collection\Iterator\MapReduce;
+use Cake\Datasource\QueryCacher;
+use Cake\Datasource\RepositoryInterface;
+use Cake\Datasource\ResultSetDecorator;
+use Cake\Event\Event;
+use Cake\ORM\ResultSet;
+
+/**
+ * Contains the characteristics for an object that is attached to a repository and
+ * can retrieve results based on any criteria.
+ *
+ */
+trait QueryTrait {
+
+/**
+ * Instance of a table object this query is bound to
+ *
+ * @var \Cake\Datasource\Repository
+ */
+	protected $_repository;
+
+/**
+ * A ResultSet.
+ *
+ * When set, query execution will be bypassed.
+ *
+ * @var Cake\ORM\ResultSet
+ * @see setResult()
+ */
+	protected $_results;
+
+/**
+ * List of map-reduce routines that should be applied over the query
+ * result
+ *
+ * @var array
+ */
+	protected $_mapReduce = [];
+
+/**
+ * List of formatter classes or callbacks that will post-process the
+ * results when fetched
+ *
+ * @var array
+ */
+	protected $_formatters = [];
+
+/**
+ * A query cacher instance if this query has caching enabled.
+ *
+ * @var Cake\Datasource\QueryCacher
+ */
+	protected $_cache;
+
+/**
+ * Returns the default table object that will be used by this query,
+ * that is, the table that will appear in the from clause.
+ *
+ * When called with a Table argument, the default table object will be set
+ * and this query object will be returned for chaining.
+ *
+ * @param \Cake\Datasource\RepositoryInterface $table The default table object to use
+ * @return \Cake\Datasource\RepositoryInterface|Query
+ */
+	public function repository(RepositoryInterface $table = null) {
+		if ($table === null) {
+			return $this->_repository;
+		}
+		$this->_repository = $table;
+		return $this;
+	}
+
+/**
+ * Set the result set for a query.
+ *
+ * Setting the resultset of a query will make execute() a no-op. Instead
+ * of executing the SQL query and fetching results, the ResultSet provided to this
+ * method will be returned.
+ *
+ * This method is most useful when combined with results stored in a persistent cache.
+ *
+ * @param Cake\ORM\ResultSet $results The results this query should return.
+ * @return Query The query instance.
+ */
+	public function setResult($results) {
+		$this->_results = $results;
+		return $this;
+	}
+
+/**
+ * Executes this query and returns a results iterator. This function is required
+ * for implementing the IteratorAggregate interface and allows the query to be
+ * iterated without having to call execute() manually, thus making it look like
+ * a result set instead of the query itself.
+ *
+ * @return Iterator
+ */
+	public function getIterator() {
+		return $this->all();
+	}
+
+/**
+ * Get the result set for this query.
+ *
+ * Will return either the results set through setResult(), or execute this query
+ * and return the ResultSetDecorator object ready for streaming of results.
+ *
+ * @return Cake\Datasource\ResultSetDecorator
+ */
+	public function getResults() {
+		if (isset($this->_results)) {
+			return $this->_results;
+		}
+
+		$table = $this->repository();
+		$event = new Event('Model.beforeFind', $table, [$this, $this->_options, true]);
+		$table->getEventManager()->dispatch($event);
+
+		if (isset($this->_results)) {
+			return $this->_results;
+		}
+
+		if ($this->_cache) {
+			$results = $this->_cache->fetch($this);
+		}
+		if (!isset($results)) {
+			$results = $this->_decorateResults(
+				new ResultSet($this, $this->execute())
+			);
+			if ($this->_cache) {
+				$this->_cache->store($this, $results);
+			}
+		}
+		$this->_results = $results;
+		return $this->_results;
+	}
+
+/**
+ * Enable result caching for this query.
+ *
+ * If a query has caching enabled, it will do the following when executed:
+ *
+ * - Check the cache for $key. If there are results no SQL will be executed.
+ *   Instead the cached results will be returned.
+ * - When the cached data is stale/missing the result set will be cached as the query
+ *   is executed.
+ *
+ * ## Usage
+ *
+ * {{{
+ * // Simple string key + config
+ * $query->cache('my_key', 'db_results');
+ *
+ * // Function to generate key.
+ * $query->cache(function($q) {
+ *   $key = serialize($q->clause('select'));
+ *   $key .= serialize($q->clause('where'));
+ *   return md5($key);
+ * });
+ *
+ * // Using a pre-built cache engine.
+ * $query->cache('my_key', $engine);
+ *
+ *
+ * // Disable caching
+ * $query->cache(false);
+ * }}}
+ *
+ * @param false|string|Closure $key Either the cache key or a function to generate the cache key.
+ *   When using a function, this query instance will be supplied as an argument.
+ * @param string|CacheEngine $config Either the name of the cache config to use, or
+ *   a cache config instance.
+ * @return Query The query instance.
+ * @throws \RuntimeException When you attempt to cache a non-select query.
+ */
+	public function cache($key, $config = 'default') {
+		if ($this->_type !== 'select' && $this->_type !== null) {
+			throw new \RuntimeException('You cannot cache the results of non-select queries.');
+		}
+		if ($key === false) {
+			$this->_cache = null;
+			return $this;
+		}
+		$this->_cache = new QueryCacher($key, $config);
+		return $this;
+	}
+
+/**
+ * Fetch the results for this query.
+ *
+ * Compiles the SQL representation of this query and executes it using the
+ * provided connection object. Returns a ResultSet iterator object.
+ *
+ * ResultSet is a travesable object that implements the methods found
+ * on Cake\Collection\Collection.
+ *
+ * @return Cake\ORM\ResultCollectionTrait
+ * @throws RuntimeException if this method is called on a non-select Query.
+ */
+	public function all() {
+		if ($this->_type !== 'select' && $this->_type !== null) {
+			throw new \RuntimeException(
+				'You cannot call all() on a non-select query. Use execute() instead.'
+			);
+		}
+		return $this->getResults();
+	}
+
+/**
+ * Returns an array representation of the results after executing the query.
+ *
+ * @return array
+ */
+	public function toArray() {
+		return $this->all()->toArray();
+	}
+
+/**
+ * Register a new MapReduce routine to be executed on top of the database results
+ * Both the mapper and caller callable should be invokable objects.
+ *
+ * The MapReduce routing will only be run when the query is executed and the first
+ * result is attempted to be fetched.
+ *
+ * If the first argument is set to null, it will return the list of previously
+ * registered map reduce routines.
+ *
+ * If the third argument is set to true, it will erase previous map reducers
+ * and replace it with the arguments passed.
+ *
+ * @param callable $mapper
+ * @param callable $reducer
+ * @param boolean $overwrite
+ * @return Cake\ORM\Query|array
+ * @see Cake\Collection\Iterator\MapReduce for details on how to use emit data to the map reducer.
+ */
+	public function mapReduce(callable $mapper = null, callable $reducer = null, $overwrite = false) {
+		if ($overwrite) {
+			$this->_mapReduce = [];
+		}
+		if ($mapper === null) {
+			return $this->_mapReduce;
+		}
+		$this->_mapReduce[] = compact('mapper', 'reducer');
+		return $this;
+	}
+
+/**
+ * Registers a new formatter callback function that is to be executed when trying 
+ * to fetch the results from the database.
+ *
+ * Formatting callbacks will get a first parameter, a `ResultSetDecorator`, that
+ * can be traversed and modified at will. As for the second parameter, the
+ * formatting callback will receive this query instance.
+ *
+ * Callbacks are required to return an iterator object, which will be used as
+ * the return value for this query's result. Formatter functions are applied
+ * after all the `MapReduce` routines for this query have been executed.
+ *
+ * If the first argument is set to null, it will return the list of previously
+ * registered map reduce routines.
+ *
+ * If the second argument is set to true, it will erase previous formatters
+ * and replace them with the passed first argument.
+ *
+ * ### Example:
+ *
+ * {{{
+ * //Return all results from the table indexed by id
+ * $query->select(['id', 'name'])->formatResults(function($results, $query) {
+ *	return $results->indexBy('id');
+ * });
+ *
+ * //Add a new column to the ResultSet
+ * $query->select(['name', 'birth_date'])->formatResults(function($results, $query) {
+ *	return $results->map(function($row) {
+ *		$row['age'] = $row['birth_date']->diff(new DateTime)->y;
+ *		return $row;
+ *	});
+ * });
+ * }}}
+ *
+ * @param callable $formatter
+ * @param boolean|integer $mode
+ * @return Cake\ORM\Query|array
+ */
+	public function formatResults(callable $formatter = null, $mode = self::APPEND) {
+		if ($mode === self::OVERWRITE) {
+			$this->_formatters = [];
+		}
+		if ($formatter === null) {
+			return $this->_formatters;
+		}
+
+		if ($mode === self::PREPEND) {
+			array_unshift($this->_formatters, $formatter);
+			return $this;
+		}
+
+		$this->_formatters[] = $formatter;
+		return $this;
+	}
+
+/**
+ * Returns the first result out of executing this query, if the query has not been
+ * executed before, it will set the limit clause to 1 for performance reasons.
+ *
+ * ### Example:
+ *
+ * `$singleUser = $query->select(['id', 'username'])->first();`
+ *
+ * @return mixed the first result from the ResultSet
+ */
+	public function first() {
+		if ($this->_dirty) {
+			$this->limit(1);
+		}
+		return $this->all()->first();
+	}
+
+/**
+ * Decorates the results iterator with MapReduce routines and formatters
+ *
+ * @param \Traversable $result Original results
+ * @return \Cake\Datasoruce\ResultSetDecorator
+ */
+	protected function _decorateResults($result) {
+		foreach ($this->_mapReduce as $functions) {
+			$result = new MapReduce($result, $functions['mapper'], $functions['reducer']);
+		}
+
+		if (!empty($this->_mapReduce)) {
+			$result = new ResultSetDecorator($result);
+		}
+
+		foreach ($this->_formatters as $formatter) {
+			$result = $formatter($result, $this);
+		}
+
+		if (!empty($this->_formatters) && !($result instanceof ResultSetDecorator)) {
+			$result = new ResultSetDecorator($result);
+		}
+
+		return $result;
+	}
+
+}

+ 1 - 3
src/ORM/ResultSetDecorator.php

@@ -1,7 +1,5 @@
 <?php
 /**
- * PHP Version 5.4
- *
  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  *
@@ -14,7 +12,7 @@
  * @since         CakePHP(tm) v 3.0.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-namespace Cake\ORM;
+namespace Cake\Datasource;
 
 use ArrayIterator;
 use Cake\Collection\Collection;

+ 1 - 0
src/ORM/Association.php

@@ -14,6 +14,7 @@
  */
 namespace Cake\ORM;
 
+use Cake\Datasource\ResultSetDecorator;
 use Cake\Event\Event;
 use Cake\ORM\Entity;
 use Cake\ORM\Query;

+ 4 - 336
src/ORM/Query.php

@@ -14,11 +14,9 @@
  */
 namespace Cake\ORM;
 
-use Cake\Collection\Iterator\MapReduce;
 use Cake\Database\Query as DatabaseQuery;
-use Cake\Event\Event;
+use Cake\Datasource\QueryTrait;
 use Cake\ORM\EagerLoader;
-use Cake\ORM\QueryCacher;
 use Cake\ORM\Table;
 
 /**
@@ -30,6 +28,8 @@ use Cake\ORM\Table;
  */
 class Query extends DatabaseQuery {
 
+	use QueryTrait;
+
 /**
  * Indicates that the operation should append to the list
  *
@@ -52,13 +52,6 @@ class Query extends DatabaseQuery {
 	const OVERWRITE = true;
 
 /**
- * Instance of a table object this query is bound to
- *
- * @var \Cake\ORM\Table
- */
-	protected $_repository;
-
-/**
  * Whether the user select any fields before being executed, this is used
  * to determined if any fields should be automatically be selected.
  *
@@ -67,16 +60,6 @@ class Query extends DatabaseQuery {
 	protected $_hasFields;
 
 /**
- * A ResultSet.
- *
- * When set, query execution will be bypassed.
- *
- * @var Cake\ORM\ResultSet
- * @see setResult()
- */
-	protected $_results;
-
-/**
  * Boolean for tracking whether or not buffered results
  * are enabled.
  *
@@ -85,22 +68,6 @@ class Query extends DatabaseQuery {
 	protected $_useBufferedResults = true;
 
 /**
- * List of map-reduce routines that should be applied over the query
- * result
- *
- * @var array
- */
-	protected $_mapReduce = [];
-
-/**
- * List of formatter classes or callbacks that will post-process the
- * results when fetched
- *
- * @var array
- */
-	protected $_formatters = [];
-
-/**
  * Holds any custom options passed using applyOptions that could not be processed
  * by any method in this class.
  *
@@ -116,13 +83,6 @@ class Query extends DatabaseQuery {
 	protected $_hydrate = true;
 
 /**
- * A query cacher instance if this query has caching enabled.
- *
- * @var Cake\ORM\QueryCacher
- */
-	protected $_cache;
-
-/**
  * A callable function that can be used to calculate the total amount of
  * records this query will match when not using `limit`
  *
@@ -154,24 +114,6 @@ class Query extends DatabaseQuery {
 	}
 
 /**
- * Returns the default table object that will be used by this query,
- * that is, the table that will appear in the from clause.
- *
- * When called with a Table argument, the default table object will be set
- * and this query object will be returned for chaining.
- *
- * @param \Cake\ORM\Table $table The default table object to use
- * @return \Cake\ORM\Table|Query
- */
-	public function repository(Table $table = null) {
-		if ($table === null) {
-			return $this->_repository;
-		}
-		$this->_repository = $table;
-		return $this;
-	}
-
-/**
  * Hints this object to associate the correct types when casting conditions
  * for the database. This is done by extracting the field types from the schema
  * associated to the passed table object. This prevents the user from repeating
@@ -399,151 +341,6 @@ class Query extends DatabaseQuery {
 	}
 
 /**
- * Set the result set for a query.
- *
- * Setting the resultset of a query will make execute() a no-op. Instead
- * of executing the SQL query and fetching results, the ResultSet provided to this
- * method will be returned.
- *
- * This method is most useful when combined with results stored in a persistent cache.
- *
- * @param Cake\ORM\ResultSet $results The results this query should return.
- * @return Query The query instance.
- */
-	public function setResult($results) {
-		$this->_results = $results;
-		return $this;
-	}
-
-/**
- * Enable result caching for this query.
- *
- * If a query has caching enabled, it will do the following when executed:
- *
- * - Check the cache for $key. If there are results no SQL will be executed.
- *   Instead the cached results will be returned.
- * - When the cached data is stale/missing the result set will be cached as the query
- *   is executed.
- *
- * ## Usage
- *
- * {{{
- * // Simple string key + config
- * $query->cache('my_key', 'db_results');
- *
- * // Function to generate key.
- * $query->cache(function($q) {
- *   $key = serialize($q->clause('select'));
- *   $key .= serialize($q->clause('where'));
- *   return md5($key);
- * });
- *
- * // Using a pre-built cache engine.
- * $query->cache('my_key', $engine);
- *
- *
- * // Disable caching
- * $query->cache(false);
- * }}}
- *
- * @param false|string|Closure $key Either the cache key or a function to generate the cache key.
- *   When using a function, this query instance will be supplied as an argument.
- * @param string|CacheEngine $config Either the name of the cache config to use, or
- *   a cache config instance.
- * @return Query The query instance.
- * @throws \RuntimeException When you attempt to cache a non-select query.
- */
-	public function cache($key, $config = 'default') {
-		if ($this->_type !== 'select' && $this->_type !== null) {
-			throw new \RuntimeException('You cannot cache the results of non-select queries.');
-		}
-		if ($key === false) {
-			$this->_cache = null;
-			return $this;
-		}
-		$this->_cache = new QueryCacher($key, $config);
-		return $this;
-	}
-
-/**
- * Executes this query and returns a results iterator. This function is required
- * for implementing the IteratorAggregate interface and allows the query to be
- * iterated without having to call execute() manually, thus making it look like
- * a result set instead of the query itself.
- *
- * @return Iterator
- */
-	public function getIterator() {
-		return $this->all();
-	}
-
-/**
- * Fetch the results for this query.
- *
- * Compiles the SQL representation of this query and executes it using the
- * provided connection object. Returns a ResultSet iterator object.
- *
- * ResultSet is a travesable object that implements the methods found
- * on Cake\Collection\Collection.
- *
- * @return Cake\ORM\ResultCollectionTrait
- * @throws RuntimeException if this method is called on a non-select Query.
- */
-	public function all() {
-		if ($this->_type !== 'select' && $this->_type !== null) {
-			throw new \RuntimeException(
-				'You cannot call all() on a non-select query. Use execute() instead.'
-			);
-		}
-		return $this->getResults();
-	}
-
-/**
- * Get the result set for this query.
- *
- * Will return either the results set through setResult(), or execute the underlying statement
- * and return the ResultSet object ready for streaming of results.
- *
- * @return Cake\ORM\ResultCollectionTrait
- */
-	public function getResults() {
-		if (isset($this->_results)) {
-			return $this->_results;
-		}
-
-		$table = $this->repository();
-		$event = new Event('Model.beforeFind', $table, [$this, $this->_options, true]);
-		$table->getEventManager()->dispatch($event);
-
-		if (isset($this->_results)) {
-			return $this->_results;
-		}
-
-		if ($this->_cache) {
-			$results = $this->_cache->fetch($this);
-		}
-		if (!isset($results)) {
-			$results = $this->_decorateResults(
-				new ResultSet($this, $this->execute())
-			);
-			if ($this->_cache) {
-				$this->_cache->store($this, $results);
-			}
-		}
-		$this->_results = $results;
-		return $this->_results;
-	}
-
-/**
- * Returns an array representation of the results after executing the query.
- *
- * @return array
- */
-	public function toArray() {
-		return $this->all()->toArray();
-	}
-
-/**
  * Returns a key => value array representing a single aliased field
  * that can be passed directly to the select() method.
  * The key will contain the alias and the value the actual field name.
@@ -681,109 +478,6 @@ class Query extends DatabaseQuery {
 	}
 
 /**
- * Register a new MapReduce routine to be executed on top of the database results
- * Both the mapper and caller callable should be invokable objects.
- *
- * The MapReduce routing will only be run when the query is executed and the first
- * result is attempted to be fetched.
- *
- * If the first argument is set to null, it will return the list of previously
- * registered map reduce routines.
- *
- * If the third argument is set to true, it will erase previous map reducers
- * and replace it with the arguments passed.
- *
- * @param callable $mapper
- * @param callable $reducer
- * @param boolean $overwrite
- * @return Cake\ORM\Query|array
- * @see Cake\Collection\Iterator\MapReduce for details on how to use emit data to the map reducer.
- */
-	public function mapReduce(callable $mapper = null, callable $reducer = null, $overwrite = false) {
-		if ($overwrite) {
-			$this->_mapReduce = [];
-		}
-		if ($mapper === null) {
-			return $this->_mapReduce;
-		}
-		$this->_mapReduce[] = compact('mapper', 'reducer');
-		return $this;
-	}
-
-/**
- * Registers a new formatter callback function that is to be executed when trying 
- * to fetch the results from the database.
- *
- * Formatting callbacks will get a first parameter, a `ResultSetDecorator`, that
- * can be traversed and modified at will. As for the second parameter, the
- * formatting callback will receive this query instance.
- *
- * Callbacks are required to return an iterator object, which will be used as
- * the return value for this query's result. Formatter functions are applied
- * after all the `MapReduce` routines for this query have been executed.
- *
- * If the first argument is set to null, it will return the list of previously
- * registered map reduce routines.
- *
- * If the second argument is set to true, it will erase previous formatters
- * and replace them with the passed first argument.
- *
- * ### Example:
- *
- * {{{
- * //Return all results from the table indexed by id
- * $query->select(['id', 'name'])->formatResults(function($results, $query) {
- *	return $results->indexBy('id');
- * });
- *
- * //Add a new column to the ResultSet
- * $query->select(['name', 'birth_date'])->formatResults(function($results, $query) {
- *	return $results->map(function($row) {
- *		$row['age'] = $row['birth_date']->diff(new DateTime)->y;
- *		return $row;
- *	});
- * });
- * }}}
- *
- * @param callable $formatter
- * @param boolean|integer $mode
- * @return Cake\ORM\Query|array
- */
-	public function formatResults(callable $formatter = null, $mode = self::APPEND) {
-		if ($mode === self::OVERWRITE) {
-			$this->_formatters = [];
-		}
-		if ($formatter === null) {
-			return $this->_formatters;
-		}
-
-		if ($mode === self::PREPEND) {
-			array_unshift($this->_formatters, $formatter);
-			return $this;
-		}
-
-		$this->_formatters[] = $formatter;
-		return $this;
-	}
-
-/**
- * Returns the first result out of executing this query, if the query has not been
- * executed before, it will set the limit clause to 1 for performance reasons.
- *
- * ### Example:
- *
- * `$singleUser = $query->select(['id', 'username'])->first();`
- *
- * @return mixed the first result from the ResultSet
- */
-	public function first() {
-		if ($this->_dirty) {
-			$this->limit(1);
-		}
-		return $this->all()->first();
-	}
-
-/**
  * Return the COUNT(*) for for the query.
  *
  * @return integer
@@ -860,32 +554,6 @@ class Query extends DatabaseQuery {
 	}
 
 /**
- * Decorates the ResultSet iterator with MapReduce routines
- *
- * @param Cake\ORM\ResultCollectionTrait $result Original results
- * @return Cake\ORM\ResultCollectionTrait
- */
-	protected function _decorateResults($result) {
-		foreach ($this->_mapReduce as $functions) {
-			$result = new MapReduce($result, $functions['mapper'], $functions['reducer']);
-		}
-
-		if (!empty($this->_mapReduce)) {
-			$result = new ResultSetDecorator($result);
-		}
-
-		foreach ($this->_formatters as $formatter) {
-			$result = $formatter($result, $this);
-		}
-
-		if (!empty($this->_formatters) && !($result instanceof ResultSetDecorator)) {
-			$result = new ResultSetDecorator($result);
-		}
-
-		return $result;
-	}
-
-/**
  * Auxiliary function used to wrap the original statement from the driver with
  * any registered callbacks. This will also setup the correct statement class
  * in order to eager load deep associations.
@@ -1031,7 +699,7 @@ class Query extends DatabaseQuery {
  */
 	public function __call($method, $arguments) {
 		if ($this->type() === 'select') {
-			$resultSetClass = __NAMESPACE__ . '\ResultSetDecorator';
+			$resultSetClass = 'Cake\Datasource\ResultSetDecorator';
 			if (in_array($method, get_class_methods($resultSetClass))) {
 				$results = $this->all();
 				return call_user_func_array([$results, $method], $arguments);

+ 7 - 9
tests/TestCase/ORM/QueryCacherTest.php

@@ -1,7 +1,5 @@
 <?php
 /**
- * PHP Version 5.4
- *
  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  *
@@ -14,10 +12,10 @@
  * @since         CakePHP(tm) v 3.0.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-namespace Cake\Test\TestCase\ORM;
+namespace Cake\Test\TestCase\Datasource;
 
 use Cake\Cache\Cache;
-use Cake\ORM\QueryCacher;
+use Cake\Datasource\QueryCacher;
 use Cake\TestSuite\TestCase;
 
 /**
@@ -57,7 +55,7 @@ class QueryCacherTest extends TestCase {
  */
 	public function testFetchFunctionKey() {
 		$this->_mockRead('my_key', 'A winner');
-		$query = $this->getMock('Cake\ORM\Query', [], [], '', false);
+		$query = $this->getMock('stdClass');
 
 		$cacher = new QueryCacher(function($q) use ($query) {
 			$this->assertSame($query, $q);
@@ -77,7 +75,7 @@ class QueryCacherTest extends TestCase {
  */
 	public function testFetchFunctionKeyNoString() {
 		$this->_mockRead('my_key', 'A winner');
-		$query = $this->getMock('Cake\ORM\Query', [], [], '', false);
+		$query = $this->getMock('stdClass');
 
 		$cacher = new QueryCacher(function($q) {
 			return false;
@@ -94,7 +92,7 @@ class QueryCacherTest extends TestCase {
 	public function testFetchCacheHitStringEngine() {
 		$this->_mockRead('my_key', 'A winner');
 		$cacher = new QueryCacher('my_key', 'queryCache');
-		$query = $this->getMock('Cake\ORM\Query', [], [], '', false);
+		$query = $this->getMock('stdClass');
 		$result = $cacher->fetch($query);
 		$this->assertEquals('A winner', $result);
 	}
@@ -107,7 +105,7 @@ class QueryCacherTest extends TestCase {
 	public function testFetchCacheHit() {
 		$this->_mockRead('my_key', 'A winner');
 		$cacher = new QueryCacher('my_key', $this->engine);
-		$query = $this->getMock('Cake\ORM\Query', [], [], '', false);
+		$query = $this->getMock('stdClass');
 		$result = $cacher->fetch($query);
 		$this->assertEquals('A winner', $result);
 	}
@@ -120,7 +118,7 @@ class QueryCacherTest extends TestCase {
 	public function testFetchCacheMiss() {
 		$this->_mockRead('my_key', false);
 		$cacher = new QueryCacher('my_key', $this->engine);
-		$query = $this->getMock('Cake\ORM\Query', [], [], '', false);
+		$query = $this->getMock('stdClass');
 		$result = $cacher->fetch($query);
 		$this->assertNull($result, 'Cache miss should not have an isset() return.');
 	}

+ 2 - 4
tests/TestCase/ORM/ResultSetDecoratorTest.php

@@ -1,7 +1,5 @@
 <?php
 /**
- * PHP Version 5.4
- *
  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  *
@@ -14,9 +12,9 @@
  * @since         CakePHP(tm) v 3.0.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-namespace Cake\Test\TestCase\ORM;
+namespace Cake\Test\TestCase\Datasource;
 
-use Cake\ORM\ResultSetDecorator;
+use Cake\Datasource\ResultSetDecorator;
 use Cake\TestSuite\TestCase;
 
 /**

+ 1 - 1
tests/TestCase/ORM/CompositeKeysTest.php

@@ -413,7 +413,7 @@ class CompositeKeyTest extends TestCase {
 			[null, $table]
 		);
 
-		$items = new \Cake\ORM\ResultSetDecorator([
+		$items = new \Cake\Datasource\ResultSetDecorator([
 			['id' => 1, 'name' => 'a', 'site_id' => 1, 'parent_id' => null],
 			['id' => 2, 'name' => 'a', 'site_id' => 2, 'parent_id' => null],
 			['id' => 3, 'name' => 'a', 'site_id' => 1, 'parent_id' => 1],