[FEATURE] Add Support for Doctrine DBAL Queries in Extbase 78/50178/8
authorBenni Mack <benni@typo3.org>
Mon, 10 Oct 2016 19:19:40 +0000 (21:19 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Tue, 13 Dec 2016 14:28:16 +0000 (15:28 +0100)
To fully utilize Extbase in conjunction with Doctrine DBAL,
Extbases' StorageBackend now supports native QueryBuilder
and Doctrine DBAL Statements, which can be used for prepared
statements.

Resolves: #78116
Releases: master
Change-Id: I73b5a9588465e345029b7985327fb0cd0d7875df
Reviewed-on: https://review.typo3.org/50178
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/core/Documentation/Changelog/master/Feature-78116-ExtbaseSupportForDoctrinesNativeDBALStatementAndQueryBuilder.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Generic/Qom/Statement.php
typo3/sysext/extbase/Classes/Persistence/Generic/Query.php
typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-78116-ExtbaseSupportForDoctrinesNativeDBALStatementAndQueryBuilder.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-78116-ExtbaseSupportForDoctrinesNativeDBALStatementAndQueryBuilder.rst
new file mode 100644 (file)
index 0000000..44c149d
--- /dev/null
@@ -0,0 +1,41 @@
+.. include:: ../../Includes.txt
+
+=======================================================================================
+Feature: #78116 - Extbase support for Doctrine's native DBAL Statement and QueryBuilder
+=======================================================================================
+
+See :issue:`78116`
+
+Description
+===========
+
+With the change to Doctrine DBAL Extbase's direct query functionality also supports "QueryBuilder" objects and instances of
+"\Doctrine\DBAL\Statement" as prepared statements instead of only "\TYPO3\CMS\Core\Database\PreparedStatement".
+
+The following example could happen inside any Extbase Repository using native Doctrine DBAL statements:
+
+.. code-block:: php
+
+   $connection = $this->objectManager->get(ConnectionPool::class)->getConnectionForTable('mytable');
+   $statement = $this->objectManager->get(
+      \Doctrine\DBAL\Statement::class
+      'SELECT * FROM mytable WHERE uid=? OR title=?',
+      $connection
+   );
+
+   $query = $this->createQuery();
+   $query->statement($statement, [$uid, $title]);
+
+
+The following example shows the usage with the QueryBuilder object:
+
+.. code-block:: php
+
+   $queryBuilder = $this->objectManager->get(ConnectionPool::class)->getQueryBuilderForTable('mytable');
+
+   ... do the SQL query with the query builder.
+
+   $query = $this->createQuery();
+   $query->statement($queryBuilder);
+
+.. index:: Database, PHP-API
index 211b450..4d28fdd 100644 (file)
@@ -20,7 +20,7 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Qom;
 class Statement implements ConstraintInterface
 {
     /**
-     * @var string|\TYPO3\CMS\Core\Database\PreparedStatement
+     * @var string|\TYPO3\CMS\Core\Database\PreparedStatement|\Doctrine\DBAL\Statement|\TYPO3\CMS\Core\Database\Query\QueryBuilder
      */
     protected $statement;
 
@@ -32,8 +32,8 @@ class Statement implements ConstraintInterface
     /**
      * Constructs the Statement instance
      *
-     * @param string|\TYPO3\CMS\Core\Database\PreparedStatement $statement The statement as sql string or TYPO3\CMS\Core\Database\PreparedStatement
-     * @param array $boundVariables An array of variables to bind to the statement, only to be used with preparedStatement
+     * @param string|\TYPO3\CMS\Core\Database\PreparedStatement|\Doctrine\DBAL\Statement|\TYPO3\CMS\Core\Database\Query\QueryBuilder $statement The statement as sql string or an instance of  TYPO3\CMS\Core\Database\PreparedStatement, \Doctrine\DBAL\Statement or \TYPO3\CMS\Core\Database\Query\QueryBuilder
+     * @param array $boundVariables An array of variables to bind to the statement, only to be used with prepared statements
      */
     public function __construct($statement, array $boundVariables = [])
     {
@@ -44,7 +44,7 @@ class Statement implements ConstraintInterface
     /**
      * Gets the statement.
      *
-     * @return string|\TYPO3\CMS\Core\Database\PreparedStatement the statement; non-null
+     * @return string|\TYPO3\CMS\Core\Database\PreparedStatement|\Doctrine\DBAL\Statement|\TYPO3\CMS\Core\Database\Query\QueryBuilder the statement; non-null
      */
     public function getStatement()
     {
index 75b33e9..c96a60f 100644 (file)
@@ -365,7 +365,7 @@ class Query implements QueryInterface
      * Sets the statement of this query. If you use this, you will lose the abstraction from a concrete storage
      * backend (database).
      *
-     * @param string|\TYPO3\CMS\Core\Database\PreparedStatement $statement The statement
+     * @param string|\TYPO3\CMS\Core\Database\PreparedStatement|\TYPO3\CMS\Core\Database\Query\QueryBuilder|\Doctrine\DBAL\Statement $statement The statement
      * @param array $parameters An array of parameters. These will be bound to placeholders '?' in the $statement.
      * @return QueryInterface
      */
index 5ec04c0..f1dc838 100644 (file)
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Storage;
 use Doctrine\DBAL\DBALException;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -361,7 +362,23 @@ class Typo3DbBackend implements BackendInterface, SingletonInterface
         $realStatement = $statement->getStatement();
         $parameters = $statement->getBoundVariables();
 
-        if ($realStatement instanceof \TYPO3\CMS\Core\Database\PreparedStatement) {
+        // The real statement is an instance of the Doctrine DBAL QueryBuilder, so fetching
+        // this directly is possible
+        if ($realStatement instanceof QueryBuilder) {
+            try {
+                $result = $realStatement->execute();
+            } catch (DBALException $e) {
+                throw new SqlErrorException($e->getPrevious()->getMessage(), 1472064721);
+            }
+            $rows = $result->fetchAll();
+        } elseif ($realStatement instanceof \Doctrine\DBAL\Statement) {
+            try {
+                $result = $realStatement->execute($parameters);
+            } catch (DBALException $e) {
+                throw new SqlErrorException($e->getPrevious()->getMessage(), 1481281404);
+            }
+            $rows = $result->fetchAll();
+        } elseif ($realStatement instanceof \TYPO3\CMS\Core\Database\PreparedStatement) {
             $realStatement->execute($parameters);
             $rows = $realStatement->fetchAll();
 
@@ -377,12 +394,7 @@ class Typo3DbBackend implements BackendInterface, SingletonInterface
                 throw new SqlErrorException($e->getPrevious()->getMessage(), 1472064775);
             }
 
-            $rows = [];
-            while ($row = $statement->fetch()) {
-                if (is_array($row)) {
-                    $rows[] = $row;
-                }
-            }
+            $rows = $statement->fetchAll();
         }
 
         return $rows;