Passed
Push — master ( 40aec1...d8787f )
by Andreas
11:30
created

mnrelation   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Test Coverage

Coverage 71.95%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 76
c 2
b 1
f 0
dl 0
loc 150
ccs 59
cts 82
cp 0.7195
rs 9.92
wmc 31

7 Methods

Rating   Name   Duplication   Size   Complexity  
A load_objects() 0 25 6
A load() 0 9 4
B save() 0 24 9
A get_score() 0 7 2
A __construct() 0 16 1
B create_relation() 0 33 7
A get_master_foreign_key() 0 6 2
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
use midcom_core_dbaobject;
12
13
/**
14
 * Experimental storage class
15
 */
16
class mnrelation extends delayed
17
{
18 40
    public function __construct(midcom_core_dbaobject $object, array $config)
19
    {
20 40
        parent::__construct($object, $config);
21 40
        $defaults = [
22 40
            'sortable' => false,
23 40
            'allow_multiple' => true,
24 40
            'mapping_class_name' => null,
25 40
            'master_fieldname' => null,
26 40
            'member_fieldname' => null,
27 40
            'master_is_id' => false,
28 40
            'constraints' => [],
29 40
            'require_corresponding_option' => false,
30 40
            'sortable_sort_order' => 'DESC',
31 40
            'additional_fields' => [],
32 40
        ];
33 40
        $this->config['type_config'] = array_merge($defaults, $this->config['type_config']);
34
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39 7
    public function save()
40
    {
41 7
        $selection = array_flip((array) $this->value);
42 7
        $existing = $this->load_objects();
43 7
        $new = array_diff_key($selection, $existing);
44 7
        $delete = array_diff_key($existing, $selection);
45
46 7
        foreach (array_keys($new) as $key) {
47 2
            $this->create_relation($key, $this->get_score($key, $selection));
48
        }
49
50 7
        foreach ($delete as $key => $member) {
51
            if (!$member->delete()) {
52
                throw new midcom_error("Failed to delete member record for key {$key}: " . midcom_connection::get_error_string());
53
            }
54
        }
55 7
        if (!empty($this->config['type_config']['sortable'])) {
56
            foreach ($existing as $key => $member) {
57
                if (array_key_exists($key, $selection)) {
58
                    $score = $this->get_score($key, $selection);
59
                    if ($member->metadata->score != $score) {
60
                        $member->metadata->score = $score;
61
                        if (!$member->update()) {
62
                            throw new midcom_error("Failed to update member record for key {$key}: " . midcom_connection::get_error_string());
63
                        }
64
                    }
65
                }
66
            }
67
        }
68
    }
69
70 2
    private function get_score($key, array $selection) : int
71
    {
72
        // if the sort order is descending, the first element needs the highest score
73 2
        if ($this->config['type_config']['sortable'] == 'DESC') {
74
            return abs($selection[$key] - count($selection));
75
        }
76 2
        return $selection[$key];
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 38
    public function load()
83
    {
84 38
        if (!$this->get_master_foreign_key()) {
85 16
            return $this->config['type_config']['allow_multiple'] ? [] : null;
86
        }
87 24
        if ($this->config['type_config']['allow_multiple']) {
88 24
            return array_keys($this->load_objects());
89
        }
90
        return key($this->load_objects());
91
    }
92
93 2
    private function create_relation(string $member_key, int $score)
94
    {
95 2
        $member = new $this->config['type_config']['mapping_class_name']();
96 2
        $member->{$this->config['type_config']['master_fieldname']} = $this->get_master_foreign_key();
97 2
        $member->{$this->config['type_config']['member_fieldname']} = $member_key;
98
99 2
        foreach ($this->config['type_config']['additional_fields'] as $fieldname => $value) {
100
            // Determine what to do if using dot (.) in the additional fields,
101 1
            if (preg_match('/^(.+)\.(.+)$/', $fieldname, $regs)) {
102
                [, $domain, $key] = $regs;
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
    }
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 2
            $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 38
    private function get_master_foreign_key()
161
    {
162 38
        if ($this->config['type_config']['master_is_id']) {
163 32
            return $this->object->id;
164
        }
165 6
        return $this->object->guid;
166
    }
167
}
168