Passed
Push — master ( f61cc5...ab3fce )
by Andreas
33:36
created

mnrelation::load()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.074

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 4
nop 0
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
crap 4.074
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright CONTENT CONTROL GmbH, http://www.contentcontrol-berlin.de
4
 */
5
6
namespace midcom\datamanager\storage;
7
8
use midcom;
9
use midcom_error;
10
use midcom_connection;
11
12
/**
13
 * Experimental storage class
14
 */
15
class mnrelation extends delayed
16
{
17 40
    public function __construct($object, $config)
18
    {
19 40
        parent::__construct($object, $config);
20
        $defaults = [
21 40
            'sortable' => false,
22
            'allow_multiple' => true,
23
            'mapping_class_name' => null,
24
            'master_fieldname' => null,
25
            'member_fieldname' => null,
26
            'master_is_id' => false,
27
            'constraints' => [],
28
            'require_corresponding_option' => false,
29
            'sortable_sort_order' => 'DESC',
30
            'additional_fields' => [],
31
        ];
32 40
        $this->config['type_config'] = array_merge($defaults, $this->config['type_config']);
33 40
    }
34
35
    /**
36
     * {@inheritdoc}
37
     */
38 7
    public function save()
39
    {
40 7
        $selection = array_flip((array) $this->value);
41 7
        $existing = $this->load_objects();
42 7
        $new = array_diff_key($selection, $existing);
43 7
        $delete = array_diff_key($existing, $selection);
44
45 7
        foreach (array_keys($new) as $key) {
46 2
            $this->create_relation($key, $this->get_score($key, $selection));
47
        }
48
49 7
        foreach ($delete as $key => $member) {
50
            if (!$member->delete()) {
51
                throw new midcom_error("Failed to delete member record for key {$key}: " . midcom_connection::get_error_string());
52
            }
53
        }
54 7
        if (!empty($this->config['type_config']['sortable'])) {
55
            foreach ($existing as $key => $member) {
56
                if (array_key_exists($key, $selection)) {
57
                    $score = $this->get_score($key, $selection);
58
                    if ($member->metadata->score != $score) {
59
                        $member->metadata->score = $score;
60
                        if (!$member->update()) {
61
                            throw new midcom_error("Failed to update member record for key {$key}: " . midcom_connection::get_error_string());
62
                        }
63
                    }
64
                }
65
            }
66
        }
67 7
    }
68
69 2
    private function get_score($key, array $selection) : int
70
    {
71
        // if the sort order is descending, the first element needs the highest score
72 2
        if ($this->config['type_config']['sortable'] == 'DESC') {
73
            return abs($selection[$key] - count($selection));
74
        }
75 2
        return $selection[$key];
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 38
    public function load()
82
    {
83 38
        if (!$this->get_master_foreign_key()) {
84 16
            return $this->config['type_config']['allow_multiple'] ? [] : null;
85
        }
86 23
        if ($this->config['type_config']['allow_multiple']) {
87 23
            return array_keys($this->load_objects());
88
        }
89
        return key($this->load_objects());
90
    }
91
92 2
    private function create_relation($member_key, int $score)
93
    {
94 2
        $member = new $this->config['type_config']['mapping_class_name']();
95 2
        $member->{$this->config['type_config']['master_fieldname']} = $this->get_master_foreign_key();
96 2
        $member->{$this->config['type_config']['member_fieldname']} = $member_key;
97
98 2
        foreach ($this->config['type_config']['additional_fields'] as $fieldname => $value) {
99
            // Determine what to do if using dot (.) in the additional fields,
100 1
            if (preg_match('/^(.+)\.(.+)$/', $fieldname, $regs)) {
101
                $domain = $regs[1];
102
                $key = $regs[2];
103
104
                // Determine what should be done with conjunction
105
                switch ($domain) {
106
                    case 'metadata':
107
                        $member->metadata->$key = $value;
108
                        break;
109
110
                    case 'parameter':
111
                        $member->set_parameter('midcom.helper.datamanager2.mnrelation', $key, $value);
112
                        break;
113
                }
114
115
                continue;
116
            }
117
118 1
            $member->{$fieldname} = $value;
119
        }
120 2
        if (!empty($this->config['type_config']['sortable'])) {
121
            $member->metadata->score = $score;
122
        }
123
124 2
        if (!$member->create()) {
125
            throw new midcom_error("Failed to create a new member record for key {$member_key}: " . midcom_connection::get_error_string());
126
        }
127 2
    }
128
129 25
    private function load_objects() : array
130
    {
131 25
        $qb = midcom::get()->dbfactory->new_query_builder($this->config['type_config']['mapping_class_name']);
132 25
        $qb->add_constraint($this->config['type_config']['master_fieldname'], '=', $this->get_master_foreign_key());
133
134 25
        if (   $this->config['type_config']['sortable']
135 25
            && preg_match('/^(ASC|DESC)/i', $this->config['type_config']['sortable_sort_order'], $regs)) {
136
            $order = strtoupper($regs[1]);
137
            $qb->add_order('metadata.score', $order);
138
        }
139
140 25
        foreach ($this->config['type_config']['constraints'] as $constraint) {
141 9
            $qb->add_constraint($this->config['type_config']['member_fieldname'] . '.' . $constraint['field'], $constraint['op'], $constraint['value']);
142
        }
143
144 25
        foreach ($this->config['type_config']['additional_fields'] as $fieldname => $value) {
145 8
            $qb->add_constraint($fieldname, '=', $value);
146
        }
147
148 25
        $indexed = [];
149 25
        $results = $qb->execute();
150 25
        foreach ($results as $result) {
151 1
            $indexed[$result->{$this->config['type_config']['member_fieldname']}] = $result;
152
        }
153 25
        return $indexed;
154
    }
155
156
    /**
157
     * Returns the foreign key of the master object. This is either the ID or the GUID of
158
     * the master object, depending on the $master_is_id member.
159
     *
160
     * @var string Foreign key for the master field in the mapping table.
161
     */
162 38
    private function get_master_foreign_key()
163
    {
164 38
        if ($this->config['type_config']['master_is_id']) {
165 32
            return $this->object->id;
166
        }
167 6
        return $this->object->guid;
168
    }
169
}
170