TYPO3 API  SVNRelease
Typo3DbBackend.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
00006 *  All rights reserved
00007 *
00008 *  This class is a backport of the corresponding class of FLOW3.
00009 *  All credits go to the v5 team.
00010 *
00011 *  This script is part of the TYPO3 project. The TYPO3 project is
00012 *  free software; you can redistribute it and/or modify
00013 *  it under the terms of the GNU General Public License as published by
00014 *  the Free Software Foundation; either version 2 of the License, or
00015 *  (at your option) any later version.
00016 *
00017 *  The GNU General Public License can be found at
00018 *  http://www.gnu.org/copyleft/gpl.html.
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 
00028 /**
00029  * A Storage backend
00030  *
00031  * @package Extbase
00032  * @subpackage Persistence\Storage
00033  * @version $Id: Typo3DbBackend.php 2297 2010-05-25 15:52:18Z jocrau $
00034  */
00035 class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persistence_Storage_BackendInterface, t3lib_Singleton {
00036 
00037     const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
00038     const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
00039 
00040     /**
00041      * The TYPO3 database object
00042      *
00043      * @var t3lib_db
00044      */
00045     protected $databaseHandle;
00046 
00047     /**
00048      * @var Tx_Extbase_Persistence_DataMapper
00049      */
00050     protected $dataMapper;
00051 
00052     /**
00053      * The TYPO3 page select object. Used for language and workspace overlay
00054      *
00055      * @var t3lib_pageSelect
00056      */
00057     protected $pageSelectObject;
00058 
00059     /**
00060      * A first-level TypoScript configuration cache
00061      *
00062      * @var array
00063      */
00064     protected $pageTSConfigCache = array();
00065 
00066     /**
00067      * Caches information about tables (esp. the existing column names)
00068      *
00069      * @var array
00070      */
00071     protected $tableInformationCache = array();
00072 
00073     /**
00074      * @var Tx_Extbase_Configuration_ConfigurationManagerInterface
00075      */
00076     protected $configurationManager;
00077 
00078     /**
00079      * Constructor. takes the database handle from $GLOBALS['TYPO3_DB']
00080      */
00081     public function __construct() {
00082         $this->databaseHandle = $GLOBALS['TYPO3_DB'];
00083     }
00084 
00085     /**
00086      * @param Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager
00087      * @return void
00088      */
00089     public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
00090         $this->configurationManager = $configurationManager;
00091     }
00092 
00093     /**
00094      * Injects the DataMapper to map nodes to objects
00095      *
00096      * @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
00097      * @return void
00098      */
00099     public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper) {
00100         $this->dataMapper = $dataMapper;
00101     }
00102 
00103     /**
00104      * Adds a row to the storage
00105      *
00106      * @param string $tableName The database table name
00107      * @param array $row The row to be inserted
00108      * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
00109      * @return int The uid of the inserted row
00110      */
00111     public function addRow($tableName, array $row, $isRelation = FALSE) {
00112         $fields = array();
00113         $values = array();
00114         $parameters = array();
00115         if (isset($row['uid'])) {
00116             unset($row['uid']);
00117         }
00118         foreach ($row as $columnName => $value) {
00119             $fields[] = $columnName;
00120             $values[] = '?';
00121             $parameters[] = $value;
00122         }
00123 
00124         $sqlString = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
00125         $this->replacePlaceholders($sqlString, $parameters);
00126         // debug($sqlString,-2);
00127         $this->databaseHandle->sql_query($sqlString);
00128         $this->checkSqlErrors($sqlString);
00129         $uid = $this->databaseHandle->sql_insert_id();
00130         if (!$isRelation) {
00131             $this->clearPageCache($tableName, $uid);
00132         }
00133         return (int)$uid;
00134     }
00135 
00136     /**
00137      * Updates a row in the storage
00138      *
00139      * @param string $tableName The database table name
00140      * @param array $row The row to be updated
00141      * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
00142      * @return void
00143      */
00144     public function updateRow($tableName, array $row, $isRelation = FALSE) {
00145         if (!isset($row['uid'])) throw new InvalidArgumentException('The given row must contain a value for "uid".');
00146         $uid = (int)$row['uid'];
00147         unset($row['uid']);
00148         $fields = array();
00149         $parameters = array();
00150         foreach ($row as $columnName => $value) {
00151             $fields[] = $columnName . '=?';
00152             $parameters[] = $value;
00153         }
00154         $parameters[] = $uid;
00155 
00156         $sqlString = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $fields) . ' WHERE uid=?';
00157         $this->replacePlaceholders($sqlString, $parameters);
00158         // debug($sqlString,-2);
00159         $returnValue = $this->databaseHandle->sql_query($sqlString);
00160         $this->checkSqlErrors($sqlString);
00161         if (!$isRelation) {
00162             $this->clearPageCache($tableName, $uid);
00163         }
00164         return $returnValue;
00165     }
00166 
00167     /**
00168      * Deletes a row in the storage
00169      *
00170      * @param string $tableName The database table name
00171      * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause
00172      * @param boolean $isRelation TRUE if we are currently manipulating a relation table, FALSE by default
00173      * @return void
00174      */
00175     public function removeRow($tableName, array $identifier, $isRelation = FALSE) {
00176         $statement = 'DELETE FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier);
00177         $this->replacePlaceholders($statement, $identifier);
00178         if (!$isRelation && isset($identifier['uid'])) {
00179             $this->clearPageCache($tableName, $identifier['uid'], $isRelation);
00180         }
00181         // debug($statement, -2);
00182         $returnValue = $this->databaseHandle->sql_query($statement);
00183         $this->checkSqlErrors($statement);
00184         return $returnValue;
00185     }
00186 
00187     /**
00188      * Fetches row data from the database
00189      *
00190      * @param string $identifier The Identifier of the row to fetch
00191      * @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap The Data Map
00192      * @return array|FALSE
00193      */
00194     public function getRowByIdentifier($tableName, array $identifier) {
00195         $statement = 'SELECT * FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier);
00196         $this->replacePlaceholders($statement, $identifier);
00197         // debug($statement,-2);
00198         $res = $this->databaseHandle->sql_query($statement);
00199         $this->checkSqlErrors($statement);
00200         $row = $this->databaseHandle->sql_fetch_assoc($res);
00201         if ($row !== FALSE) {
00202             return $row;
00203         } else {
00204             return FALSE;
00205         }
00206     }
00207 
00208     protected function parseIdentifier(array $identifier) {
00209         $fieldNames = array_keys($identifier);
00210         $suffixedFieldNames = array();
00211         foreach ($fieldNames as $fieldName) {
00212             $suffixedFieldNames[] = $fieldName . '=?';
00213         }
00214         return implode(' AND ', $suffixedFieldNames);
00215     }
00216 
00217     /**
00218      * Returns the object data matching the $query.
00219      *
00220      * @param Tx_Extbase_Persistence_QueryInterface $query
00221      * @return array
00222      * @author Karsten Dambekalns <karsten@typo3.org>
00223      */
00224     public function getObjectDataByQuery(Tx_Extbase_Persistence_QueryInterface $query) {
00225         $parameters = array();
00226 
00227         $statement = $query->getStatement();
00228         if($statement instanceof Tx_Extbase_Persistence_QOM_Statement) {
00229             $sql = $statement->getStatement();
00230             $parameters = $statement->getBoundVariables();
00231         } else {
00232             $parameters = array();
00233             $statementParts = $this->parseQuery($query, $parameters);
00234             $sql = $this->buildQuery($statementParts, $parameters);
00235         }
00236         $this->replacePlaceholders($sql, $parameters);
00237         // debug($sql,-2);
00238         $result = $this->databaseHandle->sql_query($sql);
00239         $this->checkSqlErrors($sql);
00240         $rows = $this->getRowsFromResult($query->getSource(), $result);
00241         $this->databaseHandle->sql_free_result($result);
00242         $rows = $this->doLanguageAndWorkspaceOverlay($query->getSource(), $rows);
00243         // TODO: implement $objectData = $this->processObjectRecords($statementHandle);
00244         return $rows;
00245     }
00246 
00247     /**
00248      * Returns the number of tuples matching the query.
00249      *
00250      * @param Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query
00251      * @return integer The number of matching tuples
00252      */
00253     public function getObjectCountByQuery(Tx_Extbase_Persistence_QueryInterface $query) {
00254         $constraint = $query->getConstraint();
00255         if($constraint instanceof Tx_Extbase_Persistence_QOM_StatementInterface) {
00256             throw new Tx_Extbase_Persistence_Storage_Exception_BadConstraint('Could not execute count on queries with a constraint of type Tx_Extbase_Persistence_QOM_StatementInterface', 1256661045);
00257         }
00258         $parameters = array();
00259         $statementParts = $this->parseQuery($query, $parameters);
00260         // if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
00261         if (!empty($statementParts['limit'])) {
00262             $statement = $this->buildQuery($statementParts, $parameters);
00263             $this->replacePlaceholders($statement, $parameters);
00264             $result = $this->databaseHandle->sql_query($statement);
00265             $this->checkSqlErrors($statement);
00266             $count = $this->databaseHandle->sql_num_rows($result);
00267         } else {
00268             $statementParts['fields'] = array('COUNT(*)');
00269             $statement = $this->buildQuery($statementParts, $parameters);
00270             $this->replacePlaceholders($statement, $parameters);
00271             $result = $this->databaseHandle->sql_query($statement);
00272             $this->checkSqlErrors($statement);
00273             $rows = $this->getRowsFromResult($query->getSource(), $result);
00274             $count = current(current($rows));
00275         }
00276         $this->databaseHandle->sql_free_result($result);
00277         return $count;
00278     }
00279 
00280     /**
00281      * Parses the query and returns the SQL statement parts.
00282      *
00283      * @param Tx_Extbase_Persistence_QueryInterface $query The query
00284      * @return array The SQL statement parts
00285      */
00286     public function parseQuery(Tx_Extbase_Persistence_QueryInterface $query, array &$parameters) {
00287         $sql = array();
00288         $sql['keywords'] = array();
00289         $sql['tables'] = array();
00290         $sql['unions'] = array();
00291         $sql['fields'] = array();
00292         $sql['where'] = array();
00293         $sql['additionalWhereClause'] = array();
00294         $sql['orderings'] = array();
00295         $sql['limit'] = array();
00296 
00297         $source = $query->getSource();
00298 
00299         $this->parseSource($source, $sql, $parameters);
00300         $this->parseConstraint($query->getConstraint(), $source, $sql, $parameters);
00301         $this->parseOrderings($query->getOrderings(), $source, $sql);
00302         $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $sql);
00303 
00304         $tableNames = array_unique(array_keys($sql['tables'] + $sql['unions']));
00305         foreach ($tableNames as $tableName) {
00306             if (is_string($tableName) && strlen($tableName) > 0) {
00307                 $this->addAdditionalWhereClause($query->getQuerySettings(), $tableName, $sql);
00308             }
00309         }
00310 
00311         return $sql;
00312     }
00313 
00314     /**
00315      * Returns the statement, ready to be executed.
00316      *
00317      * @param array $sql The SQL statement parts
00318      * @return string The SQL statement
00319      */
00320     public function buildQuery(array $sql) {
00321         $statement = 'SELECT ' . implode(' ', $sql['keywords']) . ' '. implode(',', $sql['fields']) . ' FROM ' . implode(' ', $sql['tables']) . ' '. implode(' ', $sql['unions']);
00322         if (!empty($sql['where'])) {
00323             $statement .= ' WHERE ' . implode('', $sql['where']);
00324             if (!empty($sql['additionalWhereClause'])) {
00325                 $statement .= ' AND ' . implode(' AND ', $sql['additionalWhereClause']);
00326             }
00327         } elseif (!empty($sql['additionalWhereClause'])) {
00328             $statement .= ' WHERE ' . implode(' AND ', $sql['additionalWhereClause']);
00329         }
00330         if (!empty($sql['orderings'])) {
00331             $statement .= ' ORDER BY ' . implode(', ', $sql['orderings']);
00332         }
00333         if (!empty($sql['limit'])) {
00334             $statement .= ' LIMIT ' . $sql['limit'];
00335         }
00336         return $statement;
00337     }
00338 
00339     /**
00340      * Checks if a Value Object equal to the given Object exists in the data base
00341      *
00342      * @param Tx_Extbase_DomainObject_AbstractValueObject $object The Value Object
00343      * @return array The matching uid
00344      */
00345     public function getUidOfAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object) {
00346         $fields = array();
00347         $parameters = array();
00348         $dataMap = $this->dataMapper->getDataMap(get_class($object));
00349         $properties = $object->_getProperties();
00350         foreach ($properties as $propertyName => $propertyValue) {
00351             // FIXME We couple the Backend to the Entity implementation (uid, isClone); changes there breaks this method
00352             if ($dataMap->isPersistableProperty($propertyName) && ($propertyName !== 'uid')  && ($propertyName !== 'pid') && ($propertyName !== 'isClone')) {
00353                 if ($propertyValue === NULL) {
00354                     $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . ' IS NULL';
00355                 } else {
00356                     $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . '=?';
00357                     $parameters[] = $this->getPlainValue($propertyValue);
00358                 }
00359             }
00360         }
00361         $sql = array();
00362         $sql['additionalWhereClause'] = array();
00363 
00364         $tableName = $dataMap->getTableName();
00365         $this->addEnableFieldsStatement($tableName, $sql);
00366 
00367         $statement = 'SELECT * FROM ' . $tableName;
00368         $statement .= ' WHERE ' . implode(' AND ', $fields);
00369         if (!empty($sql['additionalWhereClause'])) {
00370             $statement .= ' AND ' . implode(' AND ', $sql['additionalWhereClause']);
00371         }
00372         $this->replacePlaceholders($statement, $parameters);
00373         // debug($statement,-2);
00374         $res = $this->databaseHandle->sql_query($statement);
00375         $this->checkSqlErrors($statement);
00376         $row = $this->databaseHandle->sql_fetch_assoc($res);
00377         if ($row !== FALSE) {
00378             return (int)$row['uid'];
00379         } else {
00380             return FALSE;
00381         }
00382     }
00383 
00384     /**
00385      * Transforms a Query Source into SQL and parameter arrays
00386      *
00387      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
00388      * @param array &$sql
00389      * @param array &$parameters
00390      * @return void
00391      */
00392     protected function parseSource(Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql) {
00393         if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
00394             $className = $source->getNodeTypeName();
00395             $tableName = $this->dataMapper->getDataMap($className)->getTableName();
00396             $this->addRecordTypeConstraint($className, $sql);
00397             $sql['fields'][$tableName] = $tableName . '.*';
00398             $sql['tables'][$tableName] = $tableName;
00399         } elseif ($source instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
00400             $this->parseJoin($source, $sql);
00401         }
00402     }
00403 
00404     /**
00405      * Adda a constrint to ensure that the record type of the returned tuples is matching the data type of the repository.
00406      *
00407      * @param string $className The class name
00408      * @param array &$sql The query parts
00409      * @return void
00410      */
00411     protected function addRecordTypeConstraint($className, &$sql) {
00412         if ($className !== NULL) {
00413             $dataMap = $this->dataMapper->getDataMap($className);
00414             if ($dataMap->getRecordTypeColumnName() !== NULL) {
00415                 $recordTypes = array();
00416                 if ($dataMap->getRecordType() !== NULL) {
00417                     $recordTypes[] = $dataMap->getRecordType();
00418                 }
00419                 foreach ($dataMap->getSubclasses() as $subclassName) {
00420                     $subclassDataMap = $this->dataMapper->getDataMap($subclassName);
00421                     if ($subclassDataMap->getRecordType() !== NULL) {
00422                         $recordTypes[] = $subclassDataMap->getRecordType();
00423                     }
00424                 }
00425                 if (count($recordTypes) > 0) {
00426                     $recordTypeStatements = array();
00427                     foreach ($recordTypes as $recordType) {
00428                         $recordTypeStatements[] = $dataMap->getTableName() . '.' . $dataMap->getRecordTypeColumnName() . '=' . $this->databaseHandle->fullQuoteStr($recordType, 'foo');
00429                     }
00430                     $sql['additionalWhereClause'][] = '(' . implode(' OR ', $recordTypeStatements) . ')';
00431                 }
00432             }
00433         }
00434     }
00435 
00436     /**
00437      * Transforms a Join into SQL and parameter arrays
00438      *
00439      * @param Tx_Extbase_Persistence_QOM_JoinInterface $join The join
00440      * @param array &$sql The query parts
00441      * @return void
00442      */
00443     protected function parseJoin(Tx_Extbase_Persistence_QOM_JoinInterface $join, array &$sql) {
00444         $leftSource = $join->getLeft();
00445         $leftClassName = $leftSource->getNodeTypeName();
00446         $this->addRecordTypeConstraint($leftClassName, $sql);
00447         $leftTableName = $leftSource->getSelectorName();
00448         // $sql['fields'][$leftTableName] = $leftTableName . '.*';
00449         $rightSource = $join->getRight();
00450         if ($rightSource instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
00451             $rightClassName = $rightSource->getLeft()->getNodeTypeName();
00452             $rightTableName = $rightSource->getLeft()->getSelectorName();
00453         } else {
00454             $rightClassName = $rightSource->getNodeTypeName();
00455             $rightTableName = $rightSource->getSelectorName();
00456             $sql['fields'][$leftTableName] = $rightTableName . '.*';
00457         }
00458         $this->addRecordTypeConstraint($rightClassName, $sql);
00459 
00460         $sql['tables'][$leftTableName] = $leftTableName;
00461         $sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
00462 
00463         $joinCondition = $join->getJoinCondition();
00464         if ($joinCondition instanceof Tx_Extbase_Persistence_QOM_EquiJoinCondition) {
00465             $column1Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty1Name(), $leftClassName);
00466             $column2Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty2Name(), $rightClassName);
00467             $sql['unions'][$rightTableName] .= ' ON ' . $joinCondition->getSelector1Name() . '.' . $column1Name . ' = ' . $joinCondition->getSelector2Name() . '.' . $column2Name;
00468         }
00469         if ($rightSource instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
00470             $this->parseJoin($rightSource, $sql);
00471         }
00472     }
00473 
00474     /**
00475      * Transforms a constraint into SQL and parameter arrays
00476      *
00477      * @param Tx_Extbase_Persistence_QOM_ConstraintInterface $constraint The constraint
00478      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
00479      * @param array &$sql The query parts
00480      * @param array &$parameters The parameters that will replace the markers
00481      * @param array $boundVariableValues The bound variables in the query (key) and their values (value)
00482      * @return void
00483      */
00484     protected function parseConstraint(Tx_Extbase_Persistence_QOM_ConstraintInterface $constraint = NULL, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters) {
00485         if ($constraint instanceof Tx_Extbase_Persistence_QOM_AndInterface) {
00486             $sql['where'][] = '(';
00487             $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
00488             $sql['where'][] = ' AND ';
00489             $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
00490             $sql['where'][] = ')';
00491         } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_OrInterface) {
00492             $sql['where'][] = '(';
00493             $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters);
00494             $sql['where'][] = ' OR ';
00495             $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters);
00496             $sql['where'][] = ')';
00497         } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_NotInterface) {
00498             $sql['where'][] = 'NOT (';
00499             $this->parseConstraint($constraint->getConstraint(), $source, $sql, $parameters);
00500             $sql['where'][] = ')';
00501         } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_ComparisonInterface) {
00502             $this->parseComparison($constraint, $source, $sql, $parameters);
00503         }
00504     }
00505 
00506     /**
00507      * Parse a Comparison into SQL and parameter arrays.
00508      *
00509      * @param Tx_Extbase_Persistence_QOM_ComparisonInterface $comparison The comparison to parse
00510      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
00511      * @param array &$sql SQL query parts to add to
00512      * @param array &$parameters Parameters to bind to the SQL
00513      * @param array $boundVariableValues The bound variables in the query and their values
00514      * @return void
00515      */
00516     protected function parseComparison(Tx_Extbase_Persistence_QOM_ComparisonInterface $comparison, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters) {
00517         $operand1 = $comparison->getOperand1();
00518         $operator = $comparison->getOperator();
00519         $operand2 = $comparison->getOperand2();
00520         if (($operator === Tx_Extbase_Persistence_QueryInterface::OPERATOR_EQUAL_TO) && (is_array($operand2) || ($operand2 instanceof ArrayAccess) || ($operand2 instanceof Traversable))) {
00521             // this else branch enables equals() to behave like in(). This behavior is deprecated and will be removed in future. Use in() instead.
00522             $operator = Tx_Extbase_Persistence_QueryInterface::OPERATOR_IN;
00523         }
00524 
00525         if ($operator === Tx_Extbase_Persistence_QueryInterface::OPERATOR_IN) {
00526             $items = array();
00527             $hasValue = FALSE;
00528             foreach ($operand2 as $value) {
00529                 $value = $this->getPlainValue($value);
00530                 if ($value !== NULL) {
00531                     $items[] = $value;
00532                     $hasValue = TRUE;
00533                 }
00534             }
00535             if ($hasValue === FALSE) {
00536                 $sql['where'][] = '1<>1';
00537             } else {
00538                 $this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters, NULL, $operand2);
00539                 $parameters[] = $items;
00540             }
00541         } elseif ($operator === Tx_Extbase_Persistence_QueryInterface::OPERATOR_CONTAINS) {
00542             if ($operand2 === NULL) {
00543                 $sql['where'][] = '1<>1';
00544             } else {
00545                 $className = $source->getNodeTypeName();
00546                 $tableName = $this->dataMapper->convertClassNameToTableName($className);
00547                 $propertyName = $operand1->getPropertyName();
00548                 while (strpos($propertyName, '.') !== FALSE) {
00549                     $this->addUnionStatement($className, $tableName, $propertyName, $sql);
00550                 }
00551                 $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
00552                 $dataMap = $this->dataMapper->getDataMap($className);
00553                 $columnMap = $dataMap->getColumnMap($propertyName);
00554                 $typeOfRelation = $columnMap->getTypeOfRelation();
00555                 if ($typeOfRelation === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
00556                     $relationTableName = $columnMap->getRelationTableName();
00557                     $sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=' . $this->getPlainValue($operand2) . ')';
00558                 } elseif ($typeOfRelation === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
00559                     $parentKeyFieldName = $columnMap->getParentKeyFieldName();
00560                     if (isset($parentKeyFieldName)) {
00561                         $childTableName = $columnMap->getChildTableName();
00562                         $sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=' . $this->getPlainValue($operand2) . ')';
00563                     } else {
00564                         $statement = 'FIND_IN_SET(' . $this->getPlainValue($operand2) . ',' . $tableName . '.' . $columnName . ')';
00565                         $sql['where'][] = $statement;
00566                     }
00567                 } else {
00568                     throw new Tx_Extbase_Persistence_Exception_RepositoryException('Unsupported relation for contains().', 1267832524);
00569                 }
00570             }
00571         } else {
00572             if ($operand2 === NULL) {
00573                 if ($operator === Tx_Extbase_Persistence_QueryInterface::OPERATOR_EQUAL_TO) {
00574                     $operator = self::OPERATOR_EQUAL_TO_NULL;
00575                 } elseif ($operator === Tx_Extbase_Persistence_QueryInterface::OPERATOR_NOT_EQUAL_TO) {
00576                     $operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
00577                 }
00578             }
00579             $this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters);
00580             $parameters[] = $this->getPlainValue($operand2);
00581         }
00582     }
00583 
00584     /**
00585      * Returns a plain value, i.e. objects are flattened out if possible.
00586      *
00587      * @param mixed $input
00588      * @return mixed
00589      */
00590     protected function getPlainValue($input) {
00591         if (is_array($input)) {
00592             throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
00593         }
00594         if ($input instanceof DateTime) {
00595             return $input->format('U');
00596         } elseif (is_object($input)) {
00597             if ($input instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
00598                 return $input->getUid();
00599             } else {
00600                 throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('An object of class "' . get_class($input) . '" could not be converted to a plain value.', 1274799934);
00601             }
00602         } elseif (is_bool($input)) {
00603             return $input === TRUE ? 1 : 0;
00604         } else {
00605             return $input;
00606         }
00607     }
00608 
00609     /**
00610      * Parse a DynamicOperand into SQL and parameter arrays.
00611      *
00612      * @param Tx_Extbase_Persistence_QOM_DynamicOperandInterface $operand
00613      * @param string $operator One of the JCR_OPERATOR_* constants
00614      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
00615      * @param array &$sql The query parts
00616      * @param array &$parameters The parameters that will replace the markers
00617      * @param string $valueFunction an optional SQL function to apply to the operand value
00618      * @return void
00619      */
00620     protected function parseDynamicOperand(Tx_Extbase_Persistence_QOM_DynamicOperandInterface $operand, $operator, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters, $valueFunction = NULL, $operand2 = NULL) {
00621         if ($operand instanceof Tx_Extbase_Persistence_QOM_LowerCaseInterface) {
00622             $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'LOWER');
00623         } elseif ($operand instanceof Tx_Extbase_Persistence_QOM_UpperCaseInterface) {
00624             $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'UPPER');
00625         } elseif ($operand instanceof Tx_Extbase_Persistence_QOM_PropertyValueInterface) {
00626             $propertyName = $operand->getPropertyName();
00627             if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) { // FIXME Only necessary to differ from  Join
00628                 $className = $source->getNodeTypeName();
00629                 $tableName = $this->dataMapper->convertClassNameToTableName($className);
00630                 while (strpos($propertyName, '.') !== FALSE) {
00631                     $this->addUnionStatement($className, $tableName, $propertyName, $sql);
00632                 }
00633             } elseif ($source instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
00634                 $tableName = $source->getJoinCondition()->getSelector1Name();
00635             }
00636             $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
00637             $operator = $this->resolveOperator($operator);
00638             if ($valueFunction === NULL) {
00639                 $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName .  ' ' . $operator . ' ?';
00640             } else {
00641                 $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName .  ') ' . $operator . ' ?';
00642             }
00643 
00644             $sql['where'][] = $constraintSQL;
00645         }
00646     }
00647 
00648     protected function addUnionStatement(&$className, &$tableName, &$propertyPath, array &$sql) {
00649         $explodedPropertyPath = explode('.', $propertyPath, 2);
00650         $propertyName = $explodedPropertyPath[0];
00651         $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
00652         $tableName = $this->dataMapper->convertClassNameToTableName($className);
00653         $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
00654         $parentKeyFieldName = $columnMap->getParentKeyFieldName();
00655         $childTableName = $columnMap->getChildTableName();
00656         if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
00657             if (isset($parentKeyFieldName)) {
00658                 $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
00659             } else {
00660                 $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableName . '.uid';
00661             }
00662             $className = $this->dataMapper->getType($className, $propertyName);
00663         } elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
00664             if (isset($parentKeyFieldName)) {
00665                 $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
00666             } else {
00667                 $onStatement = '(FIND_IN_SET(' . $childTableName . '.uid, ' . $tableName . '.' . $columnName . '))';
00668                 $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $onStatement;
00669             }
00670             $className = $this->dataMapper->getType($className, $propertyName);
00671         } elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
00672             $relationTableName = $columnMap->getRelationTableName();
00673             $sql['unions'][$relationTableName] = 'LEFT JOIN ' . $relationTableName . ' ON ' . $tableName . '.uid=' . $relationTableName . '.uid_local';
00674             $sql['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $relationTableName . '.uid_foreign=' . $childTableName . '.uid';
00675             $className = $this->dataMapper->getType($className, $propertyName);
00676         } else {
00677             throw new Tx_Extbase_Persistence_Exception('Could not determine type of relation.', 1252502725);
00678         }
00679         // TODO check if there is another solution for this
00680         $sql['keywords']['distinct'] = 'DISTINCT';
00681         $propertyPath = $explodedPropertyPath[1];
00682         $tableName = $childTableName;
00683     }
00684 
00685     /**
00686      * Returns the SQL operator for the given JCR operator type.
00687      *
00688      * @param string $operator One of the JCR_OPERATOR_* constants
00689      * @return string an SQL operator
00690      */
00691     protected function resolveOperator($operator) {
00692         switch ($operator) {
00693             case self::OPERATOR_EQUAL_TO_NULL:
00694                 $operator = 'IS';
00695                 break;
00696             case self::OPERATOR_NOT_EQUAL_TO_NULL:
00697                 $operator = 'IS NOT';
00698                 break;
00699             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_IN:
00700                 $operator = 'IN';
00701                 break;
00702             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_EQUAL_TO:
00703                 $operator = '=';
00704                 break;
00705             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_NOT_EQUAL_TO:
00706                 $operator = '!=';
00707                 break;
00708             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_LESS_THAN:
00709                 $operator = '<';
00710                 break;
00711             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
00712                 $operator = '<=';
00713                 break;
00714             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_GREATER_THAN:
00715                 $operator = '>';
00716                 break;
00717             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
00718                 $operator = '>=';
00719                 break;
00720             case Tx_Extbase_Persistence_QueryInterface::OPERATOR_LIKE:
00721                 $operator = 'LIKE';
00722                 break;
00723             default:
00724                 throw new Tx_Extbase_Persistence_Exception('Unsupported operator encountered.', 1242816073);
00725         }
00726 
00727         return $operator;
00728     }
00729 
00730     /**
00731      * Replace query placeholders in a query part by the given
00732      * parameters.
00733      *
00734      * @param string $sqlString The query part with placeholders
00735      * @param array $parameters The parameters
00736      * @return string The query part with replaced placeholders
00737      */
00738     protected function replacePlaceholders(&$sqlString, array $parameters) {
00739         // TODO profile this method again
00740         if (substr_count($sqlString, '?') !== count($parameters)) throw new Tx_Extbase_Persistence_Exception('The number of question marks to replace must be equal to the number of parameters.', 1242816074);
00741         $offset = 0;
00742         foreach ($parameters as $parameter) {
00743             $markPosition = strpos($sqlString, '?', $offset);
00744             if ($markPosition !== FALSE) {
00745                 if ($parameter === NULL) {
00746                     $parameter = 'NULL';
00747                 } elseif (is_array($parameter) || ($parameter instanceof ArrayAccess) || ($parameter instanceof Traversable)) {
00748                     $items = array();
00749                     foreach ($parameter as $item) {
00750                         $items[] = $this->databaseHandle->fullQuoteStr($item, 'foo');
00751                     }
00752                     $parameter = '(' . implode(',', $items) . ')';
00753                 } else {
00754                     $parameter = $this->databaseHandle->fullQuoteStr($parameter, 'foo'); // FIXME This may not work with DBAL; check this
00755                 }
00756                 $sqlString = substr($sqlString, 0, $markPosition) . $parameter . substr($sqlString, $markPosition + 1);
00757             }
00758             $offset = $markPosition + strlen($parameter);
00759         }
00760     }
00761 
00762     /**
00763      * Adds additional WHERE statements according to the query settings.
00764      *
00765      * @param Tx_Extbase_Persistence_QuerySettingsInterface $querySettings The TYPO3 4.x specific query settings
00766      * @param string $tableName The table name to add the additional where clause for
00767      * @param string $sql
00768      * @return void
00769      */
00770     protected function addAdditionalWhereClause(Tx_Extbase_Persistence_QuerySettingsInterface $querySettings, $tableName, &$sql) {
00771         if ($querySettings instanceof Tx_Extbase_Persistence_Typo3QuerySettings) {
00772             if ($querySettings->getRespectEnableFields()) {
00773                 $this->addEnableFieldsStatement($tableName, $sql);
00774             }
00775             if ($querySettings->getRespectSysLanguage()) {
00776                 $this->addSysLanguageStatement($tableName, $sql);
00777             }
00778             if ($querySettings->getRespectStoragePage()) {
00779                 $this->addPageIdStatement($tableName, $sql, $querySettings->getStoragePageIds());
00780             }
00781         }
00782     }
00783 
00784     /**
00785      * Builds the enable fields statement
00786      *
00787      * @param string $tableName The database table name
00788      * @param array &$sql The query parts
00789      * @return void
00790      */
00791     protected function addEnableFieldsStatement($tableName, array &$sql) {
00792         if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
00793             if (TYPO3_MODE === 'FE') {
00794                 $statement = $GLOBALS['TSFE']->sys_page->enableFields($tableName);
00795             } else { // TYPO3_MODE === 'BE'
00796                 $statement = t3lib_BEfunc::deleteClause($tableName);
00797                 $statement .= t3lib_BEfunc::BEenableFields($tableName);
00798             }
00799             if(!empty($statement)) {
00800                 $statement = substr($statement, 5);
00801                 $sql['additionalWhereClause'][] = $statement;
00802             }
00803         }
00804     }
00805 
00806     /**
00807      * Builds the language field statement
00808      *
00809      * @param string $tableName The database table name
00810      * @param array &$sql The query parts
00811      * @return void
00812      */
00813     protected function addSysLanguageStatement($tableName, array &$sql) {
00814         if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
00815             if(isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField']) && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== NULL) {
00816                 $sql['additionalWhereClause'][] = $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (0,-1)';
00817             }
00818         }
00819     }
00820 
00821     /**
00822      * Builds the page ID checking statement
00823      *
00824      * @param string $tableName The database table name
00825      * @param array &$sql The query parts
00826      * @param array $storagePageIds list of storage page ids
00827      * @return void
00828      */
00829     protected function addPageIdStatement($tableName, array &$sql, array $storagePageIds) {
00830         if (empty($this->tableInformationCache[$tableName]['columnNames'])) {
00831             $this->tableInformationCache[$tableName]['columnNames'] = $this->databaseHandle->admin_get_fields($tableName);
00832         }
00833         if (is_array($GLOBALS['TCA'][$tableName]['ctrl']) && array_key_exists('pid', $this->tableInformationCache[$tableName]['columnNames'])) {
00834             $sql['additionalWhereClause'][] = $tableName . '.pid IN (' . implode(', ', $storagePageIds) . ')';
00835         }
00836     }
00837 
00838     /**
00839      * Transforms orderings into SQL.
00840      *
00841      * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
00842      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
00843      * @param array &$sql The query parts
00844      * @return void
00845      */
00846     protected function parseOrderings(array $orderings, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql) {
00847         foreach ($orderings as $propertyName => $order) {
00848             switch ($order) {
00849                 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_ORDER_ASCENDING: // Deprecated since Extbase 1.1
00850                 case Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING:
00851                     $order = 'ASC';
00852                     break;
00853                 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_ORDER_DESCENDING: // Deprecated since Extbase 1.1
00854                 case Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING:
00855                     $order = 'DESC';
00856                     break;
00857                 default:
00858                     throw new Tx_Extbase_Persistence_Exception_UnsupportedOrder('Unsupported order encountered.', 1242816074);
00859             }
00860             if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
00861                 $className = $source->getNodeTypeName();
00862                 $tableName = $this->dataMapper->convertClassNameToTableName($className);
00863                 while (strpos($propertyName, '.') !== FALSE) {
00864                     $this->addUnionStatement($className, $tableName, $propertyName, $sql);
00865                 }
00866             } elseif ($source instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
00867                 $tableName = $source->getLeft()->getSelectorName();
00868             }
00869             $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
00870             if (strlen($tableName) > 0) {
00871                 $sql['orderings'][] = $tableName . '.' . $columnName . ' ' . $order;
00872             } else {
00873                 $sql['orderings'][] = $columnName . ' ' . $order;
00874             }
00875         }
00876     }
00877 
00878     /**
00879      * Transforms limit and offset into SQL
00880      *
00881      * @param int $limit
00882      * @param int $offset
00883      * @param array &$sql
00884      * @return void
00885      */
00886     protected function parseLimitAndOffset($limit, $offset, array &$sql) {
00887         if ($limit !== NULL && $offset !== NULL) {
00888             $sql['limit'] = $offset . ', ' . $limit;
00889         } elseif ($limit !== NULL) {
00890             $sql['limit'] = $limit;
00891         }
00892     }
00893 
00894     /**
00895      * Transforms a Resource from a database query to an array of rows.
00896      *
00897      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source (selector od join)
00898      * @param resource $result The result
00899      * @return array The result as an array of rows (tuples)
00900      */
00901     protected function getRowsFromResult(Tx_Extbase_Persistence_QOM_SourceInterface $source, $result) {
00902         $rows = array();
00903         while ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
00904             if (is_array($row)) {
00905                 // TODO Check if this is necessary, maybe the last line is enough
00906                 $arrayKeys = range(0, count($row));
00907                 array_fill_keys($arrayKeys, $row);
00908                 $rows[] = $row;
00909             }
00910         }
00911         return $rows;
00912     }
00913 
00914     /**
00915      * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
00916      * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
00917      *
00918      * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source (selector od join)
00919      * @param array $row The row array (as reference)
00920      * @param string $languageUid The language id
00921      * @param string $workspaceUidUid The workspace id
00922      * @return void
00923      */
00924     protected function doLanguageAndWorkspaceOverlay(Tx_Extbase_Persistence_QOM_SourceInterface $source, array $rows, $languageUid = NULL, $workspaceUid = NULL) {
00925         $overlayedRows = array();
00926         foreach ($rows as $row) {
00927             if (!($this->pageSelectObject instanceof t3lib_pageSelect)) {
00928                 if (TYPO3_MODE == 'FE') {
00929                     if (is_object($GLOBALS['TSFE'])) {
00930                         $this->pageSelectObject = $GLOBALS['TSFE']->sys_page;
00931                     } else {
00932                         $this->pageSelectObject = t3lib_div::makeInstance('t3lib_pageSelect');
00933                     }
00934                 } else {
00935                     $this->pageSelectObject = t3lib_div::makeInstance( 't3lib_pageSelect' );
00936                 }
00937             }
00938             if (is_object($GLOBALS['TSFE'])) {
00939                 if ($languageUid === NULL) {
00940                     $languageUid = $GLOBALS['TSFE']->sys_language_uid;
00941                     $languageMode = $GLOBALS['TSFE']->sys_language_mode;
00942                 }
00943                 if ($workspaceUid !== NULL) {
00944                     $this->pageSelectObject->versioningWorkspaceId = $workspaceUid;
00945                 }
00946             } else {
00947                 if ($languageUid === NULL) {
00948                     $languageUid = intval(t3lib_div::_GP('L'));
00949                 }
00950                 if ($workspaceUid === NULL) {
00951                     $workspaceUid = $GLOBALS['BE_USER']->workspace;
00952                 }
00953                 $this->pageSelectObject->versioningWorkspaceId = $workspaceUid;
00954             }
00955             if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
00956                 $tableName = $source->getSelectorName();
00957             } elseif ($source instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
00958                 $tableName = $source->getRight()->getSelectorName();
00959             }
00960             $this->pageSelectObject->versionOL($tableName, $row, TRUE);
00961             if($tableName == 'pages') {
00962                 $row = $this->pageSelectObject->getPageOverlay($row, $languageUid);
00963             } elseif(isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField']) && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== '') {
00964                 if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1,0))) {
00965                     $overlayMode = ($languageMode === 'strict') ? 'hideNonTranslated' : '';
00966                     $row = $this->pageSelectObject->getRecordOverlay($tableName, $row, $languageUid, $overlayMode);
00967                 }
00968             }
00969             if ($row !== NULL && is_array($row)) {
00970                 $overlayedRows[] = $row;
00971             }
00972         }
00973         return $overlayedRows;
00974     }
00975 
00976     /**
00977      * Checks if there are SQL errors in the last query, and if yes, throw an exception.
00978      *
00979      * @return void
00980      * @param string $sql The SQL statement
00981      * @throws Tx_Extbase_Persistence_Storage_Exception_SqlError
00982      */
00983     protected function checkSqlErrors($sql='') {
00984         $error = $this->databaseHandle->sql_error();
00985         if ($error !== '') {
00986             $error .= $sql ? ': ' . $sql : '';
00987             throw new Tx_Extbase_Persistence_Storage_Exception_SqlError($error, 1247602160);
00988         }
00989     }
00990 
00991     /**
00992      * Clear the TYPO3 page cache for the given record.
00993      * If the record lies on a page, then we clear the cache of this page.
00994      * If the record has no PID column, we clear the cache of the current page as best-effort.
00995      *
00996      * Much of this functionality is taken from t3lib_tcemain::clear_cache() which unfortunately only works with logged-in BE user.
00997      *
00998      * @param $tableName Table name of the record
00999      * @param $uid UID of the record
01000      * @return void
01001      */
01002     protected function clearPageCache($tableName, $uid) {
01003         $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
01004         if (isset($frameworkConfiguration['persistence']['enableAutomaticCacheClearing']) && $frameworkConfiguration['persistence']['enableAutomaticCacheClearing'] === '1') {
01005         } else {
01006             // if disabled, return
01007             return;
01008         }
01009 
01010         $pageIdsToClear = array();
01011         $storagePage = NULL;
01012 
01013         $columns = $this->databaseHandle->admin_get_fields($tableName);
01014         if (array_key_exists('pid', $columns)) {
01015             $result = $this->databaseHandle->exec_SELECTquery('pid', $tableName, 'uid='.intval($uid));
01016             if ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
01017                 $storagePage = $row['pid'];
01018                 $pageIdsToClear[] = $storagePage;
01019             }
01020         } elseif (isset($GLOBALS['TSFE'])) {
01021             // No PID column - we can do a best-effort to clear the cache of the current page if in FE
01022             $storagePage = $GLOBALS['TSFE']->id;
01023             $pageIdsToClear[] = $storagePage;
01024         }
01025 
01026         if ($storagePage === NULL) {
01027             return;
01028         }
01029         if (!isset($this->pageTSConfigCache[$storagePage])) {
01030             $this->pageTSConfigCache[$storagePage] = t3lib_BEfunc::getPagesTSconfig($storagePage);
01031         }
01032         if (isset($this->pageTSConfigCache[$storagePage]['TCEMAIN.']['clearCacheCmd'])) {
01033             $clearCacheCommands = t3lib_div::trimExplode(',',strtolower($this->pageTSConfigCache[$storagePage]['TCEMAIN.']['clearCacheCmd']),1);
01034             $clearCacheCommands = array_unique($clearCacheCommands);
01035             foreach ($clearCacheCommands as $clearCacheCommand) {
01036                 if (t3lib_div::testInt($clearCacheCommand)) {
01037                     $pageIdsToClear[] = $clearCacheCommand;
01038                 }
01039             }
01040         }
01041 
01042         // TODO check if we can hand this over to the Dispatcher to clear the page only once, this will save around 10% time while inserting and updating
01043         Tx_Extbase_Utility_Cache::clearPageCache($pageIdsToClear);
01044     }
01045 }
01046 
01047 ?>