Skip to content

Commit

Permalink
Extract BufferedResultSet.
Browse files Browse the repository at this point in the history
During discussion on github, it became apparent that buffering results
by default is not a good idea. Split the buffering out into a subclass
that is optionally enabled using Query::bufferResults().

Do some refactoring in ResultSet to make fewer method calls and less
code overall.
  • Loading branch information
markstory committed Aug 8, 2013
1 parent 6b441f5 commit 8dd03e9
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 27 deletions.
50 changes: 50 additions & 0 deletions lib/Cake/ORM/BufferedResultSet.php
@@ -0,0 +1,50 @@
<?php
/**
* PHP Version 5.4
*
* 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\ORM;

/**
* Buffered ResultSets differ from un-buffered ResultSets in a few ways.
*
* - They can be iterated multiple times.
* - They can be both cached and iterated using the same object.
*/
class BufferedResultSet extends ResultSet {

/**
* Rewind the ResultSet
*
* @return void
*/
public function rewind() {
$this->_index = 0;
$this->_lastIndex = -1;
}

/**
* Fetch a result and buffer the fetched row.
*
* @return mixed
*/
public function valid() {
$result = parent::valid();
if (!isset($this->_results[$this->_index])) {
$this->_results[] = $this->_current;
}
return $result;
}

}
52 changes: 26 additions & 26 deletions lib/Cake/ORM/ResultSet.php
Expand Up @@ -17,6 +17,7 @@
namespace Cake\ORM;

use Cake\Database\Type;
use Cake\Database\Exception;
use \Iterator;
use \JsonSerializable;
use \Serializable;
Expand Down Expand Up @@ -142,20 +143,26 @@ public function key() {
*/
public function next() {
$this->_index++;
$this->_fetchResult();
$this->_lastIndex = $this->_index;
}

/**
* Rewind a ResultSet.
*
* Once rewound results will not be refetched from
* the database.
* Not implemented, Use a BufferedResultSet for a rewindable
* ResultSet.
*
* @return void
* @throws Cake\Database\Exception
*/
public function rewind() {
$this->_index = 0;
$this->_lastIndex = -1;
if ($this->_index == 0) {
return;
}
$msg = __d(
'cake_dev',
'You cannot rewind an un-buffered ResultSet. Use Query::bufferResults() to get a buffered ResultSet.'
);
throw new Exception($msg);
}

/**
Expand All @@ -164,7 +171,7 @@ public function rewind() {
* @return boolean
*/
public function valid() {
$this->_fetchResult();
$this->_current = $this->_fetchResult();
return $this->_current !== false;
}

Expand Down Expand Up @@ -195,28 +202,23 @@ protected function _calculateAssociationMap() {
}

/**
* Helper function to fetch the next result from the statement and update all
* internal counters.
* Helper function to fetch the next result from the statement or
* seeded results.
*
* @return void
* @return mixed
*/
protected function _fetchResult() {
if (isset($this->_results[$this->_index])) {
$this->_current = $this->_results[$this->_index];
$this->_lastIndex = $this->_index;
return;
if (!empty($this->_results) && isset($this->_results[$this->_index])) {
return $this->_results[$this->_index];
}
if (!$this->_statement) {
$this->_current = false;
return;
if (!empty($this->_results)) {
return false;
}
$row = $this->_statement->fetch('assoc');
if ($row !== false) {
$row = $this->_groupResult($row);
$this->_results[] = $row;
if ($row === false) {
return $row;
}
$this->_current = $row;
$this->_lastIndex = $this->_index;
return $this->_groupResult($row);
}

/**
Expand Down Expand Up @@ -297,8 +299,7 @@ protected function _castValues($table, $values) {
* @return string Serialized object
*/
public function serialize() {
$this->toArray();
return serialize($this->_results);
return serialize($this->toArray());
}

/**
Expand All @@ -321,8 +322,7 @@ public function unserialize($serialized) {
* @return array The data to convert to JSON
*/
public function jsonSerialize() {
$this->toArray();
return $this->_results;
return $this->toArray();
}

}
57 changes: 57 additions & 0 deletions lib/Cake/Test/TestCase/ORM/BufferedResultSetTest.php
@@ -0,0 +1,57 @@
<?php
/**
* PHP Version 5.4
*
* 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\Test\TestCase\ORM;

use Cake\Core\Configure;
use Cake\Model\ConnectionManager;
use Cake\ORM\Query;
use Cake\ORM\BufferedResultSet;
use Cake\ORM\Table;
use Cake\TestSuite\TestCase;

/**
* BufferedResultSet test case.
*/
class BufferedResultSetTest extends TestCase {

public $fixtures = ['core.article'];

public function setUp() {
parent::setUp();
$this->connection = ConnectionManager::getDataSource('test');
$this->table = new Table(['table' => 'articles', 'connection' => $this->connection]);
}

/**
* Test that result sets can be rewound and re-used.
*
* @return void
*/
public function testRewind() {
$query = $this->table->find('all');
$results = $query->bufferResults()->execute();
$first = $second = [];
foreach ($results as $result) {
$first[] = $result;
}
foreach ($results as $result) {
$second[] = $result;
}
$this->assertEquals($first, $second);
}

}
2 changes: 1 addition & 1 deletion lib/Cake/Test/TestCase/ORM/ResultSetTest.php
Expand Up @@ -45,6 +45,7 @@ public function setUp() {
/**
* Test that result sets can be rewound and re-used.
*
* @expectedException Cake\Database\Exception
* @return void
*/
public function testRewind() {
Expand All @@ -57,7 +58,6 @@ public function testRewind() {
foreach ($results as $result) {
$second[] = $result;
}
$this->assertEquals($first, $second);
}

/**
Expand Down

0 comments on commit 8dd03e9

Please sign in to comment.