Completed
Push — ezp-31420-merge-up ( ec14fb...141a64 )
by
unknown
40:13 queued 27:42
created

RelationListConverter::getRelationXmlHashFromDB()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 55
rs 8.9818
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter;
8
9
use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter;
10
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue;
11
use eZ\Publish\SPI\Persistence\Content\FieldValue;
12
use eZ\Publish\SPI\Persistence\Content\Type as ContentType;
13
use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition;
14
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition;
15
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
16
use DOMDocument;
17
use PDO;
18
19
class RelationListConverter implements Converter
20
{
21
    /** @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler */
22
    private $db;
23
24
    /**
25
     * Create instance of RelationList converter.
26
     *
27
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $db
28
     */
29
    public function __construct(DatabaseHandler $db)
30
    {
31
        $this->db = $db;
32
    }
33
34
    /**
35
     * Converts data from $value to $storageFieldValue.
36
     *
37
     * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value
38
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue
39
     */
40
    public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue)
41
    {
42
        $doc = new DOMDocument('1.0', 'utf-8');
43
        $root = $doc->createElement('related-objects');
44
        $doc->appendChild($root);
45
46
        $relationList = $doc->createElement('relation-list');
47
        $data = $this->getRelationXmlHashFromDB($value->data['destinationContentIds']);
48
        $priority = 0;
49
50
        foreach ($value->data['destinationContentIds'] as $id) {
51
            if (!isset($data[$id][0])) {
52
                // Ignore deleted content items (we can't throw as it would block ContentService->createContentDraft())
53
                continue;
54
            }
55
            $row = $data[$id][0];
56
            $row['ezcontentobject_id'] = $id;
57
            $row['priority'] = (++$priority);
58
59
            $relationItem = $doc->createElement('relation-item');
60
            foreach (self::dbAttributeMap() as $domAttrKey => $propertyKey) {
61
                if (!isset($row[$propertyKey])) {
62
                    // left join data missing, ignore the given attribute (content in trash missing location)
63
                    continue;
64
                }
65
66
                $relationItem->setAttribute($domAttrKey, $row[$propertyKey]);
67
            }
68
            $relationList->appendChild($relationItem);
69
            unset($relationItem);
70
        }
71
72
        $root->appendChild($relationList);
73
        $doc->appendChild($root);
74
75
        $storageFieldValue->dataText = $doc->saveXML();
76
        $storageFieldValue->sortKeyString = $value->sortKey;
77
    }
78
79
    /**
80
     * Converts data from $value to $fieldValue.
81
     *
82
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
83
     * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue
84
     */
85
    public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue)
86
    {
87
        $fieldValue->data = ['destinationContentIds' => []];
88
        if ($value->dataText === null) {
89
            return;
90
        }
91
92
        $priorityByContentId = [];
93
94
        $dom = new DOMDocument('1.0', 'utf-8');
95
        if ($dom->loadXML($value->dataText) === true) {
96
            foreach ($dom->getElementsByTagName('relation-item') as $relationItem) {
97
                /* @var \DOMElement $relationItem */
98
                $priorityByContentId[$relationItem->getAttribute('contentobject-id')] =
99
                    $relationItem->getAttribute('priority');
100
            }
101
        }
102
103
        asort($priorityByContentId, SORT_NUMERIC);
104
105
        $fieldValue->data['destinationContentIds'] = array_keys($priorityByContentId);
106
        $fieldValue->sortKey = $value->sortKeyString;
107
    }
108
109
    /**
110
     * Converts field definition data in $fieldDef into $storageFieldDef.
111
     *
112
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
113
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
114
     */
115
    public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef)
116
    {
117
        $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings;
118
        $validators = $fieldDef->fieldTypeConstraints->validators;
119
        $doc = new DOMDocument('1.0', 'utf-8');
120
        $root = $doc->createElement('related-objects');
121
        $doc->appendChild($root);
122
123
        $constraints = $doc->createElement('constraints');
124 View Code Duplication
        if (!empty($fieldSettings['selectionContentTypes'])) {
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...
125
            foreach ($fieldSettings['selectionContentTypes'] as $typeIdentifier) {
126
                $allowedClass = $doc->createElement('allowed-class');
127
                $allowedClass->setAttribute('contentclass-identifier', $typeIdentifier);
128
                $constraints->appendChild($allowedClass);
129
                unset($allowedClass);
130
            }
131
        }
132
        $root->appendChild($constraints);
133
134
        $type = $doc->createElement('type');
135
        $type->setAttribute('value', 2); //Deprecated advance object relation list type, set since 4.x does
136
        $root->appendChild($type);
137
138
        $objectClass = $doc->createElement('object_class');
139
        $objectClass->setAttribute('value', ''); //Deprecated advance object relation class type, set since 4.x does
140
        $root->appendChild($objectClass);
141
142
        $selectionType = $doc->createElement('selection_type');
143
        if (isset($fieldSettings['selectionMethod'])) {
144
            $selectionType->setAttribute('value', (int)$fieldSettings['selectionMethod']);
145
        } else {
146
            $selectionType->setAttribute('value', 0);
147
        }
148
        $root->appendChild($selectionType);
149
150
        $defaultLocation = $doc->createElement('contentobject-placement');
151
        if (!empty($fieldSettings['selectionDefaultLocation'])) {
152
            $defaultLocation->setAttribute('node-id', (int)$fieldSettings['selectionDefaultLocation']);
153
        }
154
        $root->appendChild($defaultLocation);
155
156
        $selectionLimit = $doc->createElement('selection_limit');
157
        if (isset($validators['RelationListValueValidator']['selectionLimit'])) {
158
            $selectionLimit->setAttribute('value', (int)$validators['RelationListValueValidator']['selectionLimit']);
159
        } else {
160
            $selectionLimit->setAttribute('value', 0);
161
        }
162
        $root->appendChild($selectionLimit);
163
164
        $doc->appendChild($root);
165
        $storageDef->dataText5 = $doc->saveXML();
166
    }
167
168
    /**
169
     * Converts field definition data in $storageDef into $fieldDef.
170
     *
171
     * <code>
172
     *   <?xml version="1.0" encoding="utf-8"?>
173
     *   <related-objects>
174
     *     <constraints>
175
     *       <allowed-class contentclass-identifier="blog_post"/>
176
     *     </constraints>
177
     *     <type value="2"/>
178
     *     <selection_type value="1"/>
179
     *     <selection_limit value="5"/>
180
     *     <object_class value=""/>
181
     *     <contentobject-placement node-id="67"/>
182
     *   </related-objects>
183
     *
184
     *   <?xml version="1.0" encoding="utf-8"?>
185
     *   <related-objects>
186
     *     <constraints/>
187
     *     <type value="2"/>
188
     *     <selection_type value="0"/>
189
     *     <object_class value=""/>
190
     *     <contentobject-placement/>
191
     *   </related-objects>
192
     * </code>
193
     *
194
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
195
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
196
     */
197
    public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef)
198
    {
199
        // default settings
200
        $fieldDef->fieldTypeConstraints->fieldSettings = [
201
            'selectionMethod' => 0,
202
            'selectionDefaultLocation' => null,
203
            'selectionContentTypes' => [],
204
        ];
205
206
        $fieldDef->fieldTypeConstraints->validators = [
207
            'RelationListValueValidator' => [
208
                'selectionLimit' => 0,
209
            ],
210
        ];
211
212
        // default value
213
        $fieldDef->defaultValue = new FieldValue();
214
        $fieldDef->defaultValue->data = ['destinationContentIds' => []];
215
216
        if ($storageDef->dataText5 === null) {
217
            return;
218
        }
219
220
        $dom = new DOMDocument('1.0', 'utf-8');
221
        if (empty($storageDef->dataText5) || $dom->loadXML($storageDef->dataText5) !== true) {
222
            return;
223
        }
224
225
        // read settings from storage
226
        $fieldSettings = &$fieldDef->fieldTypeConstraints->fieldSettings;
227 View Code Duplication
        if (
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...
228
            ($selectionType = $dom->getElementsByTagName('selection_type')->item(0)) &&
229
            $selectionType->hasAttribute('value')
0 ignored issues
show
Bug introduced by
The method hasAttribute() does not exist on DOMNode. Did you maybe mean hasAttributes()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
230
        ) {
231
            $fieldSettings['selectionMethod'] = (int)$selectionType->getAttribute('value');
232
        }
233
234 View Code Duplication
        if (
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...
235
            ($defaultLocation = $dom->getElementsByTagName('contentobject-placement')->item(0)) &&
236
            $defaultLocation->hasAttribute('node-id')
0 ignored issues
show
Bug introduced by
The method hasAttribute() does not exist on DOMNode. Did you maybe mean hasAttributes()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
237
        ) {
238
            $fieldSettings['selectionDefaultLocation'] = (int)$defaultLocation->getAttribute('node-id');
239
        }
240
241
        if (!($constraints = $dom->getElementsByTagName('constraints'))) {
242
            return;
243
        }
244
245
        foreach ($constraints->item(0)->getElementsByTagName('allowed-class') as $allowedClass) {
246
            $fieldSettings['selectionContentTypes'][] = $allowedClass->getAttribute('contentclass-identifier');
247
        }
248
249
        // read validators configuration from storage
250
        $validators = &$fieldDef->fieldTypeConstraints->validators;
251
        if (
252
            ($selectionLimit = $dom->getElementsByTagName('selection_limit')->item(0)) &&
253
            $selectionLimit->hasAttribute('value')
0 ignored issues
show
Bug introduced by
The method hasAttribute() does not exist on DOMNode. Did you maybe mean hasAttributes()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
254
        ) {
255
            $validators['RelationListValueValidator']['selectionLimit'] = (int)$selectionLimit->getAttribute('value');
256
        }
257
    }
258
259
    /**
260
     * Returns the name of the index column in the attribute table.
261
     *
262
     * Returns the name of the index column the datatype uses, which is either
263
     * "sort_key_int" or "sort_key_string". This column is then used for
264
     * filtering and sorting for this type.
265
     *
266
     * @return string
267
     */
268
    public function getIndexColumn()
269
    {
270
        return 'sort_key_string';
271
    }
272
273
    /**
274
     * @param mixed[] $destinationContentIds
275
     *
276
     * @throws \Exception
277
     *
278
     * @return array
279
     */
280
    protected function getRelationXmlHashFromDB(array $destinationContentIds)
281
    {
282
        if (empty($destinationContentIds)) {
283
            return [];
284
        }
285
286
        $q = $this->db->createSelectQuery();
287
        $q
288
            ->select(
289
                $this->db->aliasedColumn($q, 'id', 'ezcontentobject'),
290
                $this->db->aliasedColumn($q, 'remote_id', 'ezcontentobject'),
291
                $this->db->aliasedColumn($q, 'current_version', 'ezcontentobject'),
292
                $this->db->aliasedColumn($q, 'contentclass_id', 'ezcontentobject'),
293
                $this->db->aliasedColumn($q, 'node_id', 'ezcontentobject_tree'),
294
                $this->db->aliasedColumn($q, 'parent_node_id', 'ezcontentobject_tree'),
295
                $this->db->aliasedColumn($q, 'identifier', 'ezcontentclass')
296
            )
297
            ->from($this->db->quoteTable('ezcontentobject'))
298
            ->leftJoin(
299
                $this->db->quoteTable('ezcontentobject_tree'),
300
                $q->expr->lAnd(
301
                    $q->expr->eq(
302
                        $this->db->quoteColumn('contentobject_id', 'ezcontentobject_tree'),
303
                        $this->db->quoteColumn('id', 'ezcontentobject')
304
                    ),
305
                    $q->expr->eq(
306
                        $this->db->quoteColumn('node_id', 'ezcontentobject_tree'),
307
                        $this->db->quoteColumn('main_node_id', 'ezcontentobject_tree')
308
                    )
309
                )
310
            )
311
            ->leftJoin(
312
                $this->db->quoteTable('ezcontentclass'),
313
                $q->expr->lAnd(
314
                    $q->expr->eq(
315
                        $this->db->quoteColumn('id', 'ezcontentclass'),
316
                        $this->db->quoteColumn('contentclass_id', 'ezcontentobject')
317
                    ),
318
                    $q->expr->eq(
319
                        $this->db->quoteColumn('version', 'ezcontentclass'),
320
                        $q->bindValue(ContentType::STATUS_DEFINED, null, PDO::PARAM_INT)
321
                    )
322
                )
323
            )
324
            ->where(
325
                $q->expr->in(
326
                    $this->db->quoteColumn('id', 'ezcontentobject'),
327
                    $destinationContentIds
328
                )
329
            );
330
        $stmt = $q->prepare();
331
        $stmt->execute();
332
333
        return $stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_GROUP);
334
    }
335
336
    /**
337
     * @return array
338
     */
339
    private static function dbAttributeMap()
340
    {
341
        return [
342
            // 'identifier' => 'identifier',// not used
343
            'priority' => 'priority',
344
            // 'in-trash' => 'in_trash',// false by default and implies
345
            'contentobject-id' => 'ezcontentobject_id',
346
            'contentobject-version' => 'ezcontentobject_current_version',
347
            'node-id' => 'ezcontentobject_tree_node_id',
348
            'parent-node-id' => 'ezcontentobject_tree_parent_node_id',
349
            'contentclass-id' => 'ezcontentobject_contentclass_id',
350
            'contentclass-identifier' => 'ezcontentclass_identifier',
351
            // 'is-modified' => 'is_modified',// deprecated and not used
352
            'contentobject-remote-id' => 'ezcontentobject_remote_id',
353
        ];
354
    }
355
}
356