[BUGFIX] Make Typo3DbBackend use DatabaseConnection 54/27254/9
authorFelix Oertel <felix@oer.tel>
Sat, 1 Feb 2014 23:25:12 +0000 (00:25 +0100)
committerStefan Neufeind <typo3.neufeind@speedpartner.de>
Thu, 20 Feb 2014 14:31:23 +0000 (15:31 +0100)
While Typo3DbBackend claims to be Extbase's connection to
the TYPO3 API, in reality it writes a lot of SQL state-
ments itself. This replaces self-written statements with
the according DatabaseConnection API call.

Releases: 6.2
Resolves: #55571
Change-Id: Ia4f6ef0aadda16b6c5e89c7b36c8f91b185aac25
Reviewed-on: https://review.typo3.org/27254
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Tymoteusz Motylewski
Tested-by: Tymoteusz Motylewski
Reviewed-by: Mathias Brodala
Reviewed-by: Stefan Neufeind
Tested-by: Stefan Neufeind
typo3/sysext/extbase/Classes/Persistence/Generic/Storage/BackendInterface.php
typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php

index 18327aa..3ce7e07 100644 (file)
@@ -36,50 +36,50 @@ interface BackendInterface {
         * Adds a row to the storage
         *
         * @param string $tableName The database table name
-        * @param array $row The row to insert
+        * @param array $fieldValues The fieldValues to insert
         * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
         * @return integer the UID of the inserted row
         */
-       public function addRow($tableName, array $row, $isRelation = FALSE);
+       public function addRow($tableName, array $fieldValues, $isRelation = FALSE);
 
        /**
         * Updates a row in the storage
         *
         * @param string $tableName The database table name
-        * @param array $row The row to update
+        * @param array $fieldValues The fieldValues to update
         * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
         * @return mixed|void
         */
-       public function updateRow($tableName, array $row, $isRelation = FALSE);
+       public function updateRow($tableName, array $fieldValues, $isRelation = FALSE);
 
        /**
         * Updates a relation row in the storage
         *
         * @param string $tableName The database relation table name
-        * @param array $row The row to be updated
+        * @param array $fieldValues The fieldValues to be updated
         * @return boolean
         */
-       public function updateRelationTableRow($tableName, array $row);
+       public function updateRelationTableRow($tableName, array $fieldValues);
 
        /**
         * Deletes a row in the storage
         *
         * @param string $tableName The database table name
-        * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
+        * @param array $where An array of where array('fieldname' => value). This array will be transformed to a WHERE clause
         * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
         * @return mixed|void
         */
-       public function removeRow($tableName, array $identifier, $isRelation = FALSE);
+       public function removeRow($tableName, array $where, $isRelation = FALSE);
 
        /**
         * Fetches maximal value for given table column
         *
         * @param string $tableName The database table name
-        * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
+        * @param array $where An array of where array('fieldname' => value). This array will be transformed to a WHERE clause
         * @param string $columnName column name to get the max value from
         * @return mixed the max value
         */
-       public function getMaxValueFromTable($tableName, $identifier, $columnName);
+       public function getMaxValueFromTable($tableName, array $where, $columnName);
 
        /**
         * Returns the number of items matching the query.
index 10a5639..4e98538 100644 (file)
@@ -114,28 +114,19 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * Adds a row to the storage
         *
         * @param string $tableName The database table name
-        * @param array $row The row to be inserted
-        * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
+        * @param array $fieldValues The row to be inserted
+        * @param bool $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
         * @return integer The uid of the inserted row
         */
-       public function addRow($tableName, array $row, $isRelation = FALSE) {
-               $fields = array();
-               $values = array();
-               $parameters = array();
-               if (isset($row['uid'])) {
-                       unset($row['uid']);
+       public function addRow($tableName, array $fieldValues, $isRelation = FALSE) {
+               if (isset($fieldValues['uid'])) {
+                       unset($fieldValues['uid']);
                }
-               foreach ($row as $columnName => $value) {
-                       $fields[] = $columnName;
-                       $values[] = '?';
-                       $parameters[] = $value;
-               }
-               $sqlString = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
-               $this->replacePlaceholders($sqlString, $parameters, $tableName);
-               // debug($sqlString,-2);
-               $this->databaseHandle->sql_query($sqlString);
-               $this->checkSqlErrors($sqlString);
+
+               $this->databaseHandle->exec_INSERTquery($tableName, $fieldValues);
+               $this->checkSqlErrors();
                $uid = $this->databaseHandle->sql_insert_id();
+
                if (!$isRelation) {
                        $this->clearPageCache($tableName, $uid);
                }
@@ -146,141 +137,137 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * Updates a row in the storage
         *
         * @param string $tableName The database table name
-        * @param array $row The row to be updated
-        * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
+        * @param array $fieldValues The row to be updated
+        * @param bool $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
         * @throws \InvalidArgumentException
-        * @return boolean
+        * @return bool
         */
-       public function updateRow($tableName, array $row, $isRelation = FALSE) {
-               if (!isset($row['uid'])) {
+       public function updateRow($tableName, array $fieldValues, $isRelation = FALSE) {
+               if (!isset($fieldValues['uid'])) {
                        throw new \InvalidArgumentException('The given row must contain a value for "uid".');
                }
-               $uid = (int)$row['uid'];
-               unset($row['uid']);
-               $fields = array();
-               $parameters = array();
-               foreach ($row as $columnName => $value) {
-                       $fields[] = $columnName . '=?';
-                       $parameters[] = $value;
-               }
-               $parameters[] = $uid;
-               $sqlString = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $fields) . ' WHERE uid=?';
-               $this->replacePlaceholders($sqlString, $parameters, $tableName);
-               // debug($sqlString,-2);
-               $returnValue = $this->databaseHandle->sql_query($sqlString);
-               $this->checkSqlErrors($sqlString);
+
+               $uid = (int)$fieldValues['uid'];
+               unset($fieldValues['uid']);
+
+               $updateSuccessful = $this->databaseHandle->exec_UPDATEquery($tableName, 'uid = '. $uid, $fieldValues);
+               $this->checkSqlErrors();
+
                if (!$isRelation) {
                        $this->clearPageCache($tableName, $uid);
                }
-               return $returnValue;
+
+               return $updateSuccessful;
        }
 
        /**
         * Updates a relation row in the storage.
         *
         * @param string $tableName The database relation table name
-        * @param array $row The row to be updated
+        * @param array $fieldValues The row to be updated
         * @throws \InvalidArgumentException
-        * @return boolean
+        * @return bool
         */
-       public function updateRelationTableRow($tableName, array $row) {
-               if (!isset($row['uid_local']) && !isset($row['uid_foreign'])) {
+       public function updateRelationTableRow($tableName, array $fieldValues) {
+               if (!isset($fieldValues['uid_local']) && !isset($fieldValues['uid_foreign'])) {
                        throw new \InvalidArgumentException(
-                               'The given row must contain a value for "uid_local" and "uid_foreign".', 1360500126
+                               'The given fieldValues must contain a value for "uid_local" and "uid_foreign".', 1360500126
                        );
                }
-               $uidLocal = (int)$row['uid_local'];
-               $uidForeign = (int)$row['uid_foreign'];
-               unset($row['uid_local']);
-               unset($row['uid_foreign']);
-               $fields = array();
-               $parameters = array();
-               foreach ($row as $columnName => $value) {
-                       $fields[] = $columnName . '=?';
-                       $parameters[] = $value;
-               }
-               $parameters[] = $uidLocal;
-               $parameters[] = $uidForeign;
 
-               $sqlString = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $fields) . ' WHERE uid_local=? AND uid_foreign=?';
-               $this->replacePlaceholders($sqlString, $parameters);
+               $where['uid_local'] = (int)$fieldValues['uid_local'];
+               $where['uid_foreign'] = (int)$fieldValues['uid_foreign'];
+               unset($fieldValues['uid_local']);
+               unset($fieldValues['uid_foreign']);
 
-               $returnValue = $this->databaseHandle->sql_query($sqlString);
-               $this->checkSqlErrors($sqlString);
+               $updateSuccessful = $this->databaseHandle->exec_UPDATEquery(
+                       $tableName,
+                       $this->resolveWhereStatement($where, $tableName),
+                       $fieldValues
+               );
+               $this->checkSqlErrors();
 
-               return $returnValue;
+               return $updateSuccessful;
        }
 
        /**
         * Deletes a row in the storage
         *
         * @param string $tableName The database table name
-        * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
-        * @param boolean $isRelation TRUE if we are currently manipulating a relation table, FALSE by default
-        * @return boolean
+        * @param array $where An array of where array('fieldname' => value).
+        * @param bool $isRelation TRUE if we are currently manipulating a relation table, FALSE by default
+        * @return bool
         */
-       public function removeRow($tableName, array $identifier, $isRelation = FALSE) {
-               $statement = 'DELETE FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier);
-               $this->replacePlaceholders($statement, $identifier, $tableName);
-               if (!$isRelation && isset($identifier['uid'])) {
-                       $this->clearPageCache($tableName, $identifier['uid'], $isRelation);
+       public function removeRow($tableName, array $where, $isRelation = FALSE) {
+               $deleteSuccessful = $this->databaseHandle->exec_DELETEquery(
+                       $tableName,
+                       $this->resolveWhereStatement($where, $tableName)
+               );
+               $this->checkSqlErrors();
+
+               if (!$isRelation && isset($where['uid'])) {
+                       $this->clearPageCache($tableName, $where['uid']);
                }
-               // debug($statement, -2);
-               $returnValue = $this->databaseHandle->sql_query($statement);
-               $this->checkSqlErrors($statement);
-               return $returnValue;
+
+               return $deleteSuccessful;
        }
 
        /**
         * Fetches maximal value for given table column from database.
         *
         * @param string $tableName The database table name
-        * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
+        * @param array $where An array of where array('fieldname' => value).
         * @param string $columnName column name to get the max value from
         * @return mixed the max value
         */
-       public function getMaxValueFromTable($tableName, $identifier, $columnName) {
-               $sqlString = 'SELECT ' . $columnName . ' FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier) . ' ORDER BY  ' . $columnName . ' DESC LIMIT 1';
-               $this->replacePlaceholders($sqlString, $identifier);
-
-               $result = $this->databaseHandle->sql_query($sqlString);
-               $row = $this->databaseHandle->sql_fetch_assoc($result);
-               $this->checkSqlErrors($sqlString);
-               return $row[$columnName];
+       public function getMaxValueFromTable($tableName, array $where, $columnName) {
+               $result = $this->databaseHandle->exec_SELECTgetSingleRow(
+                       $columnName,
+                       $tableName,
+                       $this->resolveWhereStatement($where, $tableName),
+                       '',
+                       $columnName . ' DESC',
+                       TRUE
+               );
+               $this->checkSqlErrors();
+
+               return $result[0];
        }
 
        /**
         * Fetches row data from the database
         *
         * @param string $tableName
-        * @param array $identifier The Identifier of the row to fetch
-        * @return array|boolean
+        * @param array $where An array of where array('fieldname' => value).
+        * @return array|bool
         */
-       public function getRowByIdentifier($tableName, array $identifier) {
-               $statement = 'SELECT * FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier);
-               $this->replacePlaceholders($statement, $identifier, $tableName);
-               // debug($statement,-2);
-               $res = $this->databaseHandle->sql_query($statement);
-               $this->checkSqlErrors($statement);
-               $row = $this->databaseHandle->sql_fetch_assoc($res);
-               if ($row !== FALSE) {
-                       return $row;
-               } else {
-                       return FALSE;
-               }
+       public function getRowByIdentifier($tableName, array $where) {
+               $row = $this->databaseHandle->exec_SELECTgetSingleRow(
+                       '*',
+                       $tableName,
+                       $this->resolveWhereStatement($where, $tableName)
+               );
+               $this->checkSqlErrors();
+
+               return $row ?: FALSE;
        }
 
        /**
-        * @param array $identifier
+        * Converts an array to an AND concatenated where statement
+        *
+        * @param array $where array('fieldName' => 'fieldValue')
+        * @param string $tableName table to use for escaping config
+        *
         * @return string
         */
-       protected function parseIdentifier(array $identifier) {
-               $fieldNames = array_keys($identifier);
-               $suffixedFieldNames = array();
-               foreach ($fieldNames as $fieldName) {
-                       $suffixedFieldNames[] = $fieldName . '=?';
+       protected function resolveWhereStatement(array $where, $tableName = 'foo') {
+               $whereStatement = array();
+
+               foreach ($where as $fieldName => $fieldValue) {
+                       $whereStatement[] = $fieldName . ' = ' . $this->databaseHandle->fullQuoteStr($fieldValue, $tableName);
                }
-               return implode(' AND ', $suffixedFieldNames);
+
+               return implode(' AND ', $whereStatement);
        }
 
        /**
@@ -483,7 +470,7 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * @param array &$sql The query parts
         * @return void
         */
-       protected function addRecordTypeConstraint($className, &$sql) {
+       protected function addRecordTypeConstraint($className, array &$sql) {
                if ($className !== NULL) {
                        $dataMap = $this->dataMapper->getDataMap($className);
                        if ($dataMap->getRecordTypeColumnName() !== NULL) {
@@ -728,7 +715,7 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
        /**
         * @param string &$className
         * @param string &$tableName
-        * @param array &$propertyPath
+        * @param string &$propertyPath
         * @param array &$sql
         * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception
         * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidRelationConfigurationException
@@ -936,13 +923,13 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * Returns constraint statement for frontend context
         *
         * @param string $tableName
-        * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
+        * @param bool $ignoreEnableFields A flag indicating whether the enable fields should be ignored
         * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is NULL or an empty array (default) all enable fields are ignored.
-        * @param boolean $includeDeleted A flag indicating whether deleted records should be included
+        * @param bool $includeDeleted A flag indicating whether deleted records should be included
         * @return string
         * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InconsistentQuerySettingsException
         */
-       protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored = array(), $includeDeleted) {
+       protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, array $enableFieldsToBeIgnored = array(), $includeDeleted) {
                $statement = '';
                if ($ignoreEnableFields && !$includeDeleted) {
                        if (count($enableFieldsToBeIgnored)) {
@@ -963,8 +950,8 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * Returns constraint statement for backend context
         *
         * @param string $tableName
-        * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
-        * @param boolean $includeDeleted A flag indicating whether deleted records should be included
+        * @param bool $ignoreEnableFields A flag indicating whether the enable fields should be ignored
+        * @param bool $includeDeleted A flag indicating whether deleted records should be included
         * @return string
         */
        protected function getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted) {