Completed
Push — master ( d8aae6...8ebc15 )
by Andrey
01:13
created

MenuWidget::renderItems()   D

Complexity

Conditions 10
Paths 193

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 4.606
c 0
b 0
f 0
cc 10
eloc 18
nc 193
nop 2

How to fix   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
namespace Itstructure\MultiLevelMenu;
4
5
use yii\db\ActiveRecord;
6
use yii\helpers\{Html, ArrayHelper};
7
use yii\base\{Widget, InvalidConfigException};
8
use yii\data\ActiveDataProvider;
9
10
/**
11
 * Class MenuWidget.
12
 * Multilevel menu widget.
13
 *
14
 * @property string $primaryKeyName Primary key name.
15
 * @property string $parentKeyName Relation key name.
16
 * @property string $mainContainerTag Main container html tag.
17
 * @property array $mainContainerOptions Main container html options.
18
 * @property string $subMainContainerTag Sub main container html tag.
19
 * @property array $subMainContainerOptions Sub main container html options.
20
 * @property string $itemContainerTag Item container html tag.
21
 * @property array $itemContainerOptions Item container html options.
22
 * @property string $subItemContainerTag Sub item container html tag.
23
 * @property array $subItemContainerOptions Sub item container html options.
24
 * @property string $itemTemplate Item template to display widget elements.
25
 * @property array $itemTemplateParams Addition item template params.
26
 * @property string $subItemTemplate Sub item template to display widget elements.
27
 * @property array $subItemTemplateParams Addition sub item template params.
28
 * @property ActiveDataProvider $dataProvider Data provider records.
29
 *
30
 * @package Itstructure\MultiLevelMenu
31
 *
32
 * @author Andrey Girnik <[email protected]>
33
 */
34
class MenuWidget extends Widget
35
{
36
    /**
37
     * Primary key name.
38
     * @var string
39
     */
40
    public $primaryKeyName = 'id';
41
42
    /**
43
     * Relation key name.
44
     * @var string
45
     */
46
    public $parentKeyName = 'parentId';
47
48
    /**
49
     * Main container html tag.
50
     * @var string
51
     */
52
    public $mainContainerTag = 'ul';
53
54
    /**
55
     * Main container html options.
56
     * @var array
57
     */
58
    public $mainContainerOptions = [];
59
60
    /**
61
     * Sub main container html tag.
62
     * @var string
63
     */
64
    public $subMainContainerTag = 'ul';
65
66
    /**
67
     * Sub main container html options.
68
     * @var array
69
     */
70
    public $subMainContainerOptions = [];
71
72
    /**
73
     * Item container html tag.
74
     * @var string
75
     */
76
    public $itemContainerTag = 'li';
77
78
    /**
79
     * Item container html options.
80
     * @var array
81
     */
82
    public $itemContainerOptions = [];
83
84
    /**
85
     * Sub item container html tag.
86
     * @var string
87
     */
88
    public $subItemContainerTag = 'li';
89
90
    /**
91
     * Sub item container html options.
92
     * @var array
93
     */
94
    public $subItemContainerOptions = [];
95
96
    /**
97
     * Item template to display widget elements.
98
     * @var string
99
     */
100
    public $itemTemplate;
101
102
    /**
103
     * Addition item template params.
104
     * @var array
105
     */
106
    public $itemTemplateParams = [];
107
108
    /**
109
     * Sub item template to display widget elements.
110
     * @var string
111
     */
112
    public $subItemTemplate;
113
114
    /**
115
     * Addition sub item template params.
116
     * @var array
117
     */
118
    public $subItemTemplateParams = [];
119
120
    /**
121
     * Data provider records.
122
     * @var ActiveDataProvider
123
     */
124
    private $dataProvider;
125
126
    /**
127
     * Starts the output widget of the multi level view records according with the menu type.
128
     * @throws InvalidConfigException
129
     */
130
    public function run()
131
    {
132
        $this->checkConfiguration();
133
134
        /** @var ActiveRecord[] $models */
135
        $models = array_values($this->dataProvider->getModels());
136
        $models = $this->groupLevels($models);
137
138
        return $this->renderItems($models);
139
    }
140
141
    /**
142
     * Set data provider.
143
     * @param ActiveDataProvider $dataProvider
144
     */
145
    public function setDataProvider(ActiveDataProvider $dataProvider): void
146
    {
147
        $this->dataProvider = $dataProvider;
148
    }
149
150
    /**
151
     * Group records in to sub levels according with the relation to parent records.
152
     * @param array $models
153
     * @return array
154
     */
155
    private function groupLevels(array $models): array
156
    {
157
        if (count($models) == 0){
158
            return [];
159
        }
160
161
        $items = [];
162
163
        /** @var ActiveRecord $item */
164
        $modelsCount = count($models);
165
        for ($i=0; $i < $modelsCount; $i++) {
166
            $item = $models[$i];
167
            $items[$item->{$this->primaryKeyName}]['data'] = $item;
168
        }
169
170
        /** @var ActiveRecord $data */
171
        foreach($items as $row) {
172
            $data = $row['data'];
173
            $parentKey = !isset($data->{$this->parentKeyName}) || empty($data->{$this->parentKeyName}) ? 0 : $data->{$this->parentKeyName};
174
            $items[$parentKey]['items'][$data->{$this->primaryKeyName}] = &$items[$data->{$this->primaryKeyName}];
175
        }
176
177
        return $items[0]['items'];
178
    }
179
180
    /**
181
     * Base render.
182
     * @param array $items
183
     * @param bool $isSubLevel
184
     * @return string
185
     */
186
    private function renderItems(array $items, bool $isSubLevel = false): string
187
    {
188
        if (count($items) == 0){
189
            return '';
190
        }
191
192
        $itemTemplate         = $isSubLevel ? $this->subItemTemplate         : $this->itemTemplate;
193
        $itemTemplateParams   = $isSubLevel ? $this->subItemTemplateParams   : $this->itemTemplateParams;
194
        $mainContainerTag     = $isSubLevel ? $this->subMainContainerTag     : $this->mainContainerTag;
195
        $mainContainerOptions = $isSubLevel ? $this->subMainContainerOptions : $this->mainContainerOptions;
196
        $itemContainerTag     = $isSubLevel ? $this->subItemContainerTag     : $this->itemContainerTag;
197
        $itemContainerOptions = $isSubLevel ? $this->subItemContainerOptions : $this->itemContainerOptions;
198
199
        $outPut = '';
200
201
        /** @var array $item */
202
        foreach ($items as $item) {
203
            $contentLi = $this->render($itemTemplate, ArrayHelper::merge([
204
                'data' => $item['data']
205
            ], $itemTemplateParams));
206
207
            if (isset($item['items'])){
208
                $contentLi .= $this->renderItems($item['items']);
209
            }
210
            $outPut .= Html::tag($itemContainerTag, $contentLi, $itemContainerOptions);
211
        }
212
213
        return Html::tag($mainContainerTag, $outPut, $mainContainerOptions);
214
    }
215
216
    /**
217
     * Check for configure.
218
     * @throws InvalidConfigException
219
     */
220
    private function checkConfiguration()
221
    {
222
        if (null === $this->dataProvider){
223
            throw  new InvalidConfigException('Parameter dataProvider is not defined.');
224
        }
225
226
        if (null === $this->itemTemplate || !is_string($this->itemTemplate)){
227
            throw  new InvalidConfigException('Item template is not defined.');
228
        }
229
230
        if (null === $this->subItemTemplate || !is_string($this->subItemTemplate)){
231
            $this->subItemTemplate = $this->itemTemplate;
232
        }
233
    }
234
}
235