1<?php 2 3/* 4 * This file is part of the TYPO3 CMS project. 5 * 6 * It is free software; you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License, either version 2 8 * of the License, or any later version. 9 * 10 * For the full copyright and license information, please read the 11 * LICENSE.txt file that was distributed with this source code. 12 * 13 * The TYPO3 project - inspiring people to share! 14 */ 15 16namespace TYPO3\CMS\Extbase\Persistence\Generic; 17 18use TYPO3\CMS\Core\Utility\GeneralUtility; 19use TYPO3\CMS\Extbase\Object\ObjectManager; 20use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; 21use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper; 22use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface; 23use TYPO3\CMS\Extbase\Persistence\QueryInterface; 24use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; 25 26/** 27 * A lazy result list that is returned by Query::execute() 28 */ 29class QueryResult implements QueryResultInterface 30{ 31 /** 32 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper 33 */ 34 protected $dataMapper; 35 36 /** 37 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface 38 */ 39 protected $persistenceManager; 40 41 /** 42 * @var int|null 43 */ 44 protected $numberOfResults; 45 46 /** 47 * @var \TYPO3\CMS\Extbase\Persistence\QueryInterface 48 */ 49 protected $query; 50 51 /** 52 * @var array 53 */ 54 protected $queryResult; 55 56 /** 57 * @var ObjectManagerInterface 58 */ 59 protected $objectManager; 60 61 /** 62 * @param ObjectManagerInterface $objectManager 63 */ 64 public function injectObjectManager(ObjectManagerInterface $objectManager) 65 { 66 $this->objectManager = $objectManager; 67 } 68 69 /** 70 * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager 71 */ 72 public function injectPersistenceManager(PersistenceManagerInterface $persistenceManager) 73 { 74 $this->persistenceManager = $persistenceManager; 75 } 76 77 /** 78 * Constructor 79 * 80 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query 81 */ 82 public function __construct(QueryInterface $query) 83 { 84 $this->query = $query; 85 } 86 87 /** 88 * Object initialization called when object is created with ObjectManager, after constructor 89 */ 90 public function initializeObject() 91 { 92 $this->dataMapper = $this->objectManager->get(DataMapper::class); 93 $this->dataMapper->setQuery($this->query); 94 } 95 96 /** 97 * Loads the objects this QueryResult is supposed to hold 98 */ 99 protected function initialize() 100 { 101 if (!is_array($this->queryResult)) { 102 $this->queryResult = $this->dataMapper->map($this->query->getType(), $this->persistenceManager->getObjectDataByQuery($this->query)); 103 } 104 } 105 106 /** 107 * Returns a clone of the query object 108 * 109 * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface 110 */ 111 public function getQuery() 112 { 113 return clone $this->query; 114 } 115 116 /** 117 * Returns the first object in the result set 118 * 119 * @return object 120 */ 121 public function getFirst() 122 { 123 if (is_array($this->queryResult)) { 124 $queryResult = $this->queryResult; 125 reset($queryResult); 126 } else { 127 $query = $this->getQuery(); 128 $query->setLimit(1); 129 $queryResult = $this->dataMapper->map($query->getType(), $this->persistenceManager->getObjectDataByQuery($query)); 130 } 131 $firstResult = current($queryResult); 132 if ($firstResult === false) { 133 $firstResult = null; 134 } 135 return $firstResult; 136 } 137 138 /** 139 * Returns the number of objects in the result 140 * 141 * @return int The number of matching objects 142 */ 143 public function count() 144 { 145 if ($this->numberOfResults === null) { 146 if (is_array($this->queryResult)) { 147 $this->numberOfResults = count($this->queryResult); 148 } else { 149 $this->numberOfResults = $this->persistenceManager->getObjectCountByQuery($this->query); 150 } 151 } 152 return $this->numberOfResults; 153 } 154 155 /** 156 * Returns an array with the objects in the result set 157 * 158 * @return array 159 */ 160 public function toArray() 161 { 162 $this->initialize(); 163 return iterator_to_array($this); 164 } 165 166 /** 167 * This method is needed to implement the ArrayAccess interface, 168 * but it isn't very useful as the offset has to be an integer 169 * 170 * @param mixed $offset 171 * @return bool 172 * @see ArrayAccess::offsetExists() 173 */ 174 public function offsetExists($offset) 175 { 176 $this->initialize(); 177 return isset($this->queryResult[$offset]); 178 } 179 180 /** 181 * @param mixed $offset 182 * @return mixed 183 * @see ArrayAccess::offsetGet() 184 */ 185 public function offsetGet($offset) 186 { 187 $this->initialize(); 188 return $this->queryResult[$offset] ?? null; 189 } 190 191 /** 192 * This method has no effect on the persisted objects but only on the result set 193 * 194 * @param mixed $offset 195 * @param mixed $value 196 * @see ArrayAccess::offsetSet() 197 */ 198 public function offsetSet($offset, $value) 199 { 200 $this->initialize(); 201 $this->numberOfResults = null; 202 $this->queryResult[$offset] = $value; 203 } 204 205 /** 206 * This method has no effect on the persisted objects but only on the result set 207 * 208 * @param mixed $offset 209 * @see ArrayAccess::offsetUnset() 210 */ 211 public function offsetUnset($offset) 212 { 213 $this->initialize(); 214 $this->numberOfResults = null; 215 unset($this->queryResult[$offset]); 216 } 217 218 /** 219 * @return mixed 220 * @see Iterator::current() 221 */ 222 public function current() 223 { 224 $this->initialize(); 225 return current($this->queryResult); 226 } 227 228 /** 229 * @return mixed 230 * @see Iterator::key() 231 */ 232 public function key() 233 { 234 $this->initialize(); 235 return key($this->queryResult); 236 } 237 238 /** 239 * @see Iterator::next() 240 */ 241 public function next() 242 { 243 $this->initialize(); 244 next($this->queryResult); 245 } 246 247 /** 248 * @see Iterator::rewind() 249 */ 250 public function rewind() 251 { 252 $this->initialize(); 253 reset($this->queryResult); 254 } 255 256 /** 257 * @return bool 258 * @see Iterator::valid() 259 */ 260 public function valid() 261 { 262 $this->initialize(); 263 return current($this->queryResult) !== false; 264 } 265 266 /** 267 * Ensures that the objectManager, persistenceManager and dataMapper are back when loading the QueryResult 268 * from the cache 269 * @internal only to be used within Extbase, not part of TYPO3 Core API. 270 */ 271 public function __wakeup() 272 { 273 $objectManager = GeneralUtility::makeInstance(ObjectManager::class); 274 $this->persistenceManager = $objectManager->get(PersistenceManagerInterface::class); 275 $this->dataMapper = $objectManager->get(DataMapper::class); 276 } 277 278 /** 279 * @return array 280 * @internal only to be used within Extbase, not part of TYPO3 Core API. 281 */ 282 public function __sleep() 283 { 284 return ['query']; 285 } 286} 287