Passed
Push — master ( c41cb1...84b849 )
by Andreas
10:10
created

midcom_helper_reflector_copy::copy_object()   C

Complexity

Conditions 13
Paths 240

Size

Total Lines 51
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 18.5377

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 25
c 1
b 0
f 0
nc 240
nop 3
dl 0
loc 51
ccs 17
cts 25
cp 0.68
crap 18.5377
rs 5.2833

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
        return midgard_object_class::get_property_parent($object->__mgdschema_class_name__)
74 1
            ?? midgard_object_class::get_property_up($object->__mgdschema_class_name__);
75
    }
76
77
    /**
78
     * Copy an object tree. Both source and parent may be liberally filled. Source can be either
79
     * MgdSchema or MidCOM db object of the object and parent can be
80
     *
81
     * - MgdSchema object
82
     * - MidCOM db object
83
     * - left empty to copy as a parentless object
84
     *
85
     * This method is self-aware and will refuse to perform any infinite loops (e.g. to copy
86
     * itself to its descendant, copying itself again and again and again).
87
     *
88
     * Eventually this method will return the first root object that was created, i.e. the root
89
     * of the new tree.
90
     */
91
    public function copy_tree(midcom_core_dbaobject $source, ?midcom_core_dbaobject $parent) : ?midcom_core_dbaobject
92
    {
93
        // Copy the root object
94
        $root = $this->copy_object($source, $parent);
95
96
        if (!$root) {
97
            $this->errors[] = sprintf($this->_l10n->get('failed to copy object %s'), $source->guid);
98
            return null;
99
        }
100
101
        // Add the newly copied object to the exclusion list to prevent infinite loops
102
        $this->exclude[] = $source->guid;
103
104
        // Loop through the children and copy them to their corresponding parents
105
        foreach (midcom_helper_reflector_tree::get_child_objects($source) as $children) {
106
            // Get the children of each type
107
            foreach ($children as $child) {
108
                // Skip the excluded child
109
                if (!in_array($child->guid, $this->exclude)) {
110
                    $this->copy_tree($child, $root);
111
                }
112
            }
113
        }
114
115
        // Return the newly created root object
116
        return $root;
117
    }
118
119
    /**
120
     * Copy an object
121
     */
122 1
    public function copy_object(midcom_core_dbaobject $source, ?midcom_core_dbaobject $parent, array $defaults = []) : ?midcom_core_dbaobject
123
    {
124
        // Duplicate the object
125 1
        $class_name = $source::class;
126 1
        $target = new $class_name();
127
128
        // Copy the object properties
129 1
        foreach (midcom_helper_reflector::get_object_fieldnames($source) as $property) {
130
            // Skip certain fields
131 1
            if (!preg_match('/^(_|metadata|guid|id)/', $property)) {
132 1
                $target->$property = $source->$property;
133
            }
134
        }
135
136
        // Override with defaults
137 1
        foreach ($defaults as $name => $value) {
138
            $target->$name = $value;
139
        }
140
141 1
        if (   $this->recursive
142 1
            && $parent_property = self::get_parent_property($source)) {
143
144
            // Copy the link to parent
145
            if (!empty($parent->guid)) {
146
                // @TODO: Is there a sure way to determine if the parent is
147
                // GUID or is it ID? If so, please change it here.
148
                $parent_key = (is_string($source->$parent_property)) ? 'guid' : 'id';
149
                $target->$parent_property = $parent->$parent_key;
150
            } else {
151
                $target->$parent_property = (is_string($source->$parent_property)) ? '' : 0;
152
            }
153
        }
154 1
        if ($name_property = midcom_helper_reflector::get_name_property($target)) {
155 1
            $resolver = new midcom_helper_reflector_nameresolver($target);
156 1
            $target->$name_property = $resolver->generate_unique_name();
157
        }
158
159
        // This needs to be here, otherwise it will be overridden
160 1
        $target->allow_name_catenate = true;
161 1
        if (!$target->create()) {
162
            $this->errors[] = $this->_l10n->get('failed to create object: ' . midcom_connection::get_error_string());
163
            return null;
164
        }
165
166 1
        foreach (['parameters', 'metadata', 'attachments', 'privileges'] as $type) {
167 1
            if (!$this->_copy_data($type, $source, $target)) {
168
                return null;
169
            }
170
        }
171
172 1
        return $target;
173
    }
174
175
    /**
176
     * Copy object data
177
     */
178 1
    private function _copy_data(string $type, midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
179
    {
180 1
        if ($this->$type) {
181 1
            $method = 'copy_' . $type;
182 1
            if (!$this->$method($source, $target)) {
183
                $this->errors[] = $this->_l10n->get('failed to copy ' . $type);
184
                return false;
185
            }
186
        }
187
188 1
        return true;
189
    }
190
191
    /**
192
     * Copy parameters for the object
193
     */
194 2
    public function copy_parameters(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
195
    {
196
        // Loop through the parameters
197 2
        foreach ($source->list_parameters() as $domain => $parameters) {
198 1
            foreach ($parameters as $name => $value) {
199 1
                if (!$target->set_parameter($domain, $name, $value)) {
200
                    $this->errors[] = sprintf($this->_l10n->get('failed to copy parameters from %s to %s'), $source->guid, $target->guid);
201
                    return false;
202
                }
203
            }
204
        }
205
206 2
        return true;
207
    }
208
209
    /**
210
     * Copy metadata for the object
211
     */
212 1
    public function copy_metadata(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
213
    {
214 1
        foreach ($this->copy_metadata_fields as $property) {
215 1
            $target->metadata->$property = $source->metadata->$property;
216
        }
217
218 1
        if ($target->update()) {
219 1
            return true;
220
        }
221
222
        $this->errors[] = sprintf($this->_l10n->get('failed to copy metadata from %s to %s'), $source->guid, $target->guid);
223
        return false;
224
    }
225
226
    /**
227
     * Copy attachments
228
     */
229 1
    public function copy_attachments(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
230
    {
231 1
        $defaults = [
232 1
            'parentguid' => $target->guid,
233 1
        ];
234
235 1
        foreach ($source->list_attachments() as $attachment) {
236
            $this->copy_object($attachment, $target, $defaults);
237
        }
238
239 1
        return true;
240
    }
241
242
    /**
243
     * Copy privileges
244
     */
245 1
    public function copy_privileges(midcom_core_dbaobject $source, midcom_core_dbaobject $target) : bool
246
    {
247 1
        $qb = midcom_core_privilege_db::new_query_builder();
248 1
        $qb->add_constraint('objectguid', '=', $source->guid);
249
250 1
        foreach ($qb->execute() as $privilege) {
251
            $new = new midcom_core_privilege_db();
252
            $new->objectguid = $target->guid;
253
254
            $new->classname = $privilege->classname;
255
            $new->privilegename = $privilege->privilegename;
256
            $new->value = $privilege->value;
257
            $new->assignee = $privilege->assignee;
258
259
            if (!$new->create()) {
260
                $this->errors[] = 'privilege creation failed';
261
                return false;
262
            }
263
        }
264
265 1
        return true;
266
    }
267
268
    /**
269
     * Dispatches the copy command according to the attributes set
270
     */
271 1
    public function execute(midcom_core_dbaobject $source) : ?midcom_core_dbaobject
272
    {
273 1
        if ($this->recursive) {
274
            // Disable execution timeout and memory limit, this can be very intensive
275
            midcom::get()->disable_limits();
276
277
            $new_root_object = $this->copy_tree($source, $this->target);
278
        } else {
279 1
            $new_root_object = $this->copy_object($source, $this->target);
280
        }
281
282 1
        if (empty($new_root_object->guid)) {
283
            $this->errors[] = $this->_l10n->get('failed to get the new root object');
284
            return null;
285
        }
286
287 1
        return $new_root_object;
288
    }
289
}
290