Completed
Branch master (d8aae6)
by Andrey
02:58
created

MenuWidget::setDataProvider()   A

Complexity

Conditions 1
Paths 1

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 1
eloc 2
nc 1
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 $itemContainerTag Item container html tag.
19
 * @property array $itemContainerOptions Item container html options.
20
 * @property string $itemTemplate Item template to display widget elements.
21
 * @property array $itemTemplateParams Addition item template params.
22
 * @property ActiveDataProvider $dataProvider Data provider records.
23
 *
24
 * @package Itstructure\MultiLevelMenu
25
 *
26
 * @author Andrey Girnik <[email protected]>
27
 */
28
class MenuWidget extends Widget
29
{
30
    /**
31
     * Primary key name.
32
     * @var string
33
     */
34
    public $primaryKeyName = 'id';
35
36
    /**
37
     * Relation key name.
38
     * @var string
39
     */
40
    public $parentKeyName = 'parentId';
41
42
    /**
43
     * Main container html tag.
44
     * @var string
45
     */
46
    public $mainContainerTag = 'ul';
47
48
    /**
49
     * Main container html options.
50
     * @var array
51
     */
52
    public $mainContainerOptions = [];
53
54
    /**
55
     * Item container html tag.
56
     * @var string
57
     */
58
    public $itemContainerTag = 'li';
59
60
    /**
61
     * Item container html options.
62
     * @var array
63
     */
64
    public $itemContainerOptions = [];
65
66
    /**
67
     * Item template to display widget elements.
68
     * @var string
69
     */
70
    public $itemTemplate;
71
72
    /**
73
     * Addition item template params.
74
     * @var array
75
     */
76
    public $itemTemplateParams = [];
77
78
    /**
79
     * Data provider records.
80
     * @var ActiveDataProvider
81
     */
82
    private $dataProvider;
83
84
    /**
85
     * Starts the output widget of the multi level view records according with the menu type.
86
     * @throws InvalidConfigException
87
     */
88
    public function run()
89
    {
90
        if (null === $this->dataProvider){
91
            throw  new InvalidConfigException('Parameter dataProvider is not defined.');
92
        }
93
94
        if (null === $this->itemTemplate || !is_string($this->itemTemplate)){
95
            throw  new InvalidConfigException('Item template is not defined.');
96
        }
97
98
        /** @var ActiveRecord[] $models */
99
        $models = array_values($this->dataProvider->getModels());
100
        $models = $this->groupLevels($models);
101
102
        return $this->renderItems($models);
103
    }
104
105
    /**
106
     * Set data provider.
107
     * @param ActiveDataProvider $dataProvider
108
     */
109
    public function setDataProvider(ActiveDataProvider $dataProvider): void
110
    {
111
        $this->dataProvider = $dataProvider;
112
    }
113
114
    /**
115
     * Group records in to sub levels according with the relation to parent records.
116
     * @param array $models
117
     * @return array
118
     */
119
    private function groupLevels(array $models): array
120
    {
121
        if (count($models) == 0){
122
            return [];
123
        }
124
125
        $items = [];
126
127
        /** @var ActiveRecord $item */
128
        $modelsCount = count($models);
129
        for ($i=0; $i < $modelsCount; $i++) {
130
            $item = $models[$i];
131
            $items[$item->{$this->primaryKeyName}]['data'] = $item;
132
        }
133
134
        /** @var ActiveRecord $data */
135
        foreach($items as $row) {
136
            $data = $row['data'];
137
            $parentKey = !isset($data->{$this->parentKeyName}) || empty($data->{$this->parentKeyName}) ? 0 : $data->{$this->parentKeyName};
138
            $items[$parentKey]['items'][$data->{$this->primaryKeyName}] = &$items[$data->{$this->primaryKeyName}];
139
        }
140
141
        return $items[0]['items'];
142
    }
143
144
    /**
145
     * Base render.
146
     * @param array $items
147
     * @return string
148
     */
149
    private function renderItems(array $items): string
150
    {
151
        if (count($items) == 0){
152
            return '';
153
        }
154
155
        $outPut = '';
156
157
        /** @var array $item */
158
        foreach ($items as $item) {
159
            $contentLi = $this->render($this->itemTemplate, ArrayHelper::merge([
160
                'data' => $item['data']
161
            ], $this->itemTemplateParams));
162
163
            if (isset($item['items'])){
164
                $contentLi .= $this->renderItems($item['items']);
165
            }
166
            $outPut .= Html::tag($this->itemContainerTag, $contentLi, $this->itemContainerOptions);
167
        }
168
169
        return Html::tag($this->mainContainerTag, $outPut, $this->mainContainerOptions);
170
    }
171
}
172