Dropdown::renderItems()   C
last analyzed

Complexity

Conditions 14
Paths 77

Size

Total Lines 82
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 14.0013

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 56
c 1
b 0
f 0
nc 77
nop 2
dl 0
loc 82
ccs 51
cts 52
cp 0.9808
crap 14.0013
rs 6.2666

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
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap4;
6
7
use JsonException;
8
use RuntimeException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
12
use function array_key_exists;
13
use function array_merge;
14
use function array_merge_recursive;
15
use function is_string;
16
17
/**
18
 * Dropdown renders a Bootstrap dropdown menu component.
19
 *
20
 * For example,
21
 *
22
 * ```php
23
 * <div class="dropdown">
24
 *     <?php
25
 *         echo Dropdown::widget()
26
 *             ->items([
27
 *                 ['label' => 'DropdownA', 'url' => '/'],
28
 *                 ['label' => 'DropdownB', 'url' => '#'],
29
 *             ]);
30
 *     ?>
31
 * </div>
32
 * ```
33
 */
34
class Dropdown extends Widget
35
{
36
    private array $items = [];
37
    private bool $encodeLabels = true;
38
    private array $submenuOptions = [];
39
    private array $options = [];
40
41 16
    protected function run(): string
42
    {
43 16
        if (!isset($this->options['id'])) {
44 15
            $this->options['id'] = "{$this->getId()}-dropdown";
45
        }
46
47 16
        Html::addCssClass($this->options, ['widget' => 'dropdown-menu']);
48
49 16
        $this->registerClientEvents($this->options['id']);
50
51 16
        return $this->renderItems($this->items, $this->options);
52
    }
53
54
    /**
55
     * Renders menu items.
56
     *
57
     * @param array $items the menu items to be rendered
58
     * @param array $options the container HTML attributes
59
     *
60
     * @throws JsonException|RuntimeException if the label option is not specified in  one of the items.
61
     *
62
     * @return string the rendering result.
63
     */
64 16
    protected function renderItems(array $items, array $options = []): string
65
    {
66 16
        $lines = [];
67
68 16
        foreach ($items as $item) {
69 16
            if (is_string($item)) {
70 3
                $item = ['label' => $item, 'encode' => false, 'enclose' => false];
71
            }
72
73 16
            if (isset($item['visible']) && !$item['visible']) {
74 3
                continue;
75
            }
76
77 16
            if (!array_key_exists('label', $item)) {
78
                throw new RuntimeException('The "label" option is required.');
79
            }
80
81 16
            $encodeLabel = $item['encode'] ?? $this->encodeLabels;
82 16
            $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
83 16
            $itemOptions = ArrayHelper::getValue($item, 'options', []);
84 16
            $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
85 16
            $active = ArrayHelper::getValue($item, 'active', false);
86 16
            $disabled = ArrayHelper::getValue($item, 'disabled', false);
87 16
            $enclose = ArrayHelper::getValue($item, 'enclose', true);
88
89 16
            Html::addCssClass($linkOptions, 'dropdown-item');
90
91 16
            if ($disabled) {
92 1
                ArrayHelper::setValue($linkOptions, 'tabindex', '-1');
93 1
                ArrayHelper::setValue($linkOptions, 'aria-disabled', 'true');
94 1
                Html::addCssClass($linkOptions, 'disabled');
95 16
            } elseif ($active) {
96 3
                Html::addCssClass($linkOptions, 'active');
97
            }
98
99 16
            $url = $item['url'] ?? null;
100
101 16
            if (empty($item['items'])) {
102 16
                if ($label === '-') {
103 3
                    $content = Html::div('', ['class' => 'dropdown-divider']);
104 16
                } elseif ($enclose === false) {
105 1
                    $content = $label;
106 16
                } elseif ($url === null) {
107 8
                    $content = Html::tag('h6', $label, ['class' => 'dropdown-header']);
108
                } else {
109 10
                    $content = Html::a($label, $url, $linkOptions);
110
                }
111
112 16
                $lines[] = $content;
113
            } else {
114 3
                $submenuOptions = $this->submenuOptions;
115
116 3
                if (isset($item['submenuOptions'])) {
117 1
                    $submenuOptions = array_merge($submenuOptions, $item['submenuOptions']);
118
                }
119
120 3
                Html::addCssClass($submenuOptions, ['dropdown-submenu']);
121 3
                Html::addCssClass($linkOptions, ['dropdown-toggle']);
122
123 3
                $lines[] = Html::beginTag(
124 3
                    'div',
125 3
                    array_merge_recursive(['class' => ['dropdown'], 'aria-expanded' => 'false'], $itemOptions)
126
                );
127
128 3
                $lines[] = Html::a($label, $url, array_merge([
129 3
                    'data-toggle' => 'dropdown',
130
                    'aria-haspopup' => 'true',
131
                    'aria-expanded' => 'false',
132
                    'role' => 'button',
133 3
                ], $linkOptions));
134
135 3
                $lines[] = self::widget()
136 3
                    ->items($item['items'])
0 ignored issues
show
Bug introduced by
The method items() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Yii\Bootstrap4\Tabs or Yiisoft\Yii\Bootstrap4\Accordion or Yiisoft\Yii\Bootstrap4\Carousel or Yiisoft\Yii\Bootstrap4\Dropdown or Yiisoft\Yii\Bootstrap4\Nav. ( Ignorable by Annotation )

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

136
                    ->/** @scrutinizer ignore-call */ items($item['items'])
Loading history...
137 3
                    ->options($submenuOptions)
138 3
                    ->submenuOptions($submenuOptions)
139 3
                    ->encodeLabels($this->encodeLabels)
140 3
                    ->run();
141 3
                $lines[] = Html::endTag('div');
142
            }
143
        }
144
145 16
        return Html::tag('div', implode("\n", $lines), $options);
146
    }
147
148
    /**
149
     * List of menu items in the dropdown. Each array element can be either an HTML string, or an array representing a
150
     * single menu with the following structure:
151
     *
152
     * - label: string, required, the label of the item link.
153
     * - encode: bool, optional, whether to HTML-encode item label.
154
     * - url: string|array, optional, the URL of the item link. This will be processed by {@see currentPath}.
155
     *   If not set, the item will be treated as a menu header when the item has no sub-menu.
156
     * - visible: bool, optional, whether this menu item is visible. Defaults to true.
157
     * - linkOptions: array, optional, the HTML attributes of the item link.
158
     * - options: array, optional, the HTML attributes of the item.
159
     * - items: array, optional, the submenu items. The structure is the same as this property.
160
     *   Note that Bootstrap doesn't support dropdown submenu. You have to add your own CSS styles to support it.
161
     * - submenuOptions: array, optional, the HTML attributes for sub-menu container tag. If specified it will be
162
     *   merged with {@see submenuOptions}.
163
     *
164
     * To insert divider use `-`.
165
     *
166
     * @param array $value
167
     *
168
     * @return $this
169
     */
170 16
    public function items(array $value): self
171
    {
172 16
        $this->items = $value;
173
174 16
        return $this;
175
    }
176
177
    /**
178
     * Whether the labels for header items should be HTML-encoded.
179
     *
180
     * @param bool $value
181
     *
182
     * @return $this
183
     */
184 12
    public function encodeLabels(bool $value): self
185
    {
186 12
        $this->encodeLabels = $value;
187
188 12
        return $this;
189
    }
190
191
    /**
192
     * The HTML attributes for sub-menu container tags.
193
     *
194
     * @param array $value
195
     *
196
     * @return $this
197
     */
198 3
    public function submenuOptions(array $value): self
199
    {
200 3
        $this->submenuOptions = $value;
201
202 3
        return $this;
203
    }
204
205
    /**
206
     * @param array $value the HTML attributes for the widget container tag. The following special options are
207
     * recognized.
208
     *
209
     * @return $this
210
     *
211
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
212
     */
213 12
    public function options(array $value): self
214
    {
215 12
        $this->options = $value;
216
217 12
        return $this;
218
    }
219
}
220