66750e674075ce3c76be12e133feb590cb06656b
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Category / Collection / CategoryCollection.php
1 <?php
2 namespace TYPO3\CMS\Core\Category\Collection;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Collection\AbstractRecordCollection;
18 use TYPO3\CMS\Core\Collection\CollectionInterface;
19 use TYPO3\CMS\Core\Collection\EditableCollectionInterface;
20 use TYPO3\CMS\Core\Database\ConnectionPool;
21 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
22 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25 /**
26 * Category Collection to handle records attached to a category
27 */
28 class CategoryCollection extends AbstractRecordCollection implements EditableCollectionInterface
29 {
30 /**
31 * The table name collections are stored to
32 *
33 * @var string
34 */
35 protected static $storageTableName = 'sys_category';
36
37 /**
38 * Name of the categories-relation field (used in the MM_match_fields/fieldname property of the TCA)
39 *
40 * @var string
41 */
42 protected $relationFieldName = 'categories';
43
44 /**
45 * Creates this object.
46 *
47 * @param string $tableName Name of the table to be working on
48 * @param string $fieldName Name of the field where the categories relations are defined
49 * @throws \RuntimeException
50 */
51 public function __construct($tableName = null, $fieldName = null)
52 {
53 parent::__construct();
54 if (!empty($tableName)) {
55 $this->setItemTableName($tableName);
56 } elseif (empty($this->itemTableName)) {
57 throw new \RuntimeException(self::class . ' needs a valid itemTableName.', 1341826168);
58 }
59 if (!empty($fieldName)) {
60 $this->setRelationFieldName($fieldName);
61 }
62 }
63
64 /**
65 * Creates a new collection objects and reconstitutes the
66 * given database record to the new object.
67 *
68 * @param array $collectionRecord Database record
69 * @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
70 * @return CategoryCollection
71 */
72 public static function create(array $collectionRecord, $fillItems = false)
73 {
74 /** @var $collection CategoryCollection */
75 $collection = GeneralUtility::makeInstance(
76 self::class,
77 $collectionRecord['table_name'],
78 $collectionRecord['field_name']
79 );
80 $collection->fromArray($collectionRecord);
81 if ($fillItems) {
82 $collection->loadContents();
83 }
84 return $collection;
85 }
86
87 /**
88 * Loads the collections with the given id from persistence
89 * For memory reasons, per default only f.e. title, database-table,
90 * identifier (what ever static data is defined) is loaded.
91 * Entries can be load on first access.
92 *
93 * @param int $id Id of database record to be loaded
94 * @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
95 * @param string $tableName Name of table from which entries should be loaded
96 * @param string $fieldName Name of the categories relation field
97 * @return CollectionInterface
98 */
99 public static function load($id, $fillItems = false, $tableName = '', $fieldName = '')
100 {
101 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
102 ->getQueryBuilderForTable(static::$storageTableName);
103
104 $queryBuilder->getRestrictions()
105 ->removeAll()
106 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
107
108 $collectionRecord = $queryBuilder->select('*')
109 ->from(static::$storageTableName)
110 ->where(
111 $queryBuilder->expr()->eq('uid', (int)$id)
112 )
113 ->setMaxResults(1)
114 ->execute()
115 ->fetch();
116
117 $collectionRecord['table_name'] = $tableName;
118 $collectionRecord['field_name'] = $fieldName;
119
120 return self::create($collectionRecord, $fillItems);
121 }
122
123 /**
124 * Selects the collected records in this collection, by
125 * looking up the MM relations of this record to the
126 * table name defined in the local field 'table_name'.
127 *
128 * @return QueryBuilder
129 */
130 protected function getCollectedRecordsQueryBuilder()
131 {
132 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
133 ->getQueryBuilderForTable(static::$storageTableName);
134 $queryBuilder->getRestrictions()->removeAll();
135
136 $queryBuilder->select($this->getItemTableName() . '.*')
137 ->from(static::$storageTableName)
138 ->join(
139 static::$storageTableName,
140 'sys_category_record_mm',
141 'sys_category_record_mm',
142 $queryBuilder->expr()->eq(
143 'sys_category_record_mm.uid_local',
144 $queryBuilder->quoteIdentifier(static::$storageTableName . '.uid')
145 )
146 )
147 ->join(
148 'sys_category_record_mm',
149 $this->getItemTableName(),
150 $this->getItemTableName(),
151 $queryBuilder->expr()->eq(
152 'sys_category_record_mm.uid_foreign',
153 $queryBuilder->quoteIdentifier($this->getItemTableName() . '.uid')
154 )
155 )
156 ->where(
157 $queryBuilder->expr()->eq(static::$storageTableName . '.uid', (int)$this->getIdentifier()),
158 $queryBuilder->expr()->eq(
159 'sys_category_record_mm.tablenames',
160 $queryBuilder->createNamedParameter($this->getItemTableName())
161 ),
162 $queryBuilder->expr()->eq(
163 'sys_category_record_mm.fieldname',
164 $queryBuilder->createNamedParameter($this->getRelationFieldName())
165 )
166 );
167
168 return $queryBuilder;
169 }
170
171 /**
172 * Gets the collected records in this collection, by
173 * using <getCollectedRecordsQueryBuilder>.
174 *
175 * @return array
176 */
177 protected function getCollectedRecords()
178 {
179 $relatedRecords = [];
180
181 $queryBuilder = $this->getCollectedRecordsQueryBuilder();
182 $result = $queryBuilder->execute();
183
184 while ($record = $result->fetch()) {
185 $relatedRecords[] = $record;
186 }
187
188 return $relatedRecords;
189 }
190
191 /**
192 * Populates the content-entries of the storage
193 * Queries the underlying storage for entries of the collection
194 * and adds them to the collection data.
195 * If the content entries of the storage had not been loaded on creation
196 * ($fillItems = false) this function is to be used for loading the contents
197 * afterwards.
198 *
199 * @return void
200 */
201 public function loadContents()
202 {
203 $entries = $this->getCollectedRecords();
204 $this->removeAll();
205 foreach ($entries as $entry) {
206 $this->add($entry);
207 }
208 }
209
210 /**
211 * Returns an array of the persistable properties and contents
212 * which are processable by TCEmain.
213 * for internal usage in persist only.
214 *
215 * @return array
216 */
217 protected function getPersistableDataArray()
218 {
219 return [
220 'title' => $this->getTitle(),
221 'description' => $this->getDescription(),
222 'items' => $this->getItemUidList(true)
223 ];
224 }
225
226 /**
227 * Adds on entry to the collection
228 *
229 * @param mixed $data
230 * @return void
231 */
232 public function add($data)
233 {
234 $this->storage->push($data);
235 }
236
237 /**
238 * Adds a set of entries to the collection
239 *
240 * @param CollectionInterface $other
241 * @return void
242 */
243 public function addAll(CollectionInterface $other)
244 {
245 foreach ($other as $value) {
246 $this->add($value);
247 }
248 }
249
250 /**
251 * Removes the given entry from collection
252 * Note: not the given "index"
253 *
254 * @param mixed $data
255 * @return void
256 */
257 public function remove($data)
258 {
259 $offset = 0;
260 foreach ($this->storage as $value) {
261 if ($value == $data) {
262 break;
263 }
264 $offset++;
265 }
266 $this->storage->offsetUnset($offset);
267 }
268
269 /**
270 * Removes all entries from the collection
271 * collection will be empty afterwards
272 *
273 * @return void
274 */
275 public function removeAll()
276 {
277 $this->storage = new \SplDoublyLinkedList();
278 }
279
280 /**
281 * Gets the current available items.
282 *
283 * @return array
284 */
285 public function getItems()
286 {
287 $itemArray = [];
288 /** @var $item \TYPO3\CMS\Core\Resource\File */
289 foreach ($this->storage as $item) {
290 $itemArray[] = $item;
291 }
292 return $itemArray;
293 }
294
295 /**
296 * Sets the name of the categories relation field
297 *
298 * @param string $field
299 */
300 public function setRelationFieldName($field)
301 {
302 $this->relationFieldName = $field;
303 }
304
305 /**
306 * Gets the name of the categories relation field
307 *
308 * @return string
309 */
310 public function getRelationFieldName()
311 {
312 return $this->relationFieldName;
313 }
314
315 /**
316 * Getter for the storage table name
317 *
318 * @return string
319 */
320 public static function getStorageTableName()
321 {
322 return self::$storageTableName;
323 }
324
325 /**
326 * Getter for the storage items field
327 *
328 * @return string
329 */
330 public static function getStorageItemsField()
331 {
332 return self::$storageItemsField;
333 }
334
335 /**
336 * Gets the database object.
337 *
338 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
339 */
340 protected static function getDatabaseConnection()
341 {
342 return $GLOBALS['TYPO3_DB'];
343 }
344 }