Completed
Push — master ( 8ebc15...647db4 )
by Andrey
01:15
created

MenuWidget::currentItemTemplate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
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 $initLevel
184
     * @return string
185
     */
186
    private function renderItems(array $items, bool $initLevel = true): string
187
    {
188
        if (count($items) == 0){
189
            return '';
190
        }
191
192
        $outPut = '';
193
194
        /** @var array $item */
195
        foreach ($items as $item) {
196
            $contentLi = $this->render($this->currentItemTemplate($initLevel), ArrayHelper::merge([
197
                'data' => $item['data']
198
            ], $this->currentItemTemplateParams($initLevel)));
199
200
            if (isset($item['items'])){
201
                $contentLi .= $this->renderItems($item['items'], false);
202
            }
203
            $outPut .= Html::tag($this->currentItemContainerTag($initLevel), $contentLi, $this->currentItemContainerOptions($initLevel));
204
        }
205
206
        return Html::tag($this->currentMainContainerTag($initLevel), $outPut, $this->currentMainContainerOptions($initLevel));
207
    }
208
209
    /**
210
     * @param bool $initLevel
211
     * @return string
212
     */
213
    private function currentItemTemplate(bool $initLevel = true): string
214
    {
215
        return $initLevel ? $this->itemTemplate : $this->subItemTemplate;
216
    }
217
218
    /**
219
     * @param bool $initLevel
220
     * @return array
221
     */
222
    private function currentItemTemplateParams(bool $initLevel = true): array
223
    {
224
        return $initLevel ? $this->itemTemplateParams : $this->subItemTemplateParams;
225
    }
226
227
    /**
228
     * @param bool $initLevel
229
     * @return string
230
     */
231
    private function currentMainContainerTag(bool $initLevel = true): string
232
    {
233
        return $initLevel ? $this->mainContainerTag : $this->subMainContainerTag;
234
    }
235
236
    /**
237
     * @param bool $initLevel
238
     * @return array
239
     */
240
    private function currentMainContainerOptions(bool $initLevel = true): array
241
    {
242
        return $initLevel ? $this->mainContainerOptions : $this->subMainContainerOptions;
243
    }
244
245
    /**
246
     * @param bool $initLevel
247
     * @return string
248
     */
249
    private function currentItemContainerTag(bool $initLevel = true): string
250
    {
251
        return $initLevel ? $this->itemContainerTag : $this->subItemContainerTag;
252
    }
253
254
    /**
255
     * @param bool $initLevel
256
     * @return array
257
     */
258
    private function currentItemContainerOptions(bool $initLevel = true): array
259
    {
260
        return $initLevel ? $this->itemContainerOptions : $this->subItemContainerOptions;
261
    }
262
263
    /**
264
     * Check for configure.
265
     * @throws InvalidConfigException
266
     */
267
    private function checkConfiguration()
268
    {
269
        if (null === $this->dataProvider){
270
            throw  new InvalidConfigException('Parameter dataProvider is not defined.');
271
        }
272
273
        if (null === $this->itemTemplate || !is_string($this->itemTemplate)){
274
            throw  new InvalidConfigException('Item template is not defined.');
275
        }
276
277
        if (null === $this->subItemTemplate || !is_string($this->subItemTemplate)){
278
            $this->subItemTemplate = $this->itemTemplate;
279
        }
280
    }
281
}
282