Completed
Push — EZP-26342 ( f736d8...3812b1 )
by André
22:44
created

RelationProcessor   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 204
Duplicated Lines 15.69 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 32
loc 204
rs 9.2
c 0
b 0
f 0
wmc 34
lcom 1
cbo 11

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C appendFieldRelations() 0 72 13
F processFieldRelations() 32 89 20

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * File containing the RelationProcessor class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Repository\Helper;
12
13
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
14
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
15
use eZ\Publish\SPI\Persistence\Handler;
16
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
17
use eZ\Publish\Core\Repository\Values\Content\Relation;
18
use eZ\Publish\Core\FieldType\Value as BaseValue;
19
use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType;
20
use eZ\Publish\Core\FieldType\ValidationError;
21
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct;
22
23
/**
24
 * RelationProcessor is an internal service used for handling field relations upon Content creation or update.
25
 *
26
 * @internal
27
 */
28
class RelationProcessor
29
{
30
    /**
31
     * @var \eZ\Publish\SPI\Persistence\Handler
32
     */
33
    protected $persistenceHandler;
34
35
    /**
36
     * Setups service with reference to repository object that created it & corresponding handler.
37
     *
38
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
39
     */
40
    public function __construct(Handler $handler)
41
    {
42
        $this->persistenceHandler = $handler;
43
    }
44
45
    /**
46
     * Appends destination Content ids of given $fieldValue to the $relation array.
47
     *
48
     * If $fieldValue contains Location ids, the will be converted to the Content id that Location encapsulates.
49
     *
50
     * @param array $relations
51
     * @param array $locationIdToContentIdMapping An array with Location Ids as keys and corresponding Content Id as values
52
     * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType
53
     * @param \eZ\Publish\Core\FieldType\Value $fieldValue Accepted field value.
54
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition
55
     *
56
     * @return \eZ\Publish\SPI\FieldType\ValidationError[]
57
     */
58
    public function appendFieldRelations(
59
        array &$relations,
60
        array &$locationIdToContentIdMapping,
61
        SPIFieldType $fieldType,
62
        BaseValue $fieldValue,
63
        FieldDefinition $fieldDefinition
64
    ) {
65
        $validationErrors = [];
66
        foreach ($fieldType->getRelations($fieldValue) as $relationType => $destinationIds) {
67
            if ($relationType === Relation::FIELD) {
68
                if (!isset($relations[$relationType][$fieldDefinition->id])) {
69
                    $relations[$relationType][$fieldDefinition->id] = array();
70
                }
71
72
                foreach ($destinationIds as $destinationId) {
73
                    try {
74
                        $this->persistenceHandler->contentHandler()->loadContentInfo($destinationId);
75
                    } catch (NotFoundException $e) {
76
                        $validationErrors[] = new ValidationError(
77
                            "Relations to Content '%content%' could not be found as part of field '%identifier%'",
78
                            null,
79
                            array(
80
                                '%content%' => $destinationId,
81
                                '%identifier%' => $fieldDefinition->identifier,
82
                            ),
83
                            "[$fieldDefinition->identifier]"
84
                        );
85
                        continue;
86
                    }
87
                }
88
89
                $relations[$relationType][$fieldDefinition->id] += array_flip($destinationIds);
90
            } elseif ($relationType & (Relation::LINK | Relation::EMBED)) {
91
                // Using bitwise operators as Legacy Stack stores COMMON, LINK and EMBED relation types
92
                // in the same entry using bitmask
93
                if (!isset($relations[$relationType])) {
94
                    $relations[$relationType] = array();
95
                }
96
97
                if (isset($destinationIds['locationIds'])) {
98
                    foreach ($destinationIds['locationIds'] as $locationId) {
99
                        if (!isset($locationIdToContentIdMapping[$locationId])) {
100
                            try {
101
                                $location = $this->persistenceHandler->locationHandler()->load($locationId);
102
                            } catch (NotFoundException $e) {
103
                                $validationErrors[] = new ValidationError(
104
                                    "Relations to Location '%location%' could not be found as part of field '%identifier%'",
105
                                    null,
106
                                    array(
107
                                        '%location%' => $locationId,
108
                                        '%identifier%' => $fieldDefinition->identifier,
109
                                    ),
110
                                    "[$fieldDefinition->identifier]"
111
                                );
112
                                continue;
113
                            }
114
115
                            $locationIdToContentIdMapping[$locationId] = $location->contentId;
116
                        }
117
118
                        $relations[$relationType][$locationIdToContentIdMapping[$locationId]] = true;
119
                    }
120
                }
121
122
                if (isset($destinationIds['contentIds'])) {
123
                    $relations[$relationType] += array_flip($destinationIds['contentIds']);
124
                }
125
            }
126
        }
127
128
        return $validationErrors;
129
    }
130
131
    /**
132
     * Persists relation data for a content version.
133
     *
134
     * This method creates new relations and deletes removed relations.
135
     *
136
     * @param array $inputRelations
137
     * @param mixed $sourceContentId
138
     * @param mixed $sourceContentVersionNo
139
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
140
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $existingRelations An array of existing relations for Content version (empty when creating new content)
141
     */
142
    public function processFieldRelations(
143
        array $inputRelations,
144
        $sourceContentId,
145
        $sourceContentVersionNo,
146
        ContentType $contentType,
147
        array $existingRelations = array()
148
    ) {
149
        // Map existing relations for easier handling
150
        $mappedRelations = array();
151
        foreach ($existingRelations as $relation) {
152
            if ($relation->type === Relation::FIELD) {
153
                $fieldDefinitionId = $contentType->getFieldDefinition($relation->sourceFieldDefinitionIdentifier)->id;
154
                $mappedRelations[$relation->type][$fieldDefinitionId][$relation->destinationContentInfo->id] = $relation;
155
            }
156
            // Using bitwise AND as Legacy Stack stores COMMON, LINK and EMBED relation types
157
            // in the same entry using bitmask
158 View Code Duplication
            if ($relation->type & Relation::LINK) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
                $mappedRelations[Relation::LINK][$relation->destinationContentInfo->id] = $relation;
160
            }
161 View Code Duplication
            if ($relation->type & Relation::EMBED) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
                $mappedRelations[Relation::EMBED][$relation->destinationContentInfo->id] = $relation;
163
            }
164
        }
165
166
        // Add new relations
167
        foreach ($inputRelations as $relationType => $relationData) {
168
            if ($relationType === Relation::FIELD) {
169
                foreach ($relationData as $fieldDefinitionId => $contentIds) {
170
                    foreach (array_keys($contentIds) as $destinationContentId) {
171
                        if (isset($mappedRelations[$relationType][$fieldDefinitionId][$destinationContentId])) {
172
                            unset($mappedRelations[$relationType][$fieldDefinitionId][$destinationContentId]);
173 View Code Duplication
                        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
174
                            $this->persistenceHandler->contentHandler()->addRelation(
175
                                new SPIRelationCreateStruct(
176
                                    array(
177
                                        'sourceContentId' => $sourceContentId,
178
                                        'sourceContentVersionNo' => $sourceContentVersionNo,
179
                                        'sourceFieldDefinitionId' => $fieldDefinitionId,
180
                                        'destinationContentId' => $destinationContentId,
181
                                        'type' => $relationType,
182
                                    )
183
                                )
184
                            );
185
                        }
186
                    }
187
                }
188
            } elseif ($relationType === Relation::LINK || $relationType === Relation::EMBED) {
189
                foreach (array_keys($relationData) as $destinationContentId) {
190
                    if (isset($mappedRelations[$relationType][$destinationContentId])) {
191
                        unset($mappedRelations[$relationType][$destinationContentId]);
192 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
193
                        $this->persistenceHandler->contentHandler()->addRelation(
194
                            new SPIRelationCreateStruct(
195
                                array(
196
                                    'sourceContentId' => $sourceContentId,
197
                                    'sourceContentVersionNo' => $sourceContentVersionNo,
198
                                    'sourceFieldDefinitionId' => null,
199
                                    'destinationContentId' => $destinationContentId,
200
                                    'type' => $relationType,
201
                                )
202
                            )
203
                        );
204
                    }
205
                }
206
            }
207
        }
208
209
        // Remove relations not present in input set
210
        foreach ($mappedRelations as $relationType => $relationData) {
211
            foreach ($relationData as $relationEntry) {
212
                switch ($relationType) {
213
                    case Relation::FIELD:
214
                        foreach ($relationEntry as $relation) {
215
                            $this->persistenceHandler->contentHandler()->removeRelation(
216
                                $relation->id,
217
                                $relationType
218
                            );
219
                        }
220
                        break;
221
                    case Relation::LINK:
222
                    case Relation::EMBED:
223
                        $this->persistenceHandler->contentHandler()->removeRelation(
224
                            $relationEntry->id,
225
                            $relationType
226
                        );
227
                }
228
            }
229
        }
230
    }
231
}
232