Completed
Push — 7.0 ( dcb9ac...8de687 )
by Łukasz
13:07
created

RelationListConverter::toStorageFieldDefinition()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 52
Code Lines 38

Duplication

Lines 8
Ratio 15.38 %

Importance

Changes 0
Metric Value
cc 6
eloc 38
nc 16
nop 2
dl 8
loc 52
rs 8.6868
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
 * File containing the Relation converter.
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
namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter;
10
11
use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter;
12
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue;
13
use eZ\Publish\SPI\Persistence\Content\FieldValue;
14
use eZ\Publish\SPI\Persistence\Content\Type as ContentType;
15
use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition;
16
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition;
17
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
18
use DOMDocument;
19
use PDO;
20
21
class RelationListConverter implements Converter
22
{
23
    /**
24
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
25
     */
26
    private $db;
27
28
    /**
29
     * Create instance of RelationList converter.
30
     *
31
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $db
32
     */
33
    public function __construct(DatabaseHandler $db)
34
    {
35
        $this->db = $db;
36
    }
37
38
    /**
39
     * Converts data from $value to $storageFieldValue.
40
     *
41
     * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value
42
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue
43
     */
44
    public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue)
45
    {
46
        $doc = new DOMDocument('1.0', 'utf-8');
47
        $root = $doc->createElement('related-objects');
48
        $doc->appendChild($root);
49
50
        $relationList = $doc->createElement('relation-list');
51
        $data = $this->getRelationXmlHashFromDB($value->data['destinationContentIds']);
52
        $priority = 0;
53
54
        foreach ($value->data['destinationContentIds'] as $id) {
55
            if (!isset($data[$id][0])) {
56
                // Ignore deleted content items (we can't throw as it would block ContentService->createContentDraft())
57
                continue;
58
            }
59
            $row = $data[$id][0];
60
            $row['ezcontentobject_id'] = $id;
61
            $row['priority'] = ($priority += 1);
62
63
            $relationItem = $doc->createElement('relation-item');
64
            foreach (self::dbAttributeMap() as $domAttrKey => $propertyKey) {
65
                if (!isset($row[$propertyKey])) {
66
                    // left join data missing, ignore the given attribute (content in trash missing location)
67
                    continue;
68
                }
69
70
                $relationItem->setAttribute($domAttrKey, $row[$propertyKey]);
71
            }
72
            $relationList->appendChild($relationItem);
73
            unset($relationItem);
74
        }
75
76
        $root->appendChild($relationList);
77
        $doc->appendChild($root);
78
79
        $storageFieldValue->dataText = $doc->saveXML();
80
    }
81
82
    /**
83
     * Converts data from $value to $fieldValue.
84
     *
85
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
86
     * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue
87
     */
88
    public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue)
89
    {
90
        $fieldValue->data = array('destinationContentIds' => array());
91
        if ($value->dataText === null) {
92
            return;
93
        }
94
95
        $priorityByContentId = array();
96
97
        $dom = new DOMDocument('1.0', 'utf-8');
98
        if ($dom->loadXML($value->dataText) === true) {
99
            foreach ($dom->getElementsByTagName('relation-item') as $relationItem) {
100
                /* @var \DOMElement $relationItem */
101
                $priorityByContentId[$relationItem->getAttribute('contentobject-id')] =
102
                    $relationItem->getAttribute('priority');
103
            }
104
        }
105
106
        asort($priorityByContentId, SORT_NUMERIC);
107
108
        $fieldValue->data['destinationContentIds'] = array_keys($priorityByContentId);
109
    }
110
111
    /**
112
     * Converts field definition data in $fieldDef into $storageFieldDef.
113
     *
114
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
115
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
116
     */
117
    public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef)
118
    {
119
        $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings;
120
        $validators = $fieldDef->fieldTypeConstraints->validators;
121
        $doc = new DOMDocument('1.0', 'utf-8');
122
        $root = $doc->createElement('related-objects');
123
        $doc->appendChild($root);
124
125
        $constraints = $doc->createElement('constraints');
126 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...
127
            foreach ($fieldSettings['selectionContentTypes'] as $typeIdentifier) {
128
                $allowedClass = $doc->createElement('allowed-class');
129
                $allowedClass->setAttribute('contentclass-identifier', $typeIdentifier);
130
                $constraints->appendChild($allowedClass);
131
                unset($allowedClass);
132
            }
133
        }
134
        $root->appendChild($constraints);
135
136
        $type = $doc->createElement('type');
137
        $type->setAttribute('value', 2); //Deprecated advance object relation list type, set since 4.x does
138
        $root->appendChild($type);
139
140
        $objectClass = $doc->createElement('object_class');
141
        $objectClass->setAttribute('value', ''); //Deprecated advance object relation class type, set since 4.x does
142
        $root->appendChild($objectClass);
143
144
        $selectionType = $doc->createElement('selection_type');
145
        if (isset($fieldSettings['selectionMethod'])) {
146
            $selectionType->setAttribute('value', (int)$fieldSettings['selectionMethod']);
147
        } else {
148
            $selectionType->setAttribute('value', 0);
149
        }
150
        $root->appendChild($selectionType);
151
152
        $defaultLocation = $doc->createElement('contentobject-placement');
153
        if (!empty($fieldSettings['selectionDefaultLocation'])) {
154
            $defaultLocation->setAttribute('node-id', (int)$fieldSettings['selectionDefaultLocation']);
155
        }
156
        $root->appendChild($defaultLocation);
157
158
        $selectionLimit = $doc->createElement('selection_limit');
159
        if (isset($validators['RelationListValueValidator']['selectionLimit'])) {
160
            $selectionLimit->setAttribute('value', (int)$validators['RelationListValueValidator']['selectionLimit']);
161
        } else {
162
            $selectionLimit->setAttribute('value', 0);
163
        }
164
        $root->appendChild($selectionLimit);
165
166
        $doc->appendChild($root);
167
        $storageDef->dataText5 = $doc->saveXML();
168
    }
169
170
    /**
171
     * Converts field definition data in $storageDef into $fieldDef.
172
     *
173
     * <code>
174
     *   <?xml version="1.0" encoding="utf-8"?>
175
     *   <related-objects>
176
     *     <constraints>
177
     *       <allowed-class contentclass-identifier="blog_post"/>
178
     *     </constraints>
179
     *     <type value="2"/>
180
     *     <selection_type value="1"/>
181
     *     <selection_limit value="5"/>
182
     *     <object_class value=""/>
183
     *     <contentobject-placement node-id="67"/>
184
     *   </related-objects>
185
     *
186
     *   <?xml version="1.0" encoding="utf-8"?>
187
     *   <related-objects>
188
     *     <constraints/>
189
     *     <type value="2"/>
190
     *     <selection_type value="0"/>
191
     *     <object_class value=""/>
192
     *     <contentobject-placement/>
193
     *   </related-objects>
194
     * </code>
195
     *
196
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
197
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
198
     */
199
    public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef)
200
    {
201
        // default settings
202
        $fieldDef->fieldTypeConstraints->fieldSettings = [
203
            'selectionMethod' => 0,
204
            'selectionDefaultLocation' => null,
205
            'selectionContentTypes' => [],
206
        ];
207
208
        $fieldDef->fieldTypeConstraints->validators = [
209
            'RelationListValueValidator' => [
210
                'selectionLimit' => 0,
211
            ],
212
        ];
213
214
        // default value
215
        $fieldDef->defaultValue = new FieldValue();
216
        $fieldDef->defaultValue->data = array('destinationContentIds' => array());
217
218
        if ($storageDef->dataText5 === null) {
219
            return;
220
        }
221
222
        $dom = new DOMDocument('1.0', 'utf-8');
223
        if (empty($storageDef->dataText5) || $dom->loadXML($storageDef->dataText5) !== true) {
224
            return;
225
        }
226
227
        // read settings from storage
228
        $fieldSettings = &$fieldDef->fieldTypeConstraints->fieldSettings;
229 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...
230
            ($selectionType = $dom->getElementsByTagName('selection_type')->item(0)) &&
231
            $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...
232
        ) {
233
            $fieldSettings['selectionMethod'] = (int)$selectionType->getAttribute('value');
234
        }
235
236 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...
237
            ($defaultLocation = $dom->getElementsByTagName('contentobject-placement')->item(0)) &&
238
            $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...
239
        ) {
240
            $fieldSettings['selectionDefaultLocation'] = (int)$defaultLocation->getAttribute('node-id');
241
        }
242
243
        if (!($constraints = $dom->getElementsByTagName('constraints'))) {
244
            return;
245
        }
246
247
        foreach ($constraints->item(0)->getElementsByTagName('allowed-class') as $allowedClass) {
248
            $fieldSettings['selectionContentTypes'][] = $allowedClass->getAttribute('contentclass-identifier');
249
        }
250
251
        // read validators configuration from storage
252
        $validators = &$fieldDef->fieldTypeConstraints->validators;
253
        if (
254
            ($selectionLimit = $dom->getElementsByTagName('selection_limit')->item(0)) &&
255
            $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...
256
        ) {
257
            $validators['RelationListValueValidator']['selectionLimit'] = (int)$selectionLimit->getAttribute('value');
258
        }
259
    }
260
261
    /**
262
     * Returns the name of the index column in the attribute table.
263
     *
264
     * Returns the name of the index column the datatype uses, which is either
265
     * "sort_key_int" or "sort_key_string". This column is then used for
266
     * filtering and sorting for this type.
267
     *
268
     * @return string
269
     */
270
    public function getIndexColumn()
271
    {
272
        return 'sort_key_string';
273
    }
274
275
    /**
276
     * @param mixed[] $destinationContentIds
277
     *
278
     * @throws \Exception
279
     *
280
     * @return array
281
     */
282
    protected function getRelationXmlHashFromDB(array $destinationContentIds)
283
    {
284
        if (empty($destinationContentIds)) {
285
            return array();
286
        }
287
288
        $q = $this->db->createSelectQuery();
289
        $q
290
            ->select(
291
                $this->db->aliasedColumn($q, 'id', 'ezcontentobject'),
292
                $this->db->aliasedColumn($q, 'remote_id', 'ezcontentobject'),
293
                $this->db->aliasedColumn($q, 'current_version', 'ezcontentobject'),
294
                $this->db->aliasedColumn($q, 'contentclass_id', 'ezcontentobject'),
295
                $this->db->aliasedColumn($q, 'node_id', 'ezcontentobject_tree'),
296
                $this->db->aliasedColumn($q, 'parent_node_id', 'ezcontentobject_tree'),
297
                $this->db->aliasedColumn($q, 'identifier', 'ezcontentclass')
298
            )
299
            ->from($this->db->quoteTable('ezcontentobject'))
300
            ->leftJoin(
301
                $this->db->quoteTable('ezcontentobject_tree'),
302
                $q->expr->lAnd(
303
                    $q->expr->eq(
304
                        $this->db->quoteColumn('contentobject_id', 'ezcontentobject_tree'),
305
                        $this->db->quoteColumn('id', 'ezcontentobject')
306
                    ),
307
                    $q->expr->eq(
308
                        $this->db->quoteColumn('node_id', 'ezcontentobject_tree'),
309
                        $this->db->quoteColumn('main_node_id', 'ezcontentobject_tree')
310
                    )
311
                )
312
            )
313
            ->leftJoin(
314
                $this->db->quoteTable('ezcontentclass'),
315
                $q->expr->lAnd(
316
                    $q->expr->eq(
317
                        $this->db->quoteColumn('id', 'ezcontentclass'),
318
                        $this->db->quoteColumn('contentclass_id', 'ezcontentobject')
319
                    ),
320
                    $q->expr->eq(
321
                        $this->db->quoteColumn('version', 'ezcontentclass'),
322
                        $q->bindValue(ContentType::STATUS_DEFINED, null, PDO::PARAM_INT)
323
                    )
324
                )
325
            )
326
            ->where(
327
                $q->expr->in(
328
                    $this->db->quoteColumn('id', 'ezcontentobject'),
329
                    $destinationContentIds
330
                )
331
            );
332
        $stmt = $q->prepare();
333
        $stmt->execute();
334
335
        return $stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_GROUP);
336
    }
337
338
    /**
339
     * @return array
340
     */
341
    private static function dbAttributeMap()
342
    {
343
        return array(
344
            // 'identifier' => 'identifier',// not used
345
            'priority' => 'priority',
346
            // 'in-trash' => 'in_trash',// false by default and implies
347
            'contentobject-id' => 'ezcontentobject_id',
348
            'contentobject-version' => 'ezcontentobject_current_version',
349
            'node-id' => 'ezcontentobject_tree_node_id',
350
            'parent-node-id' => 'ezcontentobject_tree_parent_node_id',
351
            'contentclass-id' => 'ezcontentobject_contentclass_id',
352
            'contentclass-identifier' => 'ezcontentclass_identifier',
353
            // 'is-modified' => 'is_modified',// deprecated and not used
354
            'contentobject-remote-id' => 'ezcontentobject_remote_id',
355
        );
356
    }
357
}
358