Commit 35ce4341 authored by Bastian Waidelich's avatar Bastian Waidelich
Browse files

[+BUGFIX] Extbase: Some smaller tweaks and fixes

[!!!][+FEATURE] Extbase (Persistence): Backport QueryResult from FLOW3

Now Query::execute() returns an instance of QueryResultInterface that allows it to modify the query before actually accessing the records that it retrieves. This is required for the upcoming "Fluid widgets" backport (#10568).
NOTE: This change is not backwards compatible, if you work with PHPs array_* functions on the query result. To work around this issue, you'll have to convert the query result to an array before by calling the QueryResult::toArray() method. We're planning to add a compatibility mode, but that's not yet implemented.

Resolves: #10566
parent 3a19439c
......@@ -399,12 +399,10 @@ class Tx_Extbase_MVC_Controller_Argument {
$query = $this->queryFactory->create($this->dataType);
$query->getQuerySettings()->setRespectSysLanguage(FALSE);
$query->getQuerySettings()->setRespectStoragePage(FALSE);
$result = $query->matching($query->equals('uid', $uid))->execute();
$object = NULL;
if (count($result) > 0) {
$object = current($result);
}
return $object;
return $query->matching(
$query->equals('uid', $uid))
->execute()
->getFirst();
}
/**
......
......@@ -159,7 +159,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
$this->queryFactory = $queryFactory;
}
/**
* Injects the QueryObjectModelFactory
*
......@@ -196,7 +196,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
public function getQomFactory() {
return $this->qomFactory;
}
/**
* Returns the current identityMap
*
......@@ -265,12 +265,10 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
return $this->identityMap->getObjectByIdentifier($identifier, $className);
} else {
$query = $this->queryFactory->create($className);
$result = $query->matching($query->withUid($identifier))->execute();
$object = NULL;
if (count($result) > 0) {
$object = current($result);
}
return $object;
return $query->matching(
$query->withUid($identifier))
->execute()
->getFirst();
}
}
......@@ -374,11 +372,11 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$className = get_class($object);
$dataMap = $this->dataMapper->getDataMap($className);
$classSchema = $this->reflectionService->getClassSchema($className);
$properties = $object->_getProperties();
foreach ($properties as $propertyName => $propertyValue) {
if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) continue;
$columnMap = $dataMap->getColumnMap($propertyName);
$propertyMetaData = $classSchema->getProperty($propertyName);
$propertyType = $propertyMetaData['type'];
......@@ -413,7 +411,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
if (count($row) > 0) {
$this->updateObject($object, $row);
}
if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity) {
$object->_memorizeCleanState();
}
......@@ -421,7 +419,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
foreach ($queue as $queuedObject) {
$this->persistObject($queuedObject);
}
}
/**
......@@ -440,13 +438,13 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
if (is_object($value)) {
if (!($value instanceof $expectedType)) {
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . get_class($value), 1244465558);
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . get_class($value), 1244465558);
}
} elseif ($expectedType !== gettype($value)) {
} elseif ($expectedType !== gettype($value)) {
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . gettype($value), 1244465558);
}
}
/**
* Checks, if the property value is lazy loaded and was not initialized
*
......@@ -462,7 +460,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
return FALSE;
}
/**
* Persists the given value object.
*
......@@ -492,7 +490,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$this->insertObject($object, $row);
}
}
/**
* Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
*
......@@ -501,7 +499,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
protected function getUidOfAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object) {
return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
}
/**
* Persists a an object storage. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
* gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
......@@ -509,13 +507,13 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object
* @param string $propertyName The name of the property the related objects are stored in
* @param mixed $propertyValue The property value
* @param mixed $propertyValue The property value
* @return void
*/
protected function persistObjectStorage(Tx_Extbase_Persistence_ObjectStorage $objectStorage, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $propertyName, array &$queue, array &$row) {
$className = get_class($parentObject);
$columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
$columnName = $columnMap->getColumnName();
$columnName = $columnMap->getColumnName();
$propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
......@@ -544,14 +542,14 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
$sortingPosition++;
}
if ($columnMap->getParentKeyFieldName() === NULL) {
$row[$columnMap->getColumnName()] = implode(',', $currentUids);
} else {
$row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
}
}
/**
* Returns the current field value of the given object property from the storage backend.
*
......@@ -564,12 +562,14 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
$query = $this->queryFactory->create($className);
$query->getQuerySettings()->setReturnRawQueryResult(TRUE);
$rows = $query->matching($query->withUid($object->getUid()))->execute();
$currentRow = current($rows);
$currentRow = $query->matching(
$query->withUid($object->getUid()))
->execute()
->getFirst();
$fieldValue = $currentRow[$columnMap->getColumnName()];
return $fieldValue;
}
/**
* Returns the removed objects determined by a comparison of the clean property value
* with the actual property value.
......@@ -591,13 +591,13 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
return $removedObjects;
}
/**
* Updates the fields defining the relation between the object and the parent object.
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object
* @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
* @param string $parentPropertyName
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object
* @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
* @param string $parentPropertyName
* @return void
*/
protected function attachObjectToParentObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0) {
......@@ -619,18 +619,18 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
if (count($row) > 0) {
$this->updateObject($object, $row);
}
}
} elseif ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
$this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
}
}
/**
* Updates the fields defining the relation between the object and the parent object.
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object
* @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
* @param string $parentPropertyName
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object
* @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
* @param string $parentPropertyName
* @return void
*/
protected function detachObjectFromParentObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject, $parentPropertyName) {
......@@ -657,7 +657,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
}
}
/**
* Inserts an object in the storage backend
*
......@@ -680,7 +680,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
$this->identityMap->registerObject($object, $uid);
}
/**
* Inserts mm-relation into a relation table
*
......@@ -706,7 +706,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
if ($columnMap->getRelationTablePageIdColumnName() !== NULL) {
$row[$columnMap->getRelationTablePageIdColumnName()] = $this->determineStoragePageIdForNewRecord();
}
$relationTableInsertFields = $columnMap->getRelationTableInsertFields();
if (count($relationTableInsertFields)) {
foreach($relationTableInsertFields as $insertField => $insertValue) {
......@@ -798,7 +798,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
}
return $res;
}
/**
* Returns a table row to be inserted or updated in the database
*
......@@ -866,9 +866,9 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$this->removeRelatedObjects($object);
if ($this->extbaseSettings['persistence']['updateReferenceIndex'] === '1') {
$this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
}
}
}
/**
* Remove related objects
*
......@@ -879,7 +879,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$className = get_class($object);
$dataMap = $this->dataMapper->getDataMap($className);
$classSchema = $this->reflectionService->getClassSchema($className);
$properties = $object->_getProperties();
foreach ($properties as $propertyName => $propertyValue) {
$columnMap = $dataMap->getColumnMap($propertyName);
......@@ -889,7 +889,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
foreach ($propertyValue as $containedObject) {
$this->removeObject($containedObject);
}
} elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
} elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
$this->removeObject($propertyValue);
}
}
......@@ -917,7 +917,7 @@ class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendIn
$storagePidList = t3lib_div::intExplode(',', $extbaseSettings['persistence']['storagePid']);
return (int) $storagePidList[0];
}
/**
* Returns a plain value, i.e. objects are flattened out if possible.
*
......
......@@ -57,5 +57,31 @@ interface Tx_Extbase_Persistence_ManagerInterface {
* @api
*/
public function persistAll();
/**
* Returns the number of records matching the query.
*
* @param Tx_Extbase_Persistence_QueryInterface $query
* @return integer
* @api
*/
public function getObjectCountByQuery(Tx_Extbase_Persistence_QueryInterface $query);
/**
* Returns the object data matching the $query.
*
* @param Tx_Extbase_Persistence_QueryInterface $query
* @return array
* @api
*/
public function getObjectDataByQuery(Tx_Extbase_Persistence_QueryInterface $query);
/**
* Registers a repository
*
* @param string $className The class name of the repository to be reigistered
* @return void
*/
public function registerRepositoryClassName($className);
}
?>
\ No newline at end of file
......@@ -41,13 +41,18 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
*/
protected $type;
/**
* @var Tx_Extbase_Object_ObjectManagerInterface
*/
protected $objectManager;
/**
* @var Tx_Extbase_Persistence_DataMapper
*/
protected $dataMapper;
/**
* @var Tx_Extbase_Persistence_Manager
* @var Tx_Extbase_Persistence_ManagerInterface
*/
protected $persistenceManager;
......@@ -102,6 +107,14 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
$this->type = $type;
}
/**
* @param Tx_Extbase_Object_ObjectManagerInterface $objectManager
* @return void
*/
public function injectObjectManager(Tx_Extbase_Object_ObjectManagerInterface $objectManager) {
$this->objectManager = $objectManager;
}
/**
* Injects the persistence manager, used to fetch the CR session
*
......@@ -146,7 +159,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
/**
* Returns the Query Settings.
*
*
* @return Tx_Extbase_Persistence_QuerySettingsInterface $querySettings The Query Settings
* @api This method is not part of FLOW3 API
*/
......@@ -173,7 +186,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
public function setSource(Tx_Extbase_Persistence_QOM_SourceInterface $source) {
$this->source = $source;
}
/**
* Returns the selectorn name or an empty string, if the source is not a selector
* // TODO This has to be checked at another place
......@@ -202,28 +215,29 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
/**
* Executes the query against the database and returns the result
*
* @return array<object> The query result as an array of objects
* @return Tx_Extbase_Persistence_QueryResultInterface|array The query result object or an array if $this->getQuerySettings()->getReturnRawQueryResult() is TRUE
* @api
*/
public function execute() {
$rows = $this->persistenceManager->getObjectDataByQuery($this);
if ($this->getQuerySettings()->getReturnRawQueryResult() === TRUE) {
return $rows;
return $this->persistenceManager->getObjectDataByQuery($this);
} else {
return $this->dataMapper->map($this->getType(), $rows);
return $this->objectManager->create('Tx_Extbase_Persistence_QueryResultInterface', $this);
}
}
/**
* Executes the number of matching objects for the query
*
* @return integer The number of matching objects
* @deprecated since Extbase 1.3.0; was removed in FLOW3; will be removed in Extbase 1.4.0; use Query::execute()::count() instead
* @api
*/
public function count() {
t3lib_div::logDeprecatedFunction();
return $this->persistenceManager->getObjectCountByQuery($this);
}
/**
* Sets the property names to order the result by. Expected like this:
* array(
......@@ -240,7 +254,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
$this->orderings = $orderings;
return $this;
}
/**
* Returns the property names to order the result by. Like this:
* array(
......@@ -302,7 +316,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
public function getOffset() {
return $this->offset;
}
/**
* The constraint used to limit the result set. Returns $this to allow
* for chaining (fluid interface)
......@@ -328,7 +342,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
$this->statement = $this->qomFactory->statement($statement, $parameters);
return $this;
}
/**
* Returns the statement of this query.
*
......@@ -337,7 +351,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
public function getStatement() {
return $this->statement;
}
/**
* Gets the constraint for this query.
*
......@@ -375,7 +389,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
}
return $resultingConstraint;
}
/**
* Performs a logical disjunction of the two given constraints
*
......@@ -474,7 +488,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
$operand
);
}
/**
* Returns a "contains" criterion used for matching objects against a query.
* It matches if the multivalued property contains the given operand.
......@@ -505,7 +519,7 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
if (!is_array($operand) && (!$operand instanceof ArrayAccess) && (!$operand instanceof Traversable)) {
throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('The "in" operator must be given a mutlivalued operand (array, ArrayAccess, Traversable).', 1264678095);
}
return $this->qomFactory->comparison(
$this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
Tx_Extbase_Persistence_QueryInterface::OPERATOR_IN,
......@@ -576,6 +590,6 @@ class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterf
$operand
);
}
}
?>
\ No newline at end of file
......@@ -113,7 +113,7 @@ interface Tx_Extbase_Persistence_QueryInterface {
/**
* Executes the query against the backend and returns the result
*
* @return array<object> The query result as an array of objects
* @return Tx_Extbase_Persistence_QueryResultInterface|array The query result object or an array if $this->getQuerySettings()->getReturnRawQueryResult() is TRUE
* @api
*/
public function execute();
......@@ -288,5 +288,30 @@ interface Tx_Extbase_Persistence_QueryInterface {
*/
public function greaterThanOrEqual($propertyName, $operand);
/**
* Returns the type this query cares for.
*
* @return string
* @api
*/
public function getType();
/**
* Sets the Query Settings. These Query settings must match the settings expected by
* the specific Storage Backend.
*
* @param Tx_Extbase_Persistence_QuerySettingsInterface $querySettings The Query Settings
* @return void
* @api This method is not part of FLOW3 API
*/
public function setQuerySettings(Tx_Extbase_Persistence_QuerySettingsInterface $querySettings);
/**
* Returns the Query Settings.
*
* @return Tx_Extbase_Persistence_QuerySettingsInterface $querySettings The Query Settings
* @api This method is not part of FLOW3 API
*/
public function getQuerySettings();
}
?>
\ No newline at end of file
<?php
/***************************************************************
* Copyright notice
*
* (c) 2010 Bastian Waidelich <bastian@typo3.org>
* All rights reserved
*
* This class is a backport of the corresponding class of FLOW3.
* All credits go to the v5 team.
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
* A lazy result list that is returned by Query::execute()
*
* @package Extbase
* @subpackage Persistence
* @scope prototype
* @api
*/
class Tx_Extbase_Persistence_QueryResult implements Tx_Extbase_Persistence_QueryResultInterface {
/**
* @var Tx_Extbase_Persistence_Mapper_DataMapper
*/
protected $dataMapper;
/**
* @var Tx_Extbase_Persistence_ManagerInterface
*/
protected $persistenceManager;
/**
* @var Tx_Extbase_Persistence_QueryInterface
*/
protected $query;
/**
* @var array
* @transient
*/
protected $queryResult;
/**
* Constructor
*
* @param Tx_Extbase_Persistence_QueryInterface $query
*/
public function __construct(Tx_Extbase_Persistence_QueryInterface $query) {
$this->query = $query;
}
/**
* Injects the DataMapper to map records to objects
*
* @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
* @return void
*/
public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper) {
$this->dataMapper = $dataMapper;
}
/**
* Injects the persistence manager
*
* @param Tx_Extbase_Persistence_ManagerInterface $persistenceManager
* @return void
*/
public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
$this->persistenceManager = $persistenceManager;
}
/**
* Loads the objects this QueryResult is supposed to hold
*
* @return void
*/
protected function initialize() {
if (!is_array($this->queryResult)) {
$this->queryResult = $this->dataMapper->map($this->query->getType(), $this->persistenceManager->getObjectDataByQuery($this->query));
}
}
/**
* Returns a clone of the query object
*
* @return Tx_Extbase_Persistence_QueryInterface
* @api