Completed
Push — master ( d5827e...097023 )
by
unknown
15:13
created

getCollectionDatabaseTable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0
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
16
namespace TYPO3\CMS\Core\Collection;
17
18
use TYPO3\CMS\Core\Database\ConnectionPool;
19
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
20
use TYPO3\CMS\Core\DataHandling\DataHandler;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23
/**
24
 * Abstract implementation of a RecordCollection
25
 *
26
 * RecordCollection is a collections of TCA-Records.
27
 * The collection is meant to be stored in TCA-table sys_file_collections and is manageable
28
 * via FormEngine.
29
 *
30
 * A RecordCollection might be used to group a set of records (e.g. news, images, contentElements)
31
 * for output in frontend
32
 *
33
 * The AbstractRecordCollection uses SplDoublyLinkedList for internal storage
34
 */
35
abstract class AbstractRecordCollection implements RecordCollectionInterface, PersistableCollectionInterface, SortableCollectionInterface
36
{
37
    /**
38
     * The table name collections are stored to
39
     *
40
     * @var string
41
     */
42
    protected static $storageItemsField = 'items';
43
44
    /**
45
     * The table name collections are stored to, must be defined in the subclass
46
     *
47
     * @var string
48
     */
49
    protected static $storageTableName = '';
50
51
    /**
52
     * Uid of the storage
53
     *
54
     * @var int
55
     */
56
    protected $uid = 0;
57
58
    /**
59
     * Collection title
60
     *
61
     * @var string
62
     */
63
    protected $title;
64
65
    /**
66
     * Collection description
67
     *
68
     * @var string
69
     */
70
    protected $description;
71
72
    /**
73
     * Table name of the records stored in this collection
74
     *
75
     * @var string
76
     */
77
    protected $itemTableName;
78
79
    /**
80
     * The local storage
81
     *
82
     * @var \SplDoublyLinkedList
83
     */
84
    protected $storage;
85
86
    /**
87
     * Creates this object.
88
     */
89
    public function __construct()
90
    {
91
        $this->storage = new \SplDoublyLinkedList();
92
    }
93
94
    /**
95
     * (PHP 5 >= 5.1.0)
96
     * Return the current element
97
     *
98
     * @link https://php.net/manual/en/iterator.current.php
99
     * @return mixed Can return any type.
100
     */
101
    public function current()
102
    {
103
        return $this->storage->current();
104
    }
105
106
    /**
107
     * (PHP 5 >= 5.1.0)
108
     * Move forward to next element
109
     *
110
     * @link https://php.net/manual/en/iterator.next.php
111
     */
112
    public function next()
113
    {
114
        $this->storage->next();
115
    }
116
117
    /**
118
     * (PHP 5 >= 5.1.0)
119
     * Return the key of the current element
120
     *
121
     * @link https://php.net/manual/en/iterator.key.php
122
     * @return int 0 on failure.
123
     */
124
    public function key()
125
    {
126
        $currentRecord = $this->storage->current();
127
        return $currentRecord['uid'];
128
    }
129
130
    /**
131
     * (PHP 5 >= 5.1.0)
132
     * Checks if current position is valid
133
     *
134
     * @link https://php.net/manual/en/iterator.valid.php
135
     * @return bool The return value will be casted to boolean and then evaluated.
136
     */
137
    public function valid()
138
    {
139
        return $this->storage->valid();
140
    }
141
142
    /**
143
     * (PHP 5 >= 5.1.0)
144
     * Rewind the Iterator to the first element
145
     *
146
     * @link https://php.net/manual/en/iterator.rewind.php
147
     */
148
    public function rewind()
149
    {
150
        $this->storage->rewind();
151
    }
152
153
    /**
154
     * (PHP 5 >= 5.1.0)
155
     * String representation of object
156
     *
157
     * @link https://php.net/manual/en/serializable.serialize.php
158
     * @return string the string representation of the object or &null;
159
     */
160
    public function serialize()
161
    {
162
        $data = [
163
            'uid' => $this->getIdentifier()
164
        ];
165
        return serialize($data);
166
    }
167
168
    /**
169
     * (PHP 5 >= 5.1.0)
170
     * Constructs the object
171
     *
172
     * @link https://php.net/manual/en/serializable.unserialize.php
173
     * @param string $serialized The string representation of the object
174
     * @return mixed the original value unserialized.
175
     */
176
    public function unserialize($serialized)
177
    {
178
        $data = unserialize($serialized);
179
        return self::load($data['uid']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::load($data['uid']) returns the type TYPO3\CMS\Core\Collection\AbstractRecordCollection which is incompatible with the return type mandated by Serializable::unserialize() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
180
    }
181
182
    /**
183
     * (PHP 5 >= 5.1.0)
184
     * Count elements of an object
185
     *
186
     * @link https://php.net/manual/en/countable.count.php
187
     * @return int The custom count as an integer.
188
     */
189
    public function count()
190
    {
191
        return $this->storage->count();
192
    }
193
194
    /**
195
     * Getter for the title
196
     *
197
     * @return string
198
     */
199
    public function getTitle()
200
    {
201
        return $this->title;
202
    }
203
204
    /**
205
     * Getter for the UID
206
     *
207
     * @return int
208
     */
209
    public function getUid()
210
    {
211
        return $this->uid;
212
    }
213
214
    /**
215
     * Getter for the description
216
     *
217
     * @return string
218
     */
219
    public function getDescription()
220
    {
221
        return $this->description;
222
    }
223
224
    /**
225
     * Setter for the title
226
     *
227
     * @param string $title
228
     */
229
    public function setTitle($title)
230
    {
231
        $this->title = $title;
232
    }
233
234
    /**
235
     * Setter for the description
236
     *
237
     * @param string $desc
238
     */
239
    public function setDescription($desc)
240
    {
241
        $this->description = $desc;
242
    }
243
244
    /**
245
     * Setter for the name of the data-source table
246
     *
247
     * @return string
248
     */
249
    public function getItemTableName()
250
    {
251
        return $this->itemTableName;
252
    }
253
254
    /**
255
     * Setter for the name of the data-source table
256
     *
257
     * @param string $tableName
258
     */
259
    public function setItemTableName($tableName)
260
    {
261
        $this->itemTableName = $tableName;
262
    }
263
264
    /**
265
     * Sorts collection via given callBackFunction
266
     *
267
     * The comparison function given as must return an integer less than, equal to, or greater than
268
     * zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
269
     *
270
     * @param callable $callbackFunction
271
     * @see http://www.php.net/manual/en/function.usort.php
272
     */
273
    public function usort($callbackFunction)
274
    {
275
        // @todo Implement usort() method with TCEforms in mind
276
        throw new \RuntimeException('This method is not yet supported.', 1322545589);
277
    }
278
279
    /**
280
     * Moves the item within the collection
281
     *
282
     * the item at $currentPosition will be moved to
283
     * $newPosition. Omitting $newPosition will move to top.
284
     *
285
     * @param int $currentPosition
286
     * @param int $newPosition
287
     */
288
    public function moveItemAt($currentPosition, $newPosition = 0)
289
    {
290
        // @todo Implement usort() method with TCEforms in mind
291
        throw new \RuntimeException('This method is not yet supported.', 1322545626);
292
    }
293
294
    /**
295
     * Returns the uid of the collection
296
     *
297
     * @return int
298
     */
299
    public function getIdentifier()
300
    {
301
        return $this->uid;
302
    }
303
304
    /**
305
     * Sets the identifier of the collection
306
     *
307
     * @param int $id
308
     */
309
    public function setIdentifier($id)
310
    {
311
        $this->uid = (int)$id;
312
    }
313
314
    /**
315
     * Loads the collections with the given id from persistence
316
     *
317
     * For memory reasons, per default only f.e. title, database-table,
318
     * identifier (what ever static data is defined) is loaded.
319
     * Entries can be load on first access.
320
     *
321
     * @param int $id Id of database record to be loaded
322
     * @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
323
     * @return CollectionInterface
324
     */
325
    public static function load($id, $fillItems = false)
326
    {
327
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(static::getCollectionDatabaseTable());
328
        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
329
        $collectionRecord = $queryBuilder->select('*')
330
            ->from(static::getCollectionDatabaseTable())
331
            ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)))
332
            ->execute()
333
            ->fetch();
334
        return self::create($collectionRecord, $fillItems);
335
    }
336
337
    /**
338
     * Creates a new collection objects and reconstitutes the
339
     * given database record to the new object.
340
     *
341
     * @param array $collectionRecord Database record
342
     * @param bool $fillItems Populates the entries directly on load, might be bad for memory on large collections
343
     * @return CollectionInterface
344
     */
345
    public static function create(array $collectionRecord, $fillItems = false)
346
    {
347
        // [phpstan] Unsafe usage of new static()
348
        // todo: Either mark this class or its constructor final or use new self instead.
349
        $collection = new static();
350
        $collection->fromArray($collectionRecord);
351
        if ($fillItems) {
352
            $collection->loadContents();
353
        }
354
        return $collection;
355
    }
356
357
    /**
358
     * Persists current collection state to underlying storage
359
     */
360
    public function persist()
361
    {
362
        $uid = $this->getIdentifier() == 0 ? 'NEW' . random_int(100000, 999999) : $this->getIdentifier();
363
        $data = [
364
            trim(static::getCollectionDatabaseTable()) => [
365
                $uid => $this->getPersistableDataArray()
366
            ]
367
        ];
368
        // New records always must have a pid
369
        if ($this->getIdentifier() == 0) {
370
            $data[trim(static::getCollectionDatabaseTable())][$uid]['pid'] = 0;
371
        }
372
        /** @var \TYPO3\CMS\Core\DataHandling\DataHandler $tce */
373
        $tce = GeneralUtility::makeInstance(DataHandler::class);
374
        $tce->start($data, []);
375
        $tce->process_datamap();
376
    }
377
378
    /**
379
     * Returns an array of the persistable properties and contents
380
     * which are processable by DataHandler.
381
     *
382
     * For internal usage in persist only.
383
     *
384
     * @return array
385
     */
386
    abstract protected function getPersistableDataArray();
387
388
    /**
389
     * Generates comma-separated list of entry uids for usage in DataHandler
390
     *
391
     * also allow to add table name, if it might be needed by DataHandler for
392
     * storing the relation
393
     *
394
     * @param bool $includeTableName
395
     * @return string
396
     */
397
    protected function getItemUidList($includeTableName = true)
398
    {
399
        $list = [];
400
        foreach ($this->storage as $entry) {
401
            $list[] = ($includeTableName ? $this->getItemTableName() . '_' : '') . $entry['uid'];
402
        }
403
        return implode(',', $list);
404
    }
405
406
    /**
407
     * Builds an array representation of this collection
408
     *
409
     * @return array
410
     */
411
    public function toArray()
412
    {
413
        $itemArray = [];
414
        foreach ($this->storage as $item) {
415
            $itemArray[] = $item;
416
        }
417
        return [
418
            'uid' => $this->getIdentifier(),
419
            'title' => $this->getTitle(),
420
            'description' => $this->getDescription(),
421
            'table_name' => $this->getItemTableName(),
422
            'items' => $itemArray
423
        ];
424
    }
425
426
    /**
427
     * Loads the properties of this collection from an array
428
     *
429
     * @param array $array
430
     */
431
    public function fromArray(array $array)
432
    {
433
        $this->uid = $array['uid'];
434
        $this->title = $array['title'];
435
        $this->description = $array['description'];
436
        $this->itemTableName = $array['table_name'];
437
    }
438
439
    protected static function getCollectionDatabaseTable(): string
440
    {
441
        if (!empty(static::$storageTableName)) {
442
            return static::$storageTableName;
443
        }
444
        throw new \RuntimeException('No storage table name was defined the class "' . static::class . '".', 1592207959);
445
    }
446
}
447