Browse Source

Merge pull request #11886 from xaphalanx/fetch-methods-11795

Fetch methods 11795
Mark Story 8 years ago
parent
commit
da5929bdd6

+ 12 - 4
src/Database/Statement/BufferedStatement.php

@@ -82,11 +82,11 @@ class BufferedStatement extends StatementDecorator
      * @param string $type The type to fetch.
      * @return array|false
      */
-    public function fetch($type = 'num')
+    public function fetch($type = parent::FETCH_TYPE_NUM)
     {
         if ($this->_allFetched) {
             $row = ($this->_counter < $this->_count) ? $this->_records[$this->_counter++] : false;
-            $row = ($row && $type === 'num') ? array_values($row) : $row;
+            $row = ($row && $type === static::FETCH_TYPE_NUM) ? array_values($row) : $row;
 
             return $row;
         }
@@ -107,12 +107,20 @@ class BufferedStatement extends StatementDecorator
     }
 
     /**
+     * {@inheritdoc}
+     */
+    public function fetchAssoc()
+    {
+        return $this->fetch(static::FETCH_TYPE_ASSOC);
+    }
+
+    /**
      * {@inheritDoc}
      *
      * @param string $type The type to fetch.
      * @return array
      */
-    public function fetchAll($type = 'num')
+    public function fetchAll($type = parent::FETCH_TYPE_NUM)
     {
         if ($this->_allFetched) {
             return $this->_records;
@@ -133,7 +141,7 @@ class BufferedStatement extends StatementDecorator
     {
         if (!$this->_allFetched) {
             $counter = $this->_counter;
-            while ($this->fetch('assoc')) {
+            while ($this->fetch(static::FETCH_TYPE_ASSOC)) {
             }
             $this->_counter = $counter;
         }

+ 2 - 2
src/Database/Statement/CallbackStatement.php

@@ -51,7 +51,7 @@ class CallbackStatement extends StatementDecorator
      * @param string $type Either 'num' or 'assoc' to indicate the result format you would like.
      * @return array|false
      */
-    public function fetch($type = 'num')
+    public function fetch($type = parent::FETCH_TYPE_NUM)
     {
         $callback = $this->_callback;
         $row = $this->_statement->fetch($type);
@@ -67,7 +67,7 @@ class CallbackStatement extends StatementDecorator
      * @param string $type Either 'num' or 'assoc' to indicate the result format you would like.
      * @return array
      */
-    public function fetchAll($type = 'num')
+    public function fetchAll($type = parent::FETCH_TYPE_NUM)
     {
         return array_map($this->_callback, $this->_statement->fetchAll($type));
     }

+ 9 - 9
src/Database/Statement/PDOStatement.php

@@ -88,15 +88,15 @@ class PDOStatement extends StatementDecorator
      * @return array|false Result array containing columns and values or false if no results
      * are left
      */
-    public function fetch($type = 'num')
+    public function fetch($type = parent::FETCH_TYPE_NUM)
     {
-        if ($type === 'num') {
+        if ($type === static::FETCH_TYPE_NUM) {
             return $this->_statement->fetch(PDO::FETCH_NUM);
         }
-        if ($type === 'assoc') {
+        if ($type === static::FETCH_TYPE_ASSOC) {
             return $this->_statement->fetch(PDO::FETCH_ASSOC);
         }
-        if ($type === 'obj') {
+        if ($type === static::FETCH_TYPE_OBJ) {
             return $this->_statement->fetch(PDO::FETCH_OBJ);
         }
 
@@ -117,16 +117,16 @@ class PDOStatement extends StatementDecorator
      * @param string $type num for fetching columns as positional keys or assoc for column names as keys
      * @return array list of all results from database for this statement
      */
-    public function fetchAll($type = 'num')
+    public function fetchAll($type = parent::FETCH_TYPE_NUM)
     {
-        if ($type === 'num') {
+        if ($type === static::FETCH_TYPE_NUM) {
             return $this->_statement->fetchAll(PDO::FETCH_NUM);
         }
-        if ($type === 'assoc') {
+        if ($type === static::FETCH_TYPE_ASSOC) {
             return $this->_statement->fetchAll(PDO::FETCH_ASSOC);
         }
-        if ($type === 'obj') {
-            return $this->_statement->fetchAll(PDO::FETCH_OBJ);
+        if ($type === static::FETCH_TYPE_OBJ) {
+            return $this->_statement->fetch(PDO::FETCH_OBJ);
         }
 
         return $this->_statement->fetchAll($type);

+ 49 - 3
src/Database/Statement/StatementDecorator.php

@@ -36,6 +36,25 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
     use TypeConverterTrait;
 
     /**
+     * Used to designate that numeric indexes be returned in a result when calling fetch methods
+     *
+     * @var string
+     */
+    const FETCH_TYPE_NUM = 'num';
+    /**
+     * Used to designate that an associated array be returned in a result when calling fetch methods
+     *
+     * @var string
+     */
+    const FETCH_TYPE_ASSOC = 'assoc';
+    /**
+     * Used to designate that a stdClass object be returned in a result when calling fetch methods
+     *
+     * @var string
+     */
+    const FETCH_TYPE_OBJ = 'obj';
+
+    /**
      * Statement instance implementation, such as PDOStatement
      * or any other custom implementation.
      *
@@ -191,12 +210,39 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @return array|false Result array containing columns and values or false if no results
      * are left
      */
-    public function fetch($type = 'num')
+    public function fetch($type = self::FETCH_TYPE_NUM)
     {
         return $this->_statement->fetch($type);
     }
 
     /**
+     * Returns the next row in a result set as an associative array. Calling this function is the same as calling
+     * $statement->fetch(StatementDecorator::FETCH_TYPE_ASSOC). If no results are found false is returned.
+     *
+     * @return array|false Result array containing columns and values an an associative array or false if no results
+     */
+    public function fetchAssoc()
+    {
+        return $this->fetch(static::FETCH_TYPE_ASSOC);
+    }
+
+    /**
+     * Returns the value of the result at position.
+     *
+     * @param int $position The numeric position of the column to retrieve in the result
+     * @return mixed|false Returns the specific value of the column designated at $position
+     */
+    public function fetchColumn($position)
+    {
+        $result = $this->fetch(static::FETCH_TYPE_NUM);
+        if (isset($result[$position])) {
+            return $result[$position];
+        };
+
+        return false;
+    }
+
+    /**
      * Returns an array with all rows resulting from executing this statement.
      *
      * ### Example:
@@ -210,7 +256,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @param string $type num for fetching columns as positional keys or assoc for column names as keys
      * @return array List of all results from database for this statement
      */
-    public function fetchAll($type = 'num')
+    public function fetchAll($type = self::FETCH_TYPE_NUM)
     {
         return $this->_statement->fetchAll($type);
     }
@@ -306,7 +352,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
     {
         $row = null;
         if ($column && $this->columnCount()) {
-            $row = $this->fetch('assoc');
+            $row = $this->fetch(static::FETCH_TYPE_ASSOC);
         }
         if (isset($row[$column])) {
             return $row[$column];

+ 120 - 28
tests/TestCase/Database/QueryTest.php

@@ -18,6 +18,7 @@ use Cake\Database\ExpressionInterface;
 use Cake\Database\Expression\IdentifierExpression;
 use Cake\Database\Query;
 use Cake\Database\StatementInterface;
+use Cake\Database\Statement\StatementDecorator;
 use Cake\Database\TypeMap;
 use Cake\Datasource\ConnectionManager;
 use Cake\TestSuite\TestCase;
@@ -4611,34 +4612,6 @@ class QueryTest extends TestCase
     }
 
     /**
-     * Tests that fetch returns an anonymous object when the string 'obj'
-     * is passed as an argument
-     *
-     * @return void
-     */
-    public function testSelectWithObjFetchType()
-    {
-        $this->loadFixtures('Comments');
-        $query = new Query($this->connection);
-        $result = $query
-            ->select(['id'])
-            ->from('comments')
-            ->where(['id' => '1'])
-            ->execute();
-        $obj = (object)['id' => 1];
-        $this->assertEquals($obj, $result->fetch('obj'));
-
-        $query = new Query($this->connection);
-        $result = $query
-            ->select(['id'])
-            ->from('comments')
-            ->where(['id' => '1'])
-            ->execute();
-        $rows = $result->fetchAll('obj');
-        $this->assertEquals($obj, $rows[0]);
-    }
-
-    /**
      * Test getValueBinder()
      *
      * @return void
@@ -4777,4 +4750,123 @@ class QueryTest extends TestCase
         $pattern = str_replace('>', '[`"\]]' . $optional, $pattern);
         $this->assertRegExp('#' . $pattern . '#', $query);
     }
+
+    /**
+     * Test that calling fetchAssoc return an associated array.
+     * @return void
+     * @throws \Exception
+     */
+    public function testFetchAssoc()
+    {
+        $this->loadFixtures('Profiles');
+        $query = new Query($this->connection);
+        $fields = [
+            'id' => 'integer',
+            'user_id' => 'integer',
+            'is_active' => 'boolean'
+        ];
+        $typeMap = new TypeMap($fields);
+        $results = $query
+            ->select([
+                'id',
+                'user_id',
+                'is_active'
+            ])
+            ->from('profiles')
+            ->setSelectTypeMap($typeMap)
+            ->limit(1)
+            ->execute()
+            ->fetchAssoc();
+        $this->assertSame(['id' => 1, 'user_id' => 1, 'is_active' => false], $results);
+    }
+
+    /**
+     * Test that calling fetch with with FETCH_TYPE_OBJ return stdClass object.
+     * @return void
+     * @throws \Exception
+     */
+    public function testFetchObjects()
+    {
+        $this->loadFixtures('Profiles');
+        $query = new Query($this->connection);
+        $results = $query
+            ->select([
+                'id',
+                'user_id',
+                'is_active'
+            ])
+            ->from('profiles')
+            ->limit(1)
+            ->execute()
+            ->fetch(StatementDecorator::FETCH_TYPE_OBJ);
+        $this->assertInstanceOf(\stdClass::class, $results);
+    }
+
+    /**
+     * Test that fetchColumn() will return the correct value at $position.
+     * @throws \Exception
+     * @return void
+     */
+    public function testFetchColumn()
+    {
+        $this->loadFixtures('Profiles');
+        $query = new Query($this->connection);
+        $fields = [
+            'integer',
+            'integer',
+            'boolean'
+        ];
+        $typeMap = new TypeMap($fields);
+        $query
+            ->select([
+                'id',
+                'user_id',
+                'is_active'
+            ])
+            ->from('profiles')
+            ->setSelectTypeMap($typeMap)
+            ->where(['id' => 2])
+            ->limit(1);
+        $statement = $query->execute();
+        $results = $statement->fetchColumn(0);
+        $this->assertSame(2, $results);
+
+        $statement = $query->execute();
+        $results = $statement->fetchColumn(1);
+        $this->assertSame(2, $results);
+
+        $statement = $query->execute();
+        $results = $statement->fetchColumn(2);
+        $this->assertSame(false, $results);
+    }
+
+    /**
+     * Test that fetchColumn() will return false if $position is not set.
+     * @throws \Exception
+     * @return void
+     */
+    public function testFetchColumnReturnsFalse()
+    {
+        $this->loadFixtures('Profiles');
+        $query = new Query($this->connection);
+        $fields = [
+            'integer',
+            'integer',
+            'boolean'
+        ];
+        $typeMap = new TypeMap($fields);
+        $query
+            ->select([
+                'id',
+                'user_id',
+                'is_active'
+            ])
+            ->from('profiles')
+            ->setSelectTypeMap($typeMap)
+            ->where(['id' => 2])
+            ->limit(1);
+        $statement = $query->execute();
+        $results = $statement->fetchColumn(3);
+        $this->assertFalse($results);
+    }
 }