Commit f4acc79a authored by Damien Tournoud's avatar Damien Tournoud

Make all Statement classes Traversable.

This enables a more natural way of interacting with result sets,
and re-introduces in Doctrine DBAL a feature that has been available
in PDO from the begining.
parent ce227fa8
...@@ -22,11 +22,12 @@ namespace Doctrine\DBAL\Cache; ...@@ -22,11 +22,12 @@ namespace Doctrine\DBAL\Cache;
use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ResultStatement;
use PDO; use PDO;
class ArrayStatement implements ResultStatement class ArrayStatement implements \IteratorAggregate, ResultStatement
{ {
private $data; private $data;
private $columnCount = 0; private $columnCount = 0;
private $num = 0; private $num = 0;
private $defaultFetchStyle = PDO::FETCH_BOTH;
public function __construct(array $data) public function __construct(array $data)
{ {
...@@ -46,6 +47,17 @@ class ArrayStatement implements ResultStatement ...@@ -46,6 +47,17 @@ class ArrayStatement implements ResultStatement
return $this->columnCount; return $this->columnCount;
} }
public function setFetchMode($fetchStyle)
{
$this->defaultFetchStyle = $fetchStyle;
}
public function getIterator()
{
$data = $this->fetchAll($this->defaultFetchStyle);
return new \ArrayIterator($data);
}
public function fetch($fetchStyle = PDO::FETCH_BOTH) public function fetch($fetchStyle = PDO::FETCH_BOTH)
{ {
if (isset($this->data[$this->num])) { if (isset($this->data[$this->num])) {
......
...@@ -38,7 +38,7 @@ use PDO; ...@@ -38,7 +38,7 @@ use PDO;
* Also you have to realize that the cache will load the whole result into memory at once to ensure 2. * Also you have to realize that the cache will load the whole result into memory at once to ensure 2.
* This means that the memory usage for cached results might increase by using this feature. * This means that the memory usage for cached results might increase by using this feature.
*/ */
class ResultCacheStatement implements ResultStatement class ResultCacheStatement implements \IteratorAggregate, ResultStatement
{ {
/** /**
* @var \Doctrine\Common\Cache\Cache * @var \Doctrine\Common\Cache\Cache
...@@ -78,6 +78,11 @@ class ResultCacheStatement implements ResultStatement ...@@ -78,6 +78,11 @@ class ResultCacheStatement implements ResultStatement
*/ */
private $data; private $data;
/**
* @var int
*/
private $defaultFetchStyle = PDO::FETCH_BOTH;
/** /**
* @param Statement $stmt * @param Statement $stmt
* @param Cache $resultCache * @param Cache $resultCache
...@@ -127,6 +132,17 @@ class ResultCacheStatement implements ResultStatement ...@@ -127,6 +132,17 @@ class ResultCacheStatement implements ResultStatement
return $this->statement->columnCount(); return $this->statement->columnCount();
} }
public function setFetchMode($fetchStyle)
{
$this->defaultFetchStyle = $fetchStyle;
}
public function getIterator()
{
$data = $this->fetchAll($this->defaultFetchStyle);
return new \ArrayIterator($data);
}
/** /**
* fetch * fetch
* *
......
...@@ -27,7 +27,7 @@ use \PDO; ...@@ -27,7 +27,7 @@ use \PDO;
* @since 2.0 * @since 2.0
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
*/ */
class OCI8Statement implements \Doctrine\DBAL\Driver\Statement class OCI8Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement
{ {
/** Statement handle. */ /** Statement handle. */
protected $_dbh; protected $_dbh;
...@@ -40,6 +40,7 @@ class OCI8Statement implements \Doctrine\DBAL\Driver\Statement ...@@ -40,6 +40,7 @@ class OCI8Statement implements \Doctrine\DBAL\Driver\Statement
PDO::FETCH_NUM => OCI_NUM, PDO::FETCH_NUM => OCI_NUM,
PDO::PARAM_LOB => OCI_B_BLOB, PDO::PARAM_LOB => OCI_B_BLOB,
); );
protected $_defaultFetchStyle = PDO::FETCH_BOTH;
protected $_paramMap = array(); protected $_paramMap = array();
/** /**
...@@ -183,6 +184,23 @@ class OCI8Statement implements \Doctrine\DBAL\Driver\Statement ...@@ -183,6 +184,23 @@ class OCI8Statement implements \Doctrine\DBAL\Driver\Statement
return $ret; return $ret;
} }
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchStyle = PDO::FETCH_BOTH)
{
$this->_defaultFetchStyle = $fetchStyle;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$data = $this->fetchAll($this->_defaultFetchStyle);
return new \ArrayIterator($data);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -30,4 +30,13 @@ namespace Doctrine\DBAL\Driver; ...@@ -30,4 +30,13 @@ namespace Doctrine\DBAL\Driver;
class PDOStatement extends \PDOStatement implements Statement class PDOStatement extends \PDOStatement implements Statement
{ {
private function __construct() {} private function __construct() {}
public function setFetchMode($fetchStyle, $params = NULL)
{
// This thin wrapper is necessary to shield against the weird signature
// of PDOStatement::setFetchMode(): even if the second and third
// parameters are optional, PHP will not let us remove it from this
// declaration.
return parent::setFetchMode($fetchStyle);
}
} }
\ No newline at end of file
...@@ -26,7 +26,7 @@ use PDO; ...@@ -26,7 +26,7 @@ use PDO;
* *
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
*/ */
interface ResultStatement interface ResultStatement extends \Traversable
{ {
/** /**
* Closes the cursor, enabling the statement to be executed again. * Closes the cursor, enabling the statement to be executed again.
...@@ -46,6 +46,14 @@ interface ResultStatement ...@@ -46,6 +46,14 @@ interface ResultStatement
*/ */
function columnCount(); function columnCount();
/**
* setFetchMode
* Set the fetch mode to use while iterating this statement.
*
* @param integer $fetchStyle
*/
public function setFetchMode($fetchStyle);
/** /**
* fetch * fetch
* *
......
...@@ -30,7 +30,7 @@ use PDO; ...@@ -30,7 +30,7 @@ use PDO;
* @since 2.0 * @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
*/ */
class Statement implements \Doctrine\DBAL\Driver\Statement class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement
{ {
/** /**
...@@ -48,6 +48,11 @@ class Statement implements \Doctrine\DBAL\Driver\Statement ...@@ -48,6 +48,11 @@ class Statement implements \Doctrine\DBAL\Driver\Statement
*/ */
private $case; private $case;
/**
* @var int
*/
private $defaultFetchStyle = PDO::FETCH_BOTH;
/** /**
* Wraps <tt>Statement</tt> and applies portability measures * Wraps <tt>Statement</tt> and applies portability measures
* *
...@@ -96,6 +101,17 @@ class Statement implements \Doctrine\DBAL\Driver\Statement ...@@ -96,6 +101,17 @@ class Statement implements \Doctrine\DBAL\Driver\Statement
return $this->stmt->execute($params); return $this->stmt->execute($params);
} }
public function setFetchMode($fetchStyle)
{
$this->defaultFetchStyle = $fetchStyle;
}
public function getIterator()
{
$data = $this->fetchAll($this->defaultFetchStyle);
return new \ArrayIterator($data);
}
public function fetch($fetchStyle = PDO::FETCH_BOTH) public function fetch($fetchStyle = PDO::FETCH_BOTH)
{ {
$row = $this->stmt->fetch($fetchStyle); $row = $this->stmt->fetch($fetchStyle);
......
...@@ -32,7 +32,7 @@ use PDO, ...@@ -32,7 +32,7 @@ use PDO,
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @since 2.0
*/ */
class Statement implements DriverStatement class Statement implements \IteratorAggregate, DriverStatement
{ {
/** /**
* @var string The SQL statement. * @var string The SQL statement.
...@@ -177,6 +177,16 @@ class Statement implements DriverStatement ...@@ -177,6 +177,16 @@ class Statement implements DriverStatement
return $this->_stmt->errorInfo(); return $this->_stmt->errorInfo();
} }
public function setFetchMode($fetchStyle)
{
return $this->_stmt->setFetchMode($fetchStyle);
}
public function getIterator()
{
return $this->_stmt;
}
/** /**
* Fetches the next row from a result set. * Fetches the next row from a result set.
* *
......
...@@ -99,6 +99,28 @@ class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -99,6 +99,28 @@ class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->assertEquals(1, $column); $this->assertEquals(1, $column);
} }
public function testPrepareWithIterator()
{
$paramInt = 1;
$paramStr = 'foo';
$sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?";
$stmt = $this->_conn->prepare($sql);
$this->assertInstanceOf('Doctrine\DBAL\Statement', $stmt);
$stmt->bindParam(1, $paramInt);
$stmt->bindParam(2, $paramStr);
$stmt->execute();
$rows = array();
$stmt->setFetchMode(\PDO::FETCH_ASSOC);
foreach ($stmt as $row) {
$rows[] = array_change_key_case($row, \CASE_LOWER);
}
$this->assertEquals(array('test_int' => 1, 'test_string' => 'foo'), $rows[0]);
}
public function testPrepareWithQuoted() public function testPrepareWithQuoted()
{ {
$table = 'fetch_table'; $table = 'fetch_table';
......
...@@ -60,6 +60,11 @@ class PortabilityTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -60,6 +60,11 @@ class PortabilityTest extends \Doctrine\Tests\DbalFunctionalTestCase
$rows = $this->getPortableConnection()->fetchAll('SELECT * FROM portability_table'); $rows = $this->getPortableConnection()->fetchAll('SELECT * FROM portability_table');
$this->assertFetchResultRows($rows); $this->assertFetchResultRows($rows);
$stmt = $this->getPortableConnection()->query('SELECT * FROM portability_table');
foreach ($stmt as $row) {
$this->assertFetchResultRow($row);
}
$stmt = $this->getPortableConnection()->query('SELECT * FROM portability_table'); $stmt = $this->getPortableConnection()->query('SELECT * FROM portability_table');
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$this->assertFetchResultRow($row); $this->assertFetchResultRow($row);
......
...@@ -85,6 +85,24 @@ class ResultCacheTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -85,6 +85,24 @@ class ResultCacheTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->assertEquals($numExpectedResult, $data); $this->assertEquals($numExpectedResult, $data);
} }
public function testIteratorFetch()
{
$this->assertStandardAndIteratorFetchAreEqual(\PDO::FETCH_BOTH);
$this->assertStandardAndIteratorFetchAreEqual(\PDO::FETCH_ASSOC);
$this->assertStandardAndIteratorFetchAreEqual(\PDO::FETCH_NUM);
}
public function assertStandardAndIteratorFetchAreEqual($fetchStyle)
{
$stmt = $this->_conn->executeQuery("SELECT * FROM caching", array(), array(), new QueryCacheProfile(10, "testcachekey"));
$data = $this->hydrateStmt($stmt, $fetchStyle);
$stmt = $this->_conn->executeQuery("SELECT * FROM caching", array(), array(), new QueryCacheProfile(10, "testcachekey"));
$data_iterator = $this->hydrateStmtIterator($stmt, $fetchStyle);
$this->assertEquals($data, $data_iterator);
}
public function testDontCloseNoCache() public function testDontCloseNoCache()
{ {
$stmt = $this->_conn->executeQuery("SELECT * FROM caching", array(), array(), new QueryCacheProfile(10, "testcachekey")); $stmt = $this->_conn->executeQuery("SELECT * FROM caching", array(), array(), new QueryCacheProfile(10, "testcachekey"));
...@@ -167,4 +185,15 @@ class ResultCacheTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -167,4 +185,15 @@ class ResultCacheTest extends \Doctrine\Tests\DbalFunctionalTestCase
$stmt->closeCursor(); $stmt->closeCursor();
return $data; return $data;
} }
private function hydrateStmtIterator($stmt, $fetchStyle = \PDO::FETCH_ASSOC)
{
$data = array();
$stmt->setFetchMode($fetchStyle);
foreach ($stmt as $row) {
$data[] = $row;
}
$stmt->closeCursor();
return $data;
}
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment