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\Core\Resource; 17 18use TYPO3\CMS\Core\Database\ConnectionPool; 19use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer; 20use TYPO3\CMS\Core\SingletonInterface; 21use TYPO3\CMS\Core\Utility\GeneralUtility; 22use TYPO3\CMS\Core\Utility\MathUtility; 23use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface; 24use TYPO3\CMS\Extbase\Persistence\RepositoryInterface; 25use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; 26 27/** 28 * Abstract repository implementing the basic repository methods 29 */ 30abstract class AbstractRepository implements RepositoryInterface, SingletonInterface 31{ 32 /** 33 * @var string 34 */ 35 protected $table = ''; 36 37 /** 38 * @var ResourceFactory 39 */ 40 protected $factory; 41 42 /** 43 * @var string 44 */ 45 protected $typeField = ''; 46 47 /** 48 * @var string 49 */ 50 protected $type = ''; 51 52 /** 53 * The main object type of this class 54 * 55 * @var string 56 */ 57 protected $objectType; 58 59 /** 60 * Creates this object. 61 */ 62 public function __construct() 63 { 64 $this->factory = GeneralUtility::makeInstance(ResourceFactory::class); 65 } 66 67 /** 68 * Adds an object to this repository. 69 * 70 * @param object $object The object to add 71 */ 72 public function add($object) 73 { 74 } 75 76 /** 77 * Removes an object from this repository. 78 * 79 * @param object $object The object to remove 80 */ 81 public function remove($object) 82 { 83 } 84 85 /** 86 * Replaces an object by another. 87 * 88 * @param object $existingObject The existing object 89 * @param object $newObject The new object 90 */ 91 public function replace($existingObject, $newObject) 92 { 93 } 94 95 /** 96 * Replaces an existing object with the same identifier by the given object 97 * 98 * @param object $modifiedObject The modified object 99 */ 100 public function update($modifiedObject) 101 { 102 } 103 104 /** 105 * Returns all objects of this repository add()ed but not yet persisted to 106 * the storage layer. 107 * 108 * @return array An array of objects 109 * @internal 110 */ 111 public function getAddedObjects() 112 { 113 return []; 114 } 115 116 /** 117 * Returns an array with objects remove()d from the repository that 118 * had been persisted to the storage layer before. 119 * 120 * @return array 121 * @internal 122 */ 123 public function getRemovedObjects() 124 { 125 return []; 126 } 127 128 /** 129 * Returns all objects of this repository. 130 * 131 * @return array An array of objects, empty if no objects found 132 */ 133 public function findAll() 134 { 135 $items = []; 136 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table); 137 if ($this->getEnvironmentMode() === 'FE') { 138 $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); 139 } 140 $queryBuilder 141 ->select('*') 142 ->from($this->table); 143 144 if (!empty($this->type)) { 145 $queryBuilder->where( 146 $queryBuilder->expr()->eq( 147 $this->typeField, 148 $queryBuilder->createNamedParameter($this->type, \PDO::PARAM_STR) 149 ) 150 ); 151 } 152 $result = $queryBuilder->executeQuery(); 153 154 // fetch all records and create objects out of them 155 while ($row = $result->fetchAssociative()) { 156 $items[] = $this->createDomainObject($row); 157 } 158 return $items; 159 } 160 161 /** 162 * Creates an object managed by this repository. 163 * 164 * @abstract 165 * @param array $databaseRow 166 * @return object 167 */ 168 abstract protected function createDomainObject(array $databaseRow); 169 170 /** 171 * Returns the total number objects of this repository. 172 * 173 * @return int The object count 174 */ 175 public function countAll() 176 { 177 return 0; 178 } 179 180 /** 181 * Removes all objects of this repository as if remove() was called for 182 * all of them. 183 */ 184 public function removeAll() 185 { 186 } 187 188 /** 189 * Finds an object matching the given identifier. 190 * 191 * @param int $uid The identifier of the object to find 192 * 193 * @throws \RuntimeException 194 * @throws \InvalidArgumentException 195 * @return object The matching object 196 */ 197 public function findByUid($uid) 198 { 199 if (!MathUtility::canBeInterpretedAsInteger($uid)) { 200 throw new \InvalidArgumentException('The UID has to be an integer. UID given: "' . $uid . '"', 1316779798); 201 } 202 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table); 203 if ($this->getEnvironmentMode() === 'FE') { 204 $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); 205 } 206 $row = $queryBuilder 207 ->select('*') 208 ->from($this->table) 209 ->where( 210 $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)) 211 ) 212 ->executeQuery() 213 ->fetchAssociative(); 214 if (!is_array($row)) { 215 throw new \RuntimeException('Could not find row with UID "' . $uid . '" in table "' . $this->table . '"', 1314354065); 216 } 217 return $this->createDomainObject($row); 218 } 219 220 /** 221 * Sets the property names to order the result by per default. 222 * Expected like this: 223 * array( 224 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING, 225 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING 226 * ) 227 * 228 * @param array $defaultOrderings The property names to order by 229 * 230 * @throws \BadMethodCallException 231 */ 232 public function setDefaultOrderings(array $defaultOrderings) 233 { 234 throw new \BadMethodCallException('Repository does not support the setDefaultOrderings() method.', 1313185906); 235 } 236 237 /** 238 * Sets the default query settings to be used in this repository 239 * 240 * @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $defaultQuerySettings The query settings to be used by default 241 * 242 * @throws \BadMethodCallException 243 */ 244 public function setDefaultQuerySettings(QuerySettingsInterface $defaultQuerySettings) 245 { 246 throw new \BadMethodCallException('Repository does not support the setDefaultQuerySettings() method.', 1313185907); 247 } 248 249 /** 250 * Returns a query for objects of this repository 251 * 252 * @throws \BadMethodCallException 253 * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface 254 */ 255 public function createQuery() 256 { 257 throw new \BadMethodCallException('Repository does not support the createQuery() method.', 1313185908); 258 } 259 260 /** 261 * Finds an object matching the given identifier. 262 * 263 * @param mixed $identifier The identifier of the object to find 264 * @return object|null The matching object if found, otherwise NULL 265 */ 266 public function findByIdentifier($identifier) 267 { 268 return $this->findByUid($identifier); 269 } 270 271 /** 272 * Magic call method for repository methods. 273 * 274 * @param string $method Name of the method 275 * @param array $arguments The arguments 276 * 277 * @throws \BadMethodCallException 278 * @internal 279 */ 280 public function __call($method, $arguments) 281 { 282 throw new \BadMethodCallException('Repository method "' . $method . '" is not implemented.', 1378918410); 283 } 284 285 /** 286 * Returns the object type this repository is managing. 287 * 288 * @return string 289 */ 290 public function getEntityClassName() 291 { 292 return $this->objectType; 293 } 294 295 /** 296 * Function to return the current application type based on $GLOBALS['TSFE']. 297 * This function can be mocked in unit tests to be able to test frontend behaviour. 298 * 299 * @return string 300 */ 301 protected function getEnvironmentMode() 302 { 303 return ($GLOBALS['TSFE'] ?? null) instanceof TypoScriptFrontendController ? 'FE' : 'BE'; 304 } 305} 306