Passed
Push — master ( bb971f...a9eeac )
by
unknown
20:40
created

CollectionService::getElementEntityProcessor()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
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\Workspaces\Service\Dependency;
17
18
use TYPO3\CMS\Core\SingletonInterface;
19
use TYPO3\CMS\Core\Utility\GeneralUtility;
20
use TYPO3\CMS\Workspaces\Dependency;
21
use TYPO3\CMS\Workspaces\Dependency\DependencyResolver;
22
use TYPO3\CMS\Workspaces\Dependency\ElementEntity;
23
use TYPO3\CMS\Workspaces\Dependency\ElementEntityProcessor;
24
use TYPO3\CMS\Workspaces\Dependency\EventCallback;
25
use TYPO3\CMS\Workspaces\Dependency\ReferenceEntity;
26
use TYPO3\CMS\Workspaces\Service\GridDataService;
27
28
/**
29
 * Service to collect dependent elements.
30
 */
31
class CollectionService implements SingletonInterface
32
{
33
    /**
34
     * @var \TYPO3\CMS\Core\DataHandling\DataHandler
35
     */
36
    protected $dataHandler;
37
38
    /**
39
     * @var Dependency\ElementEntityProcessor
40
     */
41
    protected $elementEntityProcessor;
42
43
    /**
44
     * @var Dependency\DependencyResolver
45
     */
46
    protected $dependencyResolver;
47
48
    /**
49
     * @var array
50
     */
51
    protected $dataArray;
52
53
    /**
54
     * @var array
55
     */
56
    protected $nestedDataArray;
57
58
    /**
59
     * @return Dependency\DependencyResolver
60
     */
61
    public function getDependencyResolver()
62
    {
63
        if (!isset($this->dependencyResolver)) {
64
            $this->dependencyResolver = GeneralUtility::makeInstance(DependencyResolver::class);
65
            $this->dependencyResolver->setOuterMostParentsRequireReferences(true);
66
            $this->dependencyResolver->setWorkspace($this->getWorkspace());
67
68
            $this->dependencyResolver->setEventCallback(
69
                ElementEntity::EVENT_Construct,
70
                $this->getDependencyCallback('createNewDependentElementCallback')
71
            );
72
73
            $this->dependencyResolver->setEventCallback(
74
                ElementEntity::EVENT_CreateChildReference,
75
                $this->getDependencyCallback('createNewDependentElementChildReferenceCallback')
76
            );
77
78
            $this->dependencyResolver->setEventCallback(
79
                ElementEntity::EVENT_CreateParentReference,
80
                $this->getDependencyCallback('createNewDependentElementParentReferenceCallback')
81
            );
82
        }
83
84
        return $this->dependencyResolver;
85
    }
86
87
    /**
88
     * Gets a new callback to be used in the dependency resolver utility.
89
     *
90
     * @param string $method
91
     * @param array $targetArguments
92
     * @return Dependency\EventCallback
93
     */
94
    protected function getDependencyCallback($method, array $targetArguments = [])
95
    {
96
        return GeneralUtility::makeInstance(
97
            EventCallback::class,
98
            $this->getElementEntityProcessor(),
99
            $method,
100
            $targetArguments
101
        );
102
    }
103
104
    /**
105
     * Gets the element entity processor.
106
     *
107
     * @return Dependency\ElementEntityProcessor
108
     */
109
    protected function getElementEntityProcessor()
110
    {
111
        if (!isset($this->elementEntityProcessor)) {
112
            $this->elementEntityProcessor = GeneralUtility::makeInstance(ElementEntityProcessor::class);
113
            $this->elementEntityProcessor->setWorkspace($this->getWorkspace());
114
        }
115
        return $this->elementEntityProcessor;
116
    }
117
118
    /**
119
     * Gets the current workspace id.
120
     *
121
     * @return int
122
     */
123
    protected function getWorkspace()
124
    {
125
        return (int)$GLOBALS['BE_USER']->workspace;
126
    }
127
128
    /**
129
     * Processes the data array
130
     *
131
     * @param array $dataArray
132
     * @return array
133
     */
134
    public function process(array $dataArray)
135
    {
136
        $collection = 0;
137
        $this->dataArray = $dataArray;
138
        $this->nestedDataArray = [];
139
140
        $outerMostParents = $this->getDependencyResolver()->getOuterMostParents();
141
142
        if (empty($outerMostParents)) {
143
            return $this->dataArray;
144
        }
145
146
        // For each outer most parent, get all nested child elements:
147
        foreach ($outerMostParents as $outerMostParent) {
148
            $this->resolveDataArrayChildDependencies(
149
                $outerMostParent,
150
                ++$collection
151
            );
152
        }
153
154
        $processedDataArray = $this->finalize($this->dataArray);
155
156
        unset($this->dataArray);
157
        unset($this->nestedDataArray);
158
159
        return $processedDataArray;
160
    }
161
162
    /**
163
     * Applies structures to instance data array and
164
     * ensures children are added below accordant parent
165
     *
166
     * @param array $dataArray
167
     * @return array
168
     */
169
    protected function finalize(array $dataArray)
170
    {
171
        $processedDataArray = [];
172
        foreach ($dataArray as $dataElement) {
173
            $dataElementIdentifier = $dataElement['id'];
174
            $processedDataArray[] = $dataElement;
175
            // Insert children (if any)
176
            if (!empty($this->nestedDataArray[$dataElementIdentifier])) {
177
                $processedDataArray = array_merge(
178
                    $processedDataArray,
179
                    $this->finalize($this->nestedDataArray[$dataElementIdentifier])
180
                );
181
                unset($this->nestedDataArray[$dataElementIdentifier]);
182
            }
183
        }
184
185
        return $processedDataArray;
186
    }
187
188
    /**
189
     * Resolves nested child dependencies.
190
     *
191
     * @param Dependency\ElementEntity $parent
192
     * @param int $collection
193
     * @param string $nextParentIdentifier
194
     * @param int $collectionLevel
195
     */
196
    protected function resolveDataArrayChildDependencies(ElementEntity $parent, $collection, $nextParentIdentifier = '', $collectionLevel = 0)
197
    {
198
        $parentIdentifier = $parent->__toString();
199
        $parentIsSet = isset($this->dataArray[$parentIdentifier]);
200
201
        if ($parentIsSet) {
202
            $this->dataArray[$parentIdentifier][GridDataService::GridColumn_Collection] = $collection;
203
            $this->dataArray[$parentIdentifier][GridDataService::GridColumn_CollectionLevel] = $collectionLevel;
204
            $this->dataArray[$parentIdentifier][GridDataService::GridColumn_CollectionCurrent] = md5($parentIdentifier);
205
            $this->dataArray[$parentIdentifier][GridDataService::GridColumn_CollectionChildren] = $this->getCollectionChildrenCount($parent->getChildren());
206
            $nextParentIdentifier = $parentIdentifier;
207
            $collectionLevel++;
208
        }
209
210
        foreach ($parent->getChildren() as $child) {
211
            $this->resolveDataArrayChildDependencies(
212
                $child->getElement(),
213
                $collection,
214
                $nextParentIdentifier,
215
                $collectionLevel
216
            );
217
218
            $childIdentifier = $child->getElement()->__toString();
219
            if (!empty($nextParentIdentifier) && isset($this->dataArray[$childIdentifier])) {
220
                // Remove from dataArray, but collect to process later
221
                // and add it just next to the accordant parent element
222
                $this->dataArray[$childIdentifier][GridDataService::GridColumn_CollectionParent] = md5($nextParentIdentifier);
223
                $this->nestedDataArray[$nextParentIdentifier][] = $this->dataArray[$childIdentifier];
224
                unset($this->dataArray[$childIdentifier]);
225
            }
226
        }
227
    }
228
229
    /**
230
     * Return count of children, present in the data array
231
     *
232
     * @param ReferenceEntity[] $children
233
     * @return int
234
     */
235
    protected function getCollectionChildrenCount(array $children): int
236
    {
237
        return count(
238
            array_filter($children, function (ReferenceEntity $child) {
239
                return isset($this->dataArray[$child->getElement()->__toString()]);
240
            })
241
        );
242
    }
243
}
244