Passed
Push — master ( 5e64e2...da283d )
by Andreas
12:33
created

get_parent_property()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
crap 3.0261
rs 10
1
<?php
2
/**
3
 * @package midcom.helper.reflector
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * The Grand Unified Reflector, copying helper class
11
 *
12
 * @package midcom.helper.reflector
13
 */
14
class midcom_helper_reflector_copy
15
{
16
    use midcom_baseclasses_components_base;
0 ignored issues
show
introduced by
The trait midcom_baseclasses_components_base requires some properties which are not provided by midcom_helper_reflector_copy: $i18n, $head
Loading history...
17
18
    public ?midcom_core_dbaobject $target = null;
19
20
    /**
21
     * List of GUIDs of objects that shall not be copied
22
     */
23
    public array $exclude = [];
24
25
    /**
26
     * Switch for attachments
27
     */
28
    public bool $attachments = true;
29
30
    /**
31
     * Switch for parameters
32
     */
33
    public bool $parameters = true;
34
35
    /**
36
     * Switch for privileges
37
     */
38
    public bool $privileges = true;
39
40
    /**
41
     * Switch for metadata
42
     */
43
    public bool $metadata = true;
44
45
    /**
46
     * Copy the whole tree
47
     */
48
    public bool $recursive = true;
49
50
    /**
51
     * Metadata fields that shall be copied
52
     */
53
    public array $copy_metadata_fields = [
54
        'owner',
55
        'authors',
56
        'schedulestart',
57
        'scheduleend',
58
        'navnoentry',
59
        'hidden',
60
        'score',
61
    ];
62
63
    /**
64
     * Encountered errors
65
     */
66
    public array $errors = [];
67
68
    /**
69
     * Get the parent property for overriding it
70
     */
71 1
    public static function get_parent_property(midcom_core_dbaobject $object) : string
72
    {
73 1
        $parent = midgard_object_class::get_property_parent($object->__mgdschema_class_name__);
74 1
        if (!$parent) {
75 1
            $parent = midgard_object_class::get_property_up($object->__mgdschema_class_name__);
76
77 1
            if (!$parent) {
78
                throw new midcom_error('Failed to get the parent property for copying');
79
            }
80
        }
81
82 1
        return $parent;
83
    }
84
85
    /**
86
     * Copy an object tree. Both source and parent may be liberally filled. Source can be either
87
     * MgdSchema or MidCOM db object of the object and parent can be
88
     *
89
     * - MgdSchema object
90
     * - MidCOM db object
91
     * - left empty to copy as a parentless object
92
     *
93
     * This method is self-aware and will refuse to perform any infinite loops (e.g. to copy
94
     * itself to its descendant, copying itself again and again and again).
95
     *
96
     * Eventually this method will return the first root object that was created, i.e. the root
97
     * of the new tree.
98
     */
99
    public function copy_tree(midcom_core_dbaobject $source, midcom_core_dbaobject $parent) : ?midcom_core_dbaobject
100
    {
101
        // Copy the root object
102
        $root = $this->copy_object($source, $parent);
103
104
        if (!$root) {
105
            $this->errors[] = sprintf($this->_l10n->get('failed to copy object %s'), $source->guid);
106
            return null;
107
        }
108
109
        // Add the newly copied object to the exclusion list to prevent infinite loops
110
        $this->exclude[] = $source->guid;
111
112
        // Loop through the children and copy them to their corresponding parents
113
        foreach (midcom_helper_reflector_tree::get_child_objects($source) as $children) {
114
            // Get the children of each type
115
            foreach ($children as $child) {
116
                // Skip the excluded child
117
                if (!in_array($child->guid, $this->exclude)) {
118
                    $this->copy_tree($child, $root);
119
                }
120
            }
121
        }
122
123
        // Return the newly created root object
124
        return $root;
125
    }
126
127
    /**
128
     * Copy an object
129
     */
130 1
    public function copy_object(midcom_core_dbaobject $source, ?midcom_core_dbaobject $parent, array $defaults = []) : ?midcom_core_dbaobject
131
    {
132
        // Duplicate the object
133 1
        $class_name = get_class($source);
134 1
        $target = new $class_name();
135
136
        // Copy the object properties
137 1
        foreach (midcom_helper_reflector::get_object_fieldnames($source) as $property) {
138
            // Skip certain fields
139 1
            if (!preg_match('/^(_|metadata|guid|id)/', $property)) {
140 1
                $target->$property = $source->$property;
141
            }
142
        }
143
144
        // Override with defaults
145 1
        foreach ($defaults as $name => $value) {
146
            $target->$name = $value;
147
        }
148
149 1
        if ($this->recursive) {
150
            $parent_property = self::get_parent_property($source);
151
152
            // Copy the link to parent
153
            if (!empty($parent->guid)) {
154
                // @TODO: Is there a sure way to determine if the parent is
155
                // GUID or is it ID? If so, please change it here.
156
                $parent_key = (is_string($source->$parent_property)) ? 'guid' : 'id';
157
                $target->$parent_property = $parent->$parent_key;
158
            } else {
159
                $target->$parent_property = (is_string($source->$parent_property)) ? '' : 0;
160
            }
161
        }
162 1
        if ($name_property = midcom_helper_reflector::get_name_property($target)) {
163 1
            $resolver = new midcom_helper_reflector_nameresolver($target);
164 1
            $target->$name_property = $resolver->generate_unique_name();
165
        }
166
167
        // This needs to be here, otherwise it will be overridden
168 1
        $target->allow_name_catenate = true;
169 1
        if (!$target->create()) {
170
            $this->errors[] = $this->_l10n->get('failed to create object: ' . midcom_connection::get_error_string());
171
            return null;
172
        }
173
174 1
        foreach (['parameters', 'metadata', 'attachments', 'privileges'] as $type) {
175 1
            if (!$this->_copy_data($type, $source, $target)) {
176
                return null;
177
            }
178
        }
179
180 1
        return $target;
181
    }
182
183
    /**
184
     * Copy object data
185
     */
186 1
    private function _copy_data(string $type, midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
187
    {
188 1
        if ($this->$type) {
189 1
            $method = 'copy_' . $type;
190 1
            if (!$this->$method($source, $target)) {
191
                $this->errors[] = $this->_l10n->get('failed to copy ' . $type);
192
                return false;
193
            }
194
        }
195
196 1
        return true;
197
    }
198
199
    /**
200
     * Copy parameters for the object
201
     */
202 2
    public function copy_parameters(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
203
    {
204
        // Loop through the parameters
205 2
        foreach ($source->list_parameters() as $domain => $parameters) {
206 1
            foreach ($parameters as $name => $value) {
207 1
                if (!$target->set_parameter($domain, $name, $value)) {
208
                    $this->errors[] = sprintf($this->_l10n->get('failed to copy parameters from %s to %s'), $source->guid, $target->guid);
209
                    return false;
210
                }
211
            }
212
        }
213
214 2
        return true;
215
    }
216
217
    /**
218
     * Copy metadata for the object
219
     */
220 1
    public function copy_metadata(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
221
    {
222 1
        foreach ($this->copy_metadata_fields as $property) {
223 1
            $target->metadata->$property = $source->metadata->$property;
224
        }
225
226 1
        if ($target->update()) {
227 1
            return true;
228
        }
229
230
        $this->errors[] = sprintf($this->_l10n->get('failed to copy metadata from %s to %s'), $source->guid, $target->guid);
231
        return false;
232
    }
233
234
    /**
235
     * Copy attachments
236
     */
237 1
    public function copy_attachments(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
238
    {
239 1
        $defaults = [
240 1
            'parentguid' => $target->guid,
241 1
        ];
242
243 1
        foreach ($source->list_attachments() as $attachment) {
244
            $this->copy_object($attachment, $target, $defaults);
245
        }
246
247 1
        return true;
248
    }
249
250
    /**
251
     * Copy privileges
252
     */
253 1
    public function copy_privileges(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
254
    {
255 1
        $qb = midcom_core_privilege_db::new_query_builder();
256 1
        $qb->add_constraint('objectguid', '=', $source->guid);
257
258 1
        foreach ($qb->execute() as $privilege) {
259
            $new = new midcom_core_privilege_db();
260
            $new->objectguid = $target->guid;
261
262
            $new->classname = $privilege->classname;
263
            $new->privilegename = $privilege->privilegename;
264
            $new->value = $privilege->value;
265
            $new->assignee = $privilege->assignee;
266
267
            if (!$new->create()) {
268
                $this->errors[] = 'privilege creation failed';
269
                return false;
270
            }
271
        }
272
273 1
        return true;
274
    }
275
276
    /**
277
     * Dispatches the copy command according to the attributes set
278
     */
279 1
    public function execute(midcom_core_dbaobject $source) : ?midcom_core_dbaobject
280
    {
281 1
        if ($this->recursive) {
282
            // Disable execution timeout and memory limit, this can be very intensive
283
            midcom::get()->disable_limits();
284
285
            $new_root_object = $this->copy_tree($source, $this->target);
0 ignored issues
show
Bug introduced by
It seems like $this->target can also be of type null; however, parameter $parent of midcom_helper_reflector_copy::copy_tree() does only seem to accept midcom_core_dbaobject, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
            $new_root_object = $this->copy_tree($source, /** @scrutinizer ignore-type */ $this->target);
Loading history...
286
        } else {
287 1
            $new_root_object = $this->copy_object($source, $this->target);
288
        }
289
290 1
        if (empty($new_root_object->guid)) {
291
            $this->errors[] = $this->_l10n->get('failed to get the new root object');
292
            return null;
293
        }
294
295 1
        return $new_root_object;
296
    }
297
}
298