Completed
Push — master ( 842fa1...d2977d )
by Andreas
17:14
created

midgard_admin_asgard_schemadb::create()   C

Complexity

Conditions 14
Paths 88

Size

Total Lines 80
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 16.5534

Importance

Changes 0
Metric Value
cc 14
eloc 54
nc 88
nop 1
dl 0
loc 80
ccs 39
cts 51
cp 0.7647
crap 16.5534
rs 6.2666
c 0
b 0
f 0

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 midgard.admin.asgard
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
use midcom\datamanager\schemadb;
10
11
/**
12
 * Helper class to create a DM schema from an object via reflection
13
 *
14
 * @package midgard.admin.asgard
15
 */
16
class midgard_admin_asgard_schemadb
17
{
18
    /**
19
     * The object we're working with
20
     *
21
     * @var midcom_core_dbaobject
22
     */
23
    private $_object;
24
25
    /**
26
     * Component config for Asgard
27
     *
28
     * @var midcom_helper_configuration
29
     */
30
    private $_config;
31
32
    /**
33
     * The schema in use
34
     *
35
     * @var array
36
     */
37
    private $schema;
38
39
    /**
40
     * Midgard reflection property instance for the current object's class.
41
     *
42
     * @var midgard_reflection_property
43
     */
44
    private $_reflector;
45
46
    /**
47
     * @var midcom_services_i18n_l10n
48
     */
49
    private $l10n;
50
51
    /**
52
     * Flag that controls if fields used for copying should be added
53
     *
54
     * @var boolean
55
     */
56
    public $add_copy_fields = false;
57
58 31
    public function __construct($object, $config, $type = null)
59
    {
60 31
        if ($type != null) {
61 6
            $this->_object = new $type();
62
        } else {
63 25
            $this->_object = $object;
64
        }
65
66 31
        if (!midcom::get()->dbclassloader->is_midcom_db_object($this->_object)) {
67 1
            $this->_object = midcom::get()->dbfactory->convert_midgard_to_midcom($this->_object);
68
        }
69 31
        $this->_reflector = new midgard_reflection_property(midcom_helper_reflector::resolve_baseclass($this->_object));
70 31
        $this->_config = $config;
71 31
        $this->l10n = midcom::get()->i18n->get_l10n('midgard.admin.asgard');
72 31
    }
73
74
    /**
75
     * Generates, loads and prepares the schema database.
76
     *
77
     * The operations are done on all available schemas within the DB.
78
     */
79 9
    public function create($include_fields)
80
    {
81 9
        $type = get_class($this->_object);
82 9
        $type_fields = $this->_object->get_properties();
83
84 9
        $this->schema = [
85
            'description' => 'object schema',
86
            'l10n_db'     => 'midgard.admin.asgard',
87
            'fields'      => []
88
        ];
89
90 9
        if ($component = midcom::get()->dbclassloader->get_component_for_class($type)) {
91 9
            $this->schema['l10n_db'] = $component;
92
        }
93
94 9
        if (!empty($include_fields)) {
95
            // Skip the fields that aren't requested, if inclusion list has been defined
96 2
            $type_fields = array_intersect($type_fields, (array) $include_fields);
97
        }
98
99 9
        $type_fields = array_filter($type_fields, [$this, '_filter_schema_fields']);
100
101 9
        usort($type_fields, [$this, 'sort_schema_fields']);
102
103
        // Iterate through object properties
104 9
        foreach ($type_fields as $key) {
105
            // Linked fields should use chooser
106 8
            if ($this->_reflector->is_link($key)) {
107 8
                $this->_add_linked_field($key);
108
                // Skip rest of processing
109 8
                continue;
110
            }
111
112 7
            $field_type = $this->_reflector->get_midgard_type($key);
113
            switch ($field_type) {
114 7
                case MGD_TYPE_GUID:
115 7
                case MGD_TYPE_STRING:
116 7
                    $this->_add_string_field($key, $type);
117 7
                    break;
118 7
                case MGD_TYPE_LONGTEXT:
119 7
                    $this->_add_longtext_field($key);
120 7
                    break;
121 7
                case MGD_TYPE_INT:
122 5
                case MGD_TYPE_UINT:
123 2
                    $this->_add_int_field($key);
124 2
                    break;
125 5
                case MGD_TYPE_FLOAT:
126
                    $this->schema['fields'][$key] = [
127
                        'title'       => $key,
128
                        'storage'     => $key,
129
                        'type'        => 'number',
130
                        'widget'      => 'text',
131
                    ];
132
                    break;
133 5
                case MGD_TYPE_BOOLEAN:
134 5
                    $this->schema['fields'][$key] = [
135 5
                        'title'       => $key,
136 5
                        'storage'     => $key,
137 5
                        'type'        => 'boolean',
138 5
                        'widget'      => 'checkbox',
139
                    ];
140 5
                    break;
141
                case MGD_TYPE_TIMESTAMP:
142
                    $this->schema['fields'][$key] = [
143
                        'title'       => $key,
144
                        'storage'     => $key,
145
                        'type' => 'date',
146
                        'widget' => 'jsdate',
147
                    ];
148 7
                    break;
149
            }
150
        }
151
152 9
        $this->_add_rcs_field();
153
154 9
        if ($this->add_copy_fields) {
155 2
            $this->_add_copy_fields();
156
        }
157
158 9
        return new schemadb(['default' => $this->schema]);
159
    }
160
161 8
    private function _filter_schema_fields($key)
162
    {
163 8
        if (   $key == 'metadata'
164 8
            || in_array($key, $this->_config->get('object_skip_fields'))) {
0 ignored issues
show
Bug introduced by
It seems like $this->_config->get('object_skip_fields') can also be of type false; however, parameter $haystack of in_array() does only seem to accept array, 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

164
            || in_array($key, /** @scrutinizer ignore-type */ $this->_config->get('object_skip_fields'))) {
Loading history...
165 7
            return false;
166
        }
167
168 8
        return true;
169
    }
170
171 7
    private function _add_string_field($key, $type)
172
    {
173 7
        if (   $key == 'component'
174 7
            && $type == midcom_db_topic::class) {
175 5
            $this->_add_component_dropdown($key);
176 5
            return;
177
        }
178
179
        // Special name handling, start by checking if given type is same as $this->_object and if not making a dummy copy (we're probably in creation mode then)
180 7
        if ($this->_object instanceof $type) {
181 7
            $name_obj = $this->_object;
182
        } else {
183
            $name_obj = new $type();
184
        }
185
186 7
        if ($key === midcom_helper_reflector::get_name_property($name_obj)) {
187 7
            $this->_add_name_field($key, $name_obj);
188 7
            return;
189
        }
190
191 7
        $this->schema['fields'][$key] = [
192 7
            'title'       => $key,
193 7
            'storage'     => $key,
194 7
            'type'        => 'text',
195 7
            'widget'      => 'text',
196
        ];
197 7
    }
198
199 9
    private function _add_rcs_field()
200
    {
201 9
        $this->schema['fields']['_rcs_message'] = [
202 9
            'title'       => $this->l10n->get('revision comment'),
203
            'storage'     => null,
204 9
            'type'        => 'rcsmessage',
205 9
            'widget'      => 'text',
206
            'start_fieldset' => [
207 9
                'title' => $this->l10n->get('revision'),
208 9
                'css_group' => 'rcs',
209
            ],
210 9
            'end_fieldset' => '',
211
        ];
212 9
    }
213
214 2
    private function _add_int_field($key)
215
    {
216 2
        if (in_array($key, ['start', 'end', 'added', 'date'])) {
217
            // We can safely assume that INT fields called start and end store unixtimes
218
            $this->schema['fields'][$key] = [
219
                'title'       => $key,
220
                'storage'     => $key,
221
                'type' => 'date',
222
                'type_config' => [
223
                    'storage_type' => 'UNIXTIME'
224
                    ],
225
                'widget' => 'jsdate',
226
            ];
227
        } else {
228 2
            $this->schema['fields'][$key] = [
229 2
                'title'       => $key,
230 2
                'storage'     => $key,
231 2
                'type'        => 'number',
232 2
                'widget'      => 'text',
233
            ];
234
        }
235 2
    }
236
237 7
    private function _add_longtext_field($key)
238
    {
239
        // Figure out nice size for the editing field
240
241 7
        $output_mode = '';
242 7
        $widget = 'textarea';
243 7
        $dm_type = 'text';
244
245
        switch ($key) {
246 7
            case 'content':
247 7
            case 'description':
248 7
                $height = 30;
249
250
                // Check the user preference and configuration
251 7
                if (   midgard_admin_asgard_plugin::get_preference('tinymce_enabled')
252 7
                    || (   midgard_admin_asgard_plugin::get_preference('tinymce_enabled') !== '0'
253 7
                        && $this->_config->get('tinymce_enabled'))) {
254 7
                    $widget = 'tinymce';
255
                }
256 7
                $output_mode = 'html';
257
258 7
                break;
259 7
            case 'value':
260 7
            case 'code':
261
                // These are typical "large" fields
262 5
                $height = 30;
263
264
                // Check the user preference and configuration
265 5
                if (   midgard_admin_asgard_plugin::get_preference('codemirror_enabled')
266 5
                    || (   midgard_admin_asgard_plugin::get_preference('codemirror_enabled') !== '0'
267 5
                        && $this->_config->get('codemirror_enabled'))) {
268 5
                    $widget = 'codemirror';
269
                }
270
271 5
                $dm_type = 'php';
272 5
                $output_mode = 'code';
273
274 5
                break;
275
276
            default:
277 7
                $height = 6;
278 7
                break;
279
        }
280
281 7
        $this->schema['fields'][$key] = [
282 7
            'title'       => $key,
283 7
            'storage'     => $key,
284 7
            'type'        => $dm_type,
285
            'type_config' => [
286 7
                'output_mode' => $output_mode,
287
            ],
288 7
            'widget'      => $widget,
289
            'widget_config' => [
290 7
                'height' => $height,
291 7
                'width' => '100%',
292
            ],
293
        ];
294 7
    }
295
296 7
    private function _add_name_field($key, midcom_core_dbaobject $name_obj)
297
    {
298 7
        $type_urlname_config = [];
299 7
        $allow_unclean_name_types = $this->_config->get('allow_unclean_names_for');
300 7
        foreach ($allow_unclean_name_types as $allow_unclean_name_types_type) {
301 7
            if ($name_obj->__object instanceof $allow_unclean_name_types_type) {
302
                $type_urlname_config['allow_unclean'] = true;
303 7
                break;
304
            }
305
        }
306
307
        // Enable generating the name from the title property
308 7
        $type_urlname_config['title_field'] = midcom_helper_reflector::get_title_property($name_obj);
309
310 7
        $this->schema['fields'][$key] = [
311 7
            'title'       => $key,
312 7
            'storage'     => $key,
313 7
            'type'        => 'urlname',
314 7
            'type_config' => $type_urlname_config,
315 7
            'widget'      => 'text',
316
        ];
317 7
    }
318
319 5
    private function _add_component_dropdown($key)
320
    {
321 5
        $components = ['' => ''];
322 5
        foreach (midcom::get()->componentloader->manifests as $manifest) {
323
            // Skip purecode components
324 5
            if ($manifest->purecode) {
325 5
                continue;
326
            }
327
328 5
            $components[$manifest->name] = midcom::get()->i18n->get_string($manifest->name, $manifest->name) . " ({$manifest->name})";
329
        }
330 5
        asort($components);
331
332 5
        $this->schema['fields'][$key] = [
333 5
            'title'       => $key,
334 5
            'storage'     => $key,
335 5
            'type'        => 'select',
336
            'type_config' => [
337 5
                'options' => $components,
338
            ],
339 5
            'widget'      => 'select',
340
        ];
341 5
    }
342
343 8
    private function _add_linked_field($key)
344
    {
345 8
        $linked_type = $this->_reflector->get_link_name($key);
346 8
        $field_type = $this->_reflector->get_midgard_type($key);
347
348 8
        if ($key == 'up') {
349 8
            $field_label = sprintf($this->l10n->get('under %s'), midgard_admin_asgard_plugin::get_type_label($linked_type));
350
        } else {
351 2
            $type_label = midgard_admin_asgard_plugin::get_type_label($linked_type);
352 2
            if (substr($type_label, 0, strlen($key)) == $key) {
353
                // Handle abbreviations like "lang" for "language"
354
                $field_label = $type_label;
355 2
            } elseif ($key == $type_label) {
356
                $field_label = $key;
357
            } else {
358 2
                $ref = midcom_helper_reflector::get($this->_object);
359 2
                $component_l10n = $ref->get_component_l10n();
360 2
                $field_label = sprintf($this->l10n->get('%s (%s)'), $component_l10n->get($key), $type_label);
361
            }
362
        }
363
364
        // Get the chooser widgets
365
        switch ($field_type) {
366 8
            case MGD_TYPE_UINT:
367
            case MGD_TYPE_STRING:
368
            case MGD_TYPE_GUID:
369 8
                $class = midcom::get()->dbclassloader->get_midcom_class_name_for_mgdschema_object($linked_type);
370 8
                if (!$class) {
371
                    break;
372
                }
373 8
                $this->schema['fields'][$key] = [
374 8
                    'title'       => $field_label,
375 8
                    'storage'     => $key,
376 8
                    'type'        => 'select',
377
                    'type_config' => [
378
                        'require_corresponding_option' => false,
379
                        'options' => [],
380
                        'allow_other' => true,
381
                        'allow_multiple' => false,
382
                    ],
383 8
                    'widget' => 'autocomplete',
384 8
                    'widget_config' => $this->build_autocomplete_config($key, $class, $linked_type),
385 8
                    'required' => (midgard_object_class::get_property_parent($this->_object->__mgdschema_class_name__) == $key)
386
                ];
387 8
                break;
388
        }
389 8
    }
390
391 8
    private function build_autocomplete_config($key, $class, $linked_type)
392
    {
393 8
        $reflector = midcom_helper_reflector::get($linked_type);
394 8
        $component = midcom::get()->dbclassloader->get_component_for_class($linked_type);
395 8
        $searchfields = $reflector->get_search_properties();
396 8
        $label_property = $reflector->get_label_property();
397 8
        $has_parent = !empty(midgard_object_class::get_property_parent($linked_type)) || !empty(midgard_object_class::get_property_up($linked_type));
398 8
        $result_headers = [];
399
400 8
        foreach ($searchfields as $field) {
401 8
            if ($field !== $label_property) {
402 8
                $result_headers[] = [
403 8
                    'name' => $field,
404 8
                    'title' => ucfirst($field),
405
                ];
406
            }
407
        }
408 8
        $searchfields[] = 'guid';
409
410
        return [
411 8
            'class' => $class,
412 8
            'component' => $component,
413 8
            'titlefield' => method_exists($class, 'get_label') ? null : $label_property,
414 8
            'id_field' => $this->_reflector->get_link_target($key),
415 8
            'searchfields' => $searchfields,
416 8
            'result_headers' => $result_headers,
417
            'orders' => [],
418
            'creation_mode_enabled' => true,
419 8
            'creation_handler' => midcom_connection::get_url('self') . "__mfa/asgard/object/create/chooser/{$linked_type}/",
420 8
            'creation_default_key' => $reflector->get_title_property(new $linked_type),
421 8
            'categorize_by_parent_label' => $has_parent
422
        ];
423
    }
424
425 2
    private function _add_copy_fields()
426
    {
427
        // Add switch for copying parameters
428 2
        $this->schema['fields']['parameters'] = [
429 2
            'title'       => $this->l10n->get('copy parameters'),
430
            'storage'     => null,
431 2
            'type'        => 'boolean',
432 2
            'widget'      => 'checkbox',
433
            'default'     => true,
434
        ];
435
436
        // Add switch for copying metadata
437 2
        $this->schema['fields']['metadata'] = [
438 2
            'title'       => $this->l10n->get('copy metadata'),
439
            'storage'     => null,
440 2
            'type'        => 'boolean',
441 2
            'widget'      => 'checkbox',
442
            'default'     => true,
443
        ];
444
445
        // Add switch for copying attachments
446 2
        $this->schema['fields']['attachments'] = [
447 2
            'title'       => $this->l10n->get('copy attachments'),
448
            'storage'     => null,
449 2
            'type'        => 'boolean',
450 2
            'widget'      => 'checkbox',
451
            'default'     => true,
452
        ];
453
454
        // Add switch for copying privileges
455 2
        $this->schema['fields']['privileges'] = [
456 2
            'title'       => $this->l10n->get('copy privileges'),
457
            'storage'     => null,
458 2
            'type'        => 'boolean',
459 2
            'widget'      => 'checkbox',
460
            'default'     => true,
461
        ];
462 2
    }
463
464 29
    private function _get_score($field)
465
    {
466 29
        $preferred_fields = $this->_config->get('object_preferred_fields');
467 29
        $timerange_fields = $this->_config->get('object_timerange_fields');
468 29
        $phone_fields = $this->_config->get('object_phone_fields');
469 29
        $address_fields = $this->_config->get('object_address_fields');
470 29
        $location_fields = $this->_config->get('object_location_fields');
471
472 29
        $score = 7;
473
474 29
        if ($this->_reflector->get_midgard_type($field) == MGD_TYPE_LONGTEXT) {
475 14
            $score = 1;
476 28
        } elseif (in_array($field, $preferred_fields)) {
0 ignored issues
show
Bug introduced by
It seems like $preferred_fields can also be of type false; however, parameter $haystack of in_array() does only seem to accept array, 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

476
        } elseif (in_array($field, /** @scrutinizer ignore-type */ $preferred_fields)) {
Loading history...
477 14
            $score = 0;
478 25
        } elseif ($this->_reflector->is_link($field)) {
479 11
            $score = 2;
480 23
        } elseif (in_array($field, $timerange_fields)) {
481 5
            $score = 3;
482 19
        } elseif (in_array($field, $phone_fields)) {
483 5
            $score = 4;
484 16
        } elseif (in_array($field, $address_fields)) {
485 5
            $score = 5;
486 12
        } elseif (in_array($field, $location_fields)) {
487
            $score = 6;
488
        }
489
490 29
        return $score;
491
    }
492
493 29
    public function sort_schema_fields($first, $second)
494
    {
495 29
        $score1 = $this->_get_score($first);
496 29
        $score2 = $this->_get_score($second);
497 29
        if ($score1 < $score2) {
498 13
            return -1;
499
        }
500 23
        if ($score1 > $score2) {
501 17
            return 1;
502
        }
503 13
        if (   $score1 < 3
504 13
            || $score1 > 6) {
505 10
            return strnatcmp($first, $second);
506
        }
507
        switch ($score1) {
508 3
            case 3:
509 1
                $type = 'timerange';
510 1
                break;
511 2
            case 4:
512 1
                $type = 'phone';
513 1
                break;
514 1
            case 5:
515 1
                $type = 'address';
516 1
                break;
517
            case 6:
518
                $type = 'location';
519
                break;
520
        }
521 3
        $fields = $this->_config->get('object_' . $type . '_fields');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $type does not seem to be defined for all execution paths leading up to this point.
Loading history...
522 3
        return (array_search($first, $fields) < array_search($second, $fields)) ? -1 : 1;
0 ignored issues
show
Bug introduced by
It seems like $fields can also be of type false; however, parameter $haystack of array_search() does only seem to accept array, 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

522
        return (array_search($first, /** @scrutinizer ignore-type */ $fields) < array_search($second, $fields)) ? -1 : 1;
Loading history...
523
    }
524
}
525