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

midcom_helper_reflector_copy::copy_object()   C

Complexity

Conditions 12
Paths 240

Size

Total Lines 51
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 18.7184

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 25
c 2
b 0
f 0
nc 240
nop 3
dl 0
loc 51
ccs 16
cts 25
cp 0.64
crap 18.7184
rs 5.6333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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