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