Completed
Push — EZP-29891 ( 916cf6...0402ff )
by
unknown
16:53
created

RelationListConverter::toFieldValue()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 2
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
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
        $doc = new DOMDocument('1.0', 'utf-8');
121
        $root = $doc->createElement('related-objects');
122
        $doc->appendChild($root);
123
124
        $constraints = $doc->createElement('constraints');
125
        if (!empty($fieldSettings['selectionContentTypes'])) {
126
            foreach ($fieldSettings['selectionContentTypes'] as $typeIdentifier) {
127
                $allowedClass = $doc->createElement('allowed-class');
128
                $allowedClass->setAttribute('contentclass-identifier', $typeIdentifier);
129
                $constraints->appendChild($allowedClass);
130
                unset($allowedClass);
131
            }
132
        }
133
        $root->appendChild($constraints);
134
135
        $type = $doc->createElement('type');
136
        $type->setAttribute('value', 2); //Deprecated advance object relation list type, set since 4.x does
137
        $root->appendChild($type);
138
139
        $objectClass = $doc->createElement('object_class');
140
        $objectClass->setAttribute('value', ''); //Deprecated advance object relation class type, set since 4.x does
141
        $root->appendChild($objectClass);
142
143
        $selectionType = $doc->createElement('selection_type');
144 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...
145
            $selectionType->setAttribute('value', (int)$fieldSettings['selectionMethod']);
146
        } else {
147
            $selectionType->setAttribute('value', 0);
148
        }
149
        $root->appendChild($selectionType);
150
151
        $defaultLocation = $doc->createElement('contentobject-placement');
152
        if (!empty($fieldSettings['selectionDefaultLocation'])) {
153
            $defaultLocation->setAttribute('node-id', (int)$fieldSettings['selectionDefaultLocation']);
154
        }
155
        $root->appendChild($defaultLocation);
156
157
        $doc->appendChild($root);
158
        $storageDef->dataText5 = $doc->saveXML();
159
    }
160
161
    /**
162
     * Converts field definition data in $storageDef into $fieldDef.
163
     *
164
     * <?xml version="1.0" encoding="utf-8"?>
165
     * <related-objects>
166
     *   <constraints>
167
     *     <allowed-class contentclass-identifier="blog_post"/>
168
     *   </constraints>
169
     *   <type value="2"/>
170
     *   <selection_type value="1"/>
171
     *   <object_class value=""/>
172
     *   <contentobject-placement node-id="67"/>
173
     * </related-objects>
174
     *
175
     * <?xml version="1.0" encoding="utf-8"?>
176
     * <related-objects>
177
     *   <constraints/>
178
     *   <type value="2"/>
179
     *   <selection_type value="0"/>
180
     *   <object_class value=""/>
181
     *   <contentobject-placement/>
182
     * </related-objects>
183
     *
184
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
185
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
186
     */
187
    public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef)
188
    {
189
        // default settings
190
        $fieldDef->fieldTypeConstraints->fieldSettings = array(
191
            'selectionMethod' => 0,
192
            'selectionDefaultLocation' => null,
193
            'selectionContentTypes' => array(),
194
        );
195
196
        // default value
197
        $fieldDef->defaultValue = new FieldValue();
198
        $fieldDef->defaultValue->data = array('destinationContentIds' => array());
199
200
        if ($storageDef->dataText5 === null) {
201
            return;
202
        }
203
204
        // read settings from storage
205
        $fieldSettings = &$fieldDef->fieldTypeConstraints->fieldSettings;
206
        $dom = new DOMDocument('1.0', 'utf-8');
207
        if ($dom->loadXML($storageDef->dataText5) !== true) {
208
            return;
209
        }
210
211 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...
212
            $fieldSettings['selectionMethod'] = (int)$selectionType->item(0)->getAttribute('value');
213
        }
214
215
        if (
216
            ($defaultLocation = $dom->getElementsByTagName('contentobject-placement')) &&
217
            $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...
218
        ) {
219
            $fieldSettings['selectionDefaultLocation'] = (int)$defaultLocation->item(0)->getAttribute('node-id');
220
        }
221
222
        if (!($constraints = $dom->getElementsByTagName('constraints'))) {
223
            return;
224
        }
225
226
        foreach ($constraints->item(0)->getElementsByTagName('allowed-class') as $allowedClass) {
227
            $fieldSettings['selectionContentTypes'][] = $allowedClass->getAttribute('contentclass-identifier');
228
        }
229
    }
230
231
    /**
232
     * Returns the name of the index column in the attribute table.
233
     *
234
     * Returns the name of the index column the datatype uses, which is either
235
     * "sort_key_int" or "sort_key_string". This column is then used for
236
     * filtering and sorting for this type.
237
     *
238
     * @return bool
239
     */
240
    public function getIndexColumn()
241
    {
242
        return 'sort_key_string';
243
    }
244
245
    /**
246
     * @param mixed[] $destinationContentIds
247
     *
248
     * @throws \Exception
249
     *
250
     * @return array
251
     */
252
    protected function getRelationXmlHashFromDB(array $destinationContentIds)
253
    {
254
        if (empty($destinationContentIds)) {
255
            return array();
256
        }
257
258
        $q = $this->db->createSelectQuery();
259
        $q
260
            ->select(
261
                $this->db->aliasedColumn($q, 'id', 'ezcontentobject'),
262
                $this->db->aliasedColumn($q, 'remote_id', 'ezcontentobject'),
263
                $this->db->aliasedColumn($q, 'current_version', 'ezcontentobject'),
264
                $this->db->aliasedColumn($q, 'contentclass_id', 'ezcontentobject'),
265
                $this->db->aliasedColumn($q, 'node_id', 'ezcontentobject_tree'),
266
                $this->db->aliasedColumn($q, 'parent_node_id', 'ezcontentobject_tree'),
267
                $this->db->aliasedColumn($q, 'identifier', 'ezcontentclass')
268
            )
269
            ->from($this->db->quoteTable('ezcontentobject'))
270
            ->leftJoin(
271
                $this->db->quoteTable('ezcontentobject_tree'),
272
                $q->expr->lAnd(
273
                    $q->expr->eq(
274
                        $this->db->quoteColumn('contentobject_id', 'ezcontentobject_tree'),
275
                        $this->db->quoteColumn('id', 'ezcontentobject')
276
                    ),
277
                    $q->expr->eq(
278
                        $this->db->quoteColumn('node_id', 'ezcontentobject_tree'),
279
                        $this->db->quoteColumn('main_node_id', 'ezcontentobject_tree')
280
                    )
281
                )
282
            )
283
            ->leftJoin(
284
                $this->db->quoteTable('ezcontentclass'),
285
                $q->expr->lAnd(
286
                    $q->expr->eq(
287
                        $this->db->quoteColumn('id', 'ezcontentclass'),
288
                        $this->db->quoteColumn('contentclass_id', 'ezcontentobject')
289
                    ),
290
                    $q->expr->eq(
291
                        $this->db->quoteColumn('version', 'ezcontentclass'),
292
                        $q->bindValue(ContentType::STATUS_DEFINED, null, PDO::PARAM_INT)
293
                    )
294
                )
295
            )
296
            ->where(
297
                $q->expr->in(
298
                    $this->db->quoteColumn('id', 'ezcontentobject'),
299
                    $destinationContentIds
300
                )
301
            );
302
        $stmt = $q->prepare();
303
        $stmt->execute();
304
305
        return $stmt->fetchAll(PDO::FETCH_ASSOC | PDO::FETCH_GROUP);
306
    }
307
308
    /**
309
     * @return array
310
     */
311
    private static function dbAttributeMap()
312
    {
313
        return array(
314
            // 'identifier' => 'identifier',// not used
315
            'priority' => 'priority',
316
            // 'in-trash' => 'in_trash',// false by default and implies
317
            'contentobject-id' => 'ezcontentobject_id',
318
            'contentobject-version' => 'ezcontentobject_current_version',
319
            'node-id' => 'ezcontentobject_tree_node_id',
320
            'parent-node-id' => 'ezcontentobject_tree_parent_node_id',
321
            'contentclass-id' => 'ezcontentobject_contentclass_id',
322
            'contentclass-identifier' => 'ezcontentclass_identifier',
323
            // 'is-modified' => 'is_modified',// deprecated and not used
324
            'contentobject-remote-id' => 'ezcontentobject_remote_id',
325
        );
326
    }
327
}
328