Test Failed
Push — develop ( 39822a...f2441f )
by Paul
10:43
created

ElementControlsTrait   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 424
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 40
eloc 240
c 1
b 0
f 1
dl 0
loc 424
rs 9.2

15 Methods

Rating   Name   Duplication   Size   Complexity  
A bdProcessControlGroups() 0 16 3
A contentControls() 0 17 3
A bdControlText() 0 8 1
A ssrArgs() 0 34 4
A bdControlCheckbox() 0 22 2
B bdProcessConfig() 0 29 6
A bdFindGroupSlug() 0 5 1
A bdControlSelect() 0 42 4
B bdContentControlPaths() 0 27 8
A bdTransformDropdownItems() 0 7 1
A bdControlNumber() 0 13 1
A bdControl() 0 11 1
A bdSections() 0 34 1
A defaultProperties() 0 16 3
A bdSectionGroups() 0 73 1

How to fix   Complexity   

Complex Class

Complex classes like ElementControlsTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ElementControlsTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Integrations\Breakdance;
4
5
use GeminiLabs\SiteReviews\Arguments;
6
use GeminiLabs\SiteReviews\Contracts\ShortcodeContract;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Helpers\Arr;
9
use GeminiLabs\SiteReviews\Integrations\Breakdance\Defaults\ControlDefaults;
10
use GeminiLabs\SiteReviews\Integrations\Breakdance\Defaults\SectionDefaults;
11
12
use function Breakdance\Elements\c;
0 ignored issues
show
introduced by
The function Breakdance\Elements\c was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
13
14
trait ElementControlsTrait
15
{
16
    public static function bdSectionGroups(): array
17
    {
18
        $config = static::bdShortcode()->settings();
19
        $groups = [ // order is intentional
20
            'rating_group' => glsr(SectionDefaults::class)->merge([
21
                'children' => [
22
                    static::bdControl([
23
                        'slug' => 'rating_field_notice',
24
                        'options' => [
25
                            'alertBoxOptions' => [
26
                                'style' => 'info',
27
                                'content' => $config['rating_field']['description'] ?? '',
28
                            ],
29
                            'layout' => 'vertical',
30
                            'type' => 'alert_box',
31
                        ],
32
                    ]),
33
                ],
34
                'fields' => ['rating', 'rating_field'],
35
                'label' => esc_html_x('Rating Options', 'admin-text', 'site-reviews'),
36
                'options' => [
37
                    'sectionOptions' => [
38
                        'type' => 'popout',
39
                    ],
40
                    'type' => 'section',
41
                ],
42
            ]),
43
            'pagination_group' => glsr(SectionDefaults::class)->merge([
44
                'fields' => ['display', 'pagination'],
45
                'label' => esc_html_x('Pagination Options', 'admin-text', 'site-reviews'),
46
                'options' => [
47
                    'sectionOptions' => [
48
                        'type' => 'popout',
49
                    ],
50
                    'type' => 'section',
51
                ],
52
            ]),
53
            'schema_group' => glsr(SectionDefaults::class)->merge([
54
                'children' => [
55
                    static::bdControl([
56
                        'slug' => 'schema_notice',
57
                        'options' => [
58
                            'alertBoxOptions' => [
59
                                'style' => 'warning',
60
                                'content' => $config['schema']['description'] ?? '',
61
                            ],
62
                            'layout' => 'vertical',
63
                            'type' => 'alert_box',
64
                        ],
65
                    ]),
66
                ],
67
                'fields' => ['schema'],
68
                'label' => esc_html_x('Schema Options', 'admin-text', 'site-reviews'),
69
                'options' => [
70
                    'sectionOptions' => [
71
                        'type' => 'popout',
72
                    ],
73
                    'type' => 'section',
74
                ],
75
            ]),
76
            'hide_group' => glsr(SectionDefaults::class)->merge([
77
                'fields' => ['hide'],
78
                'label' => esc_html_x('Hide Options', 'admin-text', 'site-reviews'),
79
                'options' => [
80
                    'sectionOptions' => [
81
                        'type' => 'popout',
82
                    ],
83
                    'type' => 'section',
84
                ],
85
            ]),
86
        ];
87
        $groups = glsr()->filterArray('breakdance/groups', $groups, static::class);
88
        return $groups;
89
    }
90
91
    public static function bdSections(): array
92
    {
93
        $config = static::bdShortcode()->settings();
94
        $sections = [ // order is intentional
95
            'general' => glsr(SectionDefaults::class)->merge([
96
                'label' => esc_html_x('General', 'admin-text', 'site-reviews'),
97
                'options' => [
98
                    'layout' => 'vertical',
99
                    'type' => 'section',
100
                ],
101
            ]),
102
            'advanced' => glsr(SectionDefaults::class)->merge([
103
                'children' => [
104
                    static::bdControl([
105
                        'slug' => 'reviews_id_notice',
106
                        'options' => [
107
                            'alertBoxOptions' => [
108
                                'style' => 'default',
109
                                'content' => $config['reviews_id']['description'] ?? '',
110
                            ],
111
                            'layout' => 'vertical',
112
                            'type' => 'alert_box',
113
                        ],
114
                    ]),
115
                ],
116
                'label' => esc_html_x('Advanced', 'admin-text', 'site-reviews'),
117
                'options' => [
118
                    'layout' => 'vertical',
119
                    'type' => 'section',
120
                ],
121
            ]),
122
        ];
123
        $sections = glsr()->filterArray('breakdance/sections', $sections, static::class);
124
        return $sections;
125
    }
126
127
    abstract public static function bdShortcode(): ShortcodeContract;
128
129
    /**
130
     * @return array[]
131
     */
132
    public static function contentControls()
133
    {
134
        $config = static::bdProcessConfig();
135
        $controls = [];
136
        foreach ($config as $slug => $section) {
137
            $children = array_filter($section['children']);
138
            if (empty($children)) {
139
                continue;
140
            }
141
            $controls[] = static::bdControl([
142
                'children' => $section['children'],
143
                'label' => $section['label'],
144
                'options' => $section['options'],
145
                'slug' => $slug,
146
            ]);
147
        }
148
        return $controls;
149
    }
150
151
    /**
152
     * This must return false if the element has no default properties
153
     * otherwise SSR will not trigger when control values are changed.
154
     *
155
     * @return array|false
156
     */
157
    public static function defaultProperties()
158
    {
159
        $config = static::bdShortcode()->settings();
160
        $paths = array_keys(Arr::flatten(static::bdContentControlPaths()));
161
        $props = array_combine($paths,
162
            array_map(fn ($item) => substr($item, strrpos($item, '.') + 1), $paths)
163
        );
164
        $defaults = [];
165
        foreach ($props as $path => $slug) {
166
            $defaults[$path] = $config[$slug]['default'] ?? null;
167
        }
168
        $defaults = array_filter($defaults, fn ($value) => !is_null($value));
169
        if (empty($defaults)) {
170
            return false;
171
        }
172
        return Arr::unflatten($defaults);
173
    }
174
175
    public static function ssrArgs(array $data): array
176
    {
177
        $content = Arr::getAs('array', $data, 'content');
178
        $sections = array_keys(static::bdSections());
179
        $groups = array_keys(static::bdSectionGroups());
180
        $controls = array_merge(...array_map(fn ($key) => $content[$key] ?? [], $sections));
181
        $ungrouped = array_filter($controls, fn ($val) => !is_array($val) || wp_is_numeric_array($val));
182
        $grouped = array_map(fn ($groupKey) => $controls[$groupKey] ?? [], $groups);
183
        $args = array_merge($ungrouped, ...$grouped);
184
        $hide = Arr::getAs('array', $args, 'hide');
185
        $args['hide'] = array_keys(array_filter($hide));
186
        if (is_array($args['assigned_posts'] ?? null)) {
187
            $replacements = [ // the post_chooser control requires integer keys
188
                -10 => 'post_id',
189
                -20 => 'parent_id',
190
            ];
191
            $args['assigned_posts'] = array_map(
192
                fn ($value) => $replacements[$value] ?? $value,
193
                $args['assigned_posts']
194
            );
195
        }
196
        if (is_array($args['assigned_users'] ?? null)) {
197
            $replacements = [ // the post_chooser control requires integer keys
198
                -10 => 'user_id',
199
                -20 => 'author_id',
200
                -30 => 'profile_id',
201
            ];
202
            $args['assigned_users'] = array_map(
203
                fn ($value) => $replacements[$value] ?? $value,
204
                $args['assigned_users']
205
            );
206
        }
207
        $args = glsr()->filterArray('breakdance/ssr', $args, $data);
208
        return $args;
209
    }
210
211
    /**
212
     * @return string[]
213
     */
214
    public static function bdContentControlPaths(): array
215
    {
216
        $config = static::bdShortcode()->settings();
217
        $props = [];
218
        foreach (static::contentControls() as $section) {
219
            $slug = $section['slug'];
220
            foreach ($section['children'] as $child) {
221
                if (!empty($child['children'])) {
222
                    continue; // this is a section group
223
                }
224
                $field = $child['slug'];
225
                if (!array_key_exists($field, $config)) {
226
                    continue;
227
                }
228
                $props['content'][$slug][$field] = $config[$field]['default'] ?? null;
229
            }
230
        }
231
        $groupedFields = wp_list_pluck(static::bdSectionGroups(), 'fields');
232
        foreach ($groupedFields as $slug => $fields) {
233
            foreach ($fields as $field) {
234
                if (!array_key_exists($field, $config)) {
235
                    continue;
236
                }
237
                $props['content']['general'][$slug][$field] = $config[$field]['default'] ?? null;
238
            }
239
        }
240
        return $props;
241
    }
242
243
    protected static function bdControl(array $args): array
244
    {
245
        $args = glsr(ControlDefaults::class)->merge($args);
246
        return c(
0 ignored issues
show
Bug introduced by
Are you sure the usage of c($args['slug'], $args['...r'], $args['keywords']) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug Best Practice introduced by
The expression return c($args['slug'], ...r'], $args['keywords']) returns the type null which is incompatible with the type-hinted return array.
Loading history...
247
            $args['slug'],
0 ignored issues
show
Unused Code introduced by
The call to c() has too many arguments starting with $args['slug']. ( Ignorable by Annotation )

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

247
        return /** @scrutinizer ignore-call */ c(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
248
            $args['label'],
249
            $args['children'],
250
            $args['options'],
251
            $args['enableMediaQueries'],
252
            $args['enableHover'],
253
            $args['keywords']
254
        );
255
    }
256
257
    protected static function bdControlCheckbox(Arguments $args): array
258
    {
259
        $control = [
260
            'label' => $args->label,
261
            'options' => [
262
                'layout' => 'inline',
263
                'type' => 'toggle',
264
            ],
265
            'slug' => $args->slug,
266
        ];
267
        if ($items = static::bdTransformDropdownItems($args->array('options'))) {
268
            $control['children'] = array_map(fn ($item) => static::bdControl([
269
                'slug' => $item['value'],
270
                'label' => $item['text'],
271
                'options' => [
272
                    'layout' => 'inline',
273
                    'type' => 'toggle',
274
                ],
275
            ]), $items);
276
            $control['options']['type'] = 'section';
277
        }
278
        return static::bdControl($control);
279
    }
280
281
    protected static function bdControlNumber(Arguments $args): array
282
    {
283
        return static::bdControl([
284
            'slug' => $args->slug,
285
            'label' => $args->label,
286
            'options' => [
287
                'layout' => 'inline',
288
                'rangeOptions' => [
289
                    'min' => $args->cast('min', 'int', 0),
290
                    'max' => $args->cast('max', 'int', 1),
291
                    'step' => $args->cast('step', 'int', 1),
292
                ],
293
                'type' => 'number',
294
            ],
295
        ]);
296
    }
297
298
    /**
299
     * If multiple select is required in an AJAX-populated dropdown then we have
300
     * to use the post_chooser control because:
301
     * a) The dropdown control does not support multiple selection.
302
     * b) The multiselect control does not support AJAX searching.
303
     *
304
     * @see Controller::searchAssignedPosts
305
     * @see Controller::searchAssignedTerms
306
     * @see Controller::searchAssignedUsers
307
     * @see \GeminiLabs\SiteReviews\Controllers\MainController::parseAssignedPostTypesInQuery
308
     */
309
    protected static function bdControlSelect(Arguments $args): array
310
    {
311
        $control = [
312
            'label' => $args->label,
313
            'slug' => $args->slug,
314
            'options' => [
315
                'layout' => 'vertical',
316
                'placeholder' => $args->placeholder ?: esc_html_x('Select...', 'admin-text', 'site-reviews'),
317
                'type' => 'dropdown',
318
            ],
319
        ];
320
        if (!$args->exists('options')) {
321
            // Unfortunately we can't use this because Breakdance does not support
322
            // AJAX searching in dropdown controls.
323
            // if (true !== $args->multiple) {
324
            //     $control['options']['dropdownOptions']['populate'] = [
325
            //         'fetchDataAction' => glsr()->prefix.'breakdance_'.$args->slug,
326
            //     ];
327
            //     return static::bdControl($control);
328
            // }
329
            $control['options']['type'] = 'post_chooser';
330
            $control['options']['postChooserOptions'] = [
331
                'multiple' => $args->multiple,
332
                'postType' => glsr()->prefix.$args->slug,
333
                'showThumbnails' => 'assigned_users' === $args->slug,
334
            ];
335
            // Unfortunately we can't use this yet because Breakdance does not support
336
            // AJAX searching in multiselect controls.
337
            // $control['options']['type'] = 'multiselect';
338
            // $control['options']['searchable'] = true;
339
            // $control['options']['multiselectOptions']['populate'] = [
340
            //     'fetchDataAction' => glsr()->prefix.'breakdance_'.$args->slug,
341
            //     'fetchContextPath' => 'content.general.'.$args->slug,
342
            // ];
343
            return static::bdControl($control);
344
        }
345
        $items = static::bdTransformDropdownItems($args->array('options'));
346
        if (!empty($items)) {
347
            $control['options']['items'] = $items;
348
            return static::bdControl($control);
349
        }
350
        return [];
351
    }
352
353
    protected static function bdControlText(Arguments $args): array
354
    {
355
        return static::bdControl([
356
            'slug' => $args->slug,
357
            'label' => $args->label,
358
            'options' => [
359
                'layout' => 'vertical',
360
                'type' => 'text',
361
            ],
362
        ]);
363
    }
364
365
    /**
366
     * Find the group slug for the given slug in grouped fields.
367
     */
368
    protected static function bdFindGroupSlug(string $slug, array $groupedFields): string
369
    {
370
        return (string) array_search(true, array_map(
371
            fn ($fields) => in_array($slug, $fields),
372
            $groupedFields
373
        ));
374
    }
375
376
    /**
377
     * Process the configuration settings and group controls.
378
     */
379
    protected static function bdProcessConfig(): array
380
    {
381
        $config = static::bdShortcode()->settings();
382
        $groups = static::bdSectionGroups();
383
        $groupedFields = wp_list_pluck($groups, 'fields');
384
        $sections = static::bdSections();
385
        foreach ($config as $slug => $args) {
386
            $args = wp_parse_args($args, [
387
                'group' => 'general',
388
                'type' => 'text',
389
                'slug' => $slug,
390
            ]);
391
            $args['group'] = array_key_exists($args['group'], $sections) ? $args['group'] : 'general';
392
            $method = Helper::buildMethodName('bd', 'control', $args['type']);
393
            if (!method_exists(static::class, $method)) {
394
                continue;
395
            }
396
            $control = call_user_func([static::class, $method], glsr()->args($args));
397
            if (empty($control)) {
398
                continue;
399
            }
400
            $groupSlug = static::bdFindGroupSlug($slug, $groupedFields);
401
            if (!empty($groupSlug)) {
402
                $groups[$groupSlug]['children'][] = $control;
403
                continue;
404
            }
405
            $sections[$args['group']]['children'][] = $control;
406
        }
407
        return static::bdProcessControlGroups($groups, $sections);
408
    }
409
410
    /**
411
     * Process groups and add them to sections if they have valid children.
412
     */
413
    protected static function bdProcessControlGroups(array $groups, array $sections): array
414
    {
415
        foreach ($groups as $slug => $group) {
416
            $filteredChildren = array_filter($group['children'], function ($child) {
417
                return 'alert_box' !== ($child['options']['type'] ?? 'alert_box');
418
            });
419
            if (!empty($filteredChildren)) {
420
                $sections['general']['children'][] = static::bdControl([
421
                    'children' => $group['children'],
422
                    'label' => $group['label'],
423
                    'options' => $group['options'],
424
                    'slug' => $slug,
425
                ]);
426
            }
427
        }
428
        return $sections;
429
    }
430
431
    protected static function bdTransformDropdownItems(array $options): array
432
    {
433
        $callback = fn ($k, $v) => [
434
            'text' => $v,
435
            'value' => $k,
436
        ];
437
        return array_map($callback, array_keys($options), $options);
438
    }
439
}
440