Completed
Push — master ( 0455f1...3050cd )
by André
102:45 queued 67:40
created

RelationListConverter   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 307
Duplicated Lines 2.61 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 8
loc 307
rs 10
c 0
b 0
f 0
wmc 27
lcom 1
cbo 7

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B toStorageValue() 0 37 5
B toFieldValue() 0 22 4
B toStorageFieldDefinition() 5 43 5
C toFieldDefinition() 3 43 8
A getIndexColumn() 0 4 1
A getRelationXmlHashFromDB() 0 55 2
A dbAttributeMap() 0 16 1

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