86c09a99c743a27548053c8c7a29465ca60c7af4
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / Dependency / ElementEntity.php
1 <?php
2 namespace TYPO3\CMS\Workspaces\Dependency;
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\Database\ConnectionPool;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * Object to hold information on a dependent database element in abstract.
22 */
23 class ElementEntity
24 {
25 const REFERENCES_ChildOf = 'childOf';
26 const REFERENCES_ParentOf = 'parentOf';
27 const EVENT_Construct = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity::construct';
28 const EVENT_CreateChildReference = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity::createChildReference';
29 const EVENT_CreateParentReference = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity::createParentReference';
30 const RESPONSE_Skip = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity->skip';
31
32 /**
33 * @var bool
34 */
35 protected $invalid = false;
36
37 /**
38 * @var string
39 */
40 protected $table;
41
42 /**
43 * @var int
44 */
45 protected $id;
46
47 /**
48 * @var array
49 */
50 protected $data;
51
52 /**
53 * @var array
54 */
55 protected $record;
56
57 /**
58 * @var DependencyResolver
59 */
60 protected $dependency;
61
62 /**
63 * @var array
64 */
65 protected $children;
66
67 /**
68 * @var array
69 */
70 protected $parents;
71
72 /**
73 * @var bool
74 */
75 protected $traversingParents = false;
76
77 /**
78 * @var ElementEntity
79 */
80 protected $outerMostParent;
81
82 /**
83 * @var array
84 */
85 protected $nestedChildren;
86
87 /**
88 * Creates this object.
89 *
90 * @param string $table
91 * @param int $id
92 * @param array $data (optional)
93 * @param DependencyResolver $dependency
94 */
95 public function __construct($table, $id, array $data, DependencyResolver $dependency)
96 {
97 $this->table = $table;
98 $this->id = (int)$id;
99 $this->data = $data;
100 $this->dependency = $dependency;
101 $this->dependency->executeEventCallback(self::EVENT_Construct, $this);
102 }
103
104 /**
105 * @param bool $invalid
106 */
107 public function setInvalid($invalid)
108 {
109 $this->invalid = (bool)$invalid;
110 }
111
112 /**
113 * @return bool
114 */
115 public function isInvalid()
116 {
117 return $this->invalid;
118 }
119
120 /**
121 * Gets the table.
122 *
123 * @return string
124 */
125 public function getTable()
126 {
127 return $this->table;
128 }
129
130 /**
131 * Gets the id.
132 *
133 * @return int
134 */
135 public function getId()
136 {
137 return $this->id;
138 }
139
140 /**
141 * Sets the id.
142 *
143 * @param int $id
144 */
145 public function setId($id)
146 {
147 $this->id = (int)$id;
148 }
149
150 /**
151 * Gets the data.
152 *
153 * @return array
154 */
155 public function getData()
156 {
157 return $this->data;
158 }
159
160 /**
161 * Gets a value for a particular key from the data.
162 *
163 * @param string $key
164 * @return mixed
165 */
166 public function getDataValue($key)
167 {
168 $result = null;
169 if ($this->hasDataValue($key)) {
170 $result = $this->data[$key];
171 }
172 return $result;
173 }
174
175 /**
176 * Sets a value for a particular key in the data.
177 *
178 * @param string $key
179 * @param mixed $value
180 */
181 public function setDataValue($key, $value)
182 {
183 $this->data[$key] = $value;
184 }
185
186 /**
187 * Determines whether a particular key holds data.
188 *
189 * @param string $key
190 * @return bool
191 */
192 public function hasDataValue($key)
193 {
194 return isset($this->data[$key]);
195 }
196
197 /**
198 * Converts this object for string representation.
199 *
200 * @return string
201 */
202 public function __toString()
203 {
204 return self::getIdentifier($this->table, $this->id);
205 }
206
207 /**
208 * Gets the parent dependency object.
209 *
210 * @return DependencyResolver
211 */
212 public function getDependency()
213 {
214 return $this->dependency;
215 }
216
217 /**
218 * Gets all child references.
219 *
220 * @return array|ReferenceEntity[]
221 */
222 public function getChildren()
223 {
224 if (!isset($this->children)) {
225 $this->children = [];
226
227 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
228 ->getQueryBuilderForTable('sys_refindex');
229
230 $result = $queryBuilder
231 ->select('*')
232 ->from('sys_refindex')
233 ->where(
234 $queryBuilder->expr()->eq(
235 'tablename',
236 $queryBuilder->createNamedParameter($this->table, \PDO::PARAM_STR)
237 ),
238 $queryBuilder->expr()->eq(
239 'recuid',
240 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
241 ),
242 $queryBuilder->expr()->eq(
243 'workspace',
244 $queryBuilder->createNamedParameter($this->dependency->getWorkspace(), \PDO::PARAM_INT)
245 )
246 )
247 ->orderBy('sorting')
248 ->execute();
249
250 while ($row = $result->fetch()) {
251 if ($row['ref_table'] !== '_FILE' && $row['ref_table'] !== '_STRING') {
252 $arguments = [
253 'table' => $row['ref_table'],
254 'id' => $row['ref_uid'],
255 'field' => $row['field'],
256 'scope' => self::REFERENCES_ChildOf
257 ];
258
259 $callbackResponse = $this->dependency->executeEventCallback(
260 self::EVENT_CreateChildReference,
261 $this,
262 $arguments
263 );
264 if ($callbackResponse !== self::RESPONSE_Skip) {
265 $this->children[] = $this->getDependency()->getFactory()->getReferencedElement(
266 $row['ref_table'],
267 $row['ref_uid'],
268 $row['field'],
269 [],
270 $this->getDependency()
271 );
272 }
273 }
274 }
275 }
276 return $this->children;
277 }
278
279 /**
280 * Gets all parent references.
281 *
282 * @return array|ReferenceEntity[]
283 */
284 public function getParents()
285 {
286 if (!isset($this->parents)) {
287 $this->parents = [];
288
289 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
290 ->getQueryBuilderForTable('sys_refindex');
291
292 $result = $queryBuilder
293 ->select('*')
294 ->from('sys_refindex')
295 ->where(
296 $queryBuilder->expr()->eq(
297 'deleted',
298 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
299 ),
300 $queryBuilder->expr()->eq(
301 'ref_table',
302 $queryBuilder->createNamedParameter($this->table, \PDO::PARAM_STR)
303 ),
304 $queryBuilder->expr()->eq(
305 'ref_uid',
306 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
307 ),
308 $queryBuilder->expr()->eq(
309 'workspace',
310 $queryBuilder->createNamedParameter($this->dependency->getWorkspace(), \PDO::PARAM_INT)
311 )
312 )
313 ->orderBy('sorting')
314 ->execute();
315
316 while ($row = $result->fetch()) {
317 $arguments = [
318 'table' => $row['tablename'],
319 'id' => $row['recuid'],
320 'field' => $row['field'],
321 'scope' => self::REFERENCES_ParentOf
322 ];
323 $callbackResponse = $this->dependency->executeEventCallback(
324 self::EVENT_CreateParentReference,
325 $this,
326 $arguments
327 );
328 if ($callbackResponse !== self::RESPONSE_Skip) {
329 $this->parents[] = $this->getDependency()->getFactory()->getReferencedElement(
330 $row['tablename'],
331 $row['recuid'],
332 $row['field'],
333 [],
334 $this->getDependency()
335 );
336 }
337 }
338 }
339 return $this->parents;
340 }
341
342 /**
343 * Determines whether there are child or parent references.
344 *
345 * @return bool
346 */
347 public function hasReferences()
348 {
349 return !empty($this->getChildren()) || !empty($this->getParents());
350 }
351
352 /**
353 * Gets the outermost parent element.
354 *
355 * @return ElementEntity|bool
356 */
357 public function getOuterMostParent()
358 {
359 if (!isset($this->outerMostParent)) {
360 $parents = $this->getParents();
361 if (empty($parents)) {
362 $this->outerMostParent = $this;
363 } else {
364 $this->outerMostParent = false;
365 /** @var ReferenceEntity $parent */
366 foreach ($parents as $parent) {
367 $outerMostParent = $parent->getElement()->getOuterMostParent();
368 if ($outerMostParent instanceof ElementEntity) {
369 $this->outerMostParent = $outerMostParent;
370 break;
371 }
372 if ($outerMostParent === false) {
373 break;
374 }
375 }
376 }
377 }
378 return $this->outerMostParent;
379 }
380
381 /**
382 * Gets nested children accumulated.
383 *
384 * @return array|ReferenceEntity[]
385 */
386 public function getNestedChildren()
387 {
388 if (!isset($this->nestedChildren)) {
389 $this->nestedChildren = [[]];
390 $children = $this->getChildren();
391 /** @var ReferenceEntity $child */
392 foreach ($children as $child) {
393 $this->nestedChildren[] = [$child->getElement()->__toString() => $child->getElement()];
394 $this->nestedChildren[] = $child->getElement()->getNestedChildren();
395 }
396 }
397 return array_merge(...$this->nestedChildren);
398 }
399
400 /**
401 * Converts the object for string representation.
402 *
403 * @param string $table
404 * @param int $id
405 * @return string
406 */
407 public static function getIdentifier($table, $id)
408 {
409 return $table . ':' . $id;
410 }
411
412 /**
413 * Gets the database record of this element.
414 *
415 * @return array
416 */
417 public function getRecord()
418 {
419 if (empty($this->record['uid']) || (int)$this->record['uid'] !== $this->getId()) {
420 $this->record = [];
421
422 $fieldNames = ['uid', 'pid', 't3ver_wsid', 't3ver_state', 't3ver_oid'];
423 if (!empty($GLOBALS['TCA'][$this->getTable()]['ctrl']['delete'])) {
424 $fieldNames[] = $GLOBALS['TCA'][$this->getTable()]['ctrl']['delete'];
425 }
426
427 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
428 ->getQueryBuilderForTable($this->getTable());
429 $queryBuilder->getRestrictions()->removeAll();
430
431 $row = $queryBuilder
432 ->select(...$fieldNames)
433 ->from($this->getTable())
434 ->where(
435 $queryBuilder->expr()->eq(
436 'uid',
437 $queryBuilder->createNamedParameter($this->getId(), \PDO::PARAM_INT)
438 )
439 )
440 ->execute()
441 ->fetch();
442
443 if (is_array($row)) {
444 $this->record = $row;
445 }
446 }
447
448 return $this->record;
449 }
450 }