Completed
Pull Request — master (#140)
by
unknown
07:16
created

MenuService::getNestedAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Menu\Service;
6
7
use Category\Service\CategoryService;
8
use Menu\Filter\MenuFilter;
9
use Menu\Mapper\MenuMapper;
10
use MysqlUuid\Formats\Binary;
11
use MysqlUuid\Uuid as MysqlUuid;
12
use Page\Service\PageService;
13
use Ramsey\Uuid\Uuid;
14
use Std\FilterException;
15
use Zend\Db\Sql\Expression;
16
17
class MenuService
18
{
19
    private $menuMapper;
20
    private $menuFilter;
21
    private $categoryService;
22
    private $pageService;
23
24
    /**
25
     * We store menu items in DB as flat structure,
26
     * but we need nested(tree) structure to show in the menu.
27
     *
28
     * @param array $flatArray Array from DB
29
     *
30
     * @return array Return same array with tree structure
31
     */
32
    private function unflattenArray(array $flatArray)
33
    {
34
        $flatArray = array_reverse($flatArray);
35
        $refs = [];
36
        $result = [];
37
38
        while (count($flatArray) > 0) {
39
            for ($i = count($flatArray) - 1; $i >= 0; $i--) {
40
                if (!isset($flatArray[$i]['children'])) {
41
                    $flatArray[$i]['children'] = [];
42
                }
43
44
                if (!$flatArray[$i]['parent_id']) {
45
                    $result[$flatArray[$i]['menu_id']] = $flatArray[$i];
46
                    $refs[$flatArray[$i]['menu_id']] = &$result[$flatArray[$i]['menu_id']];
47
                    unset($flatArray[$i]);
48
                    $flatArray = array_values($flatArray);
49
                } elseif ($flatArray[$i]['parent_id'] != 0) {
50
                    if (array_key_exists($flatArray[$i]['parent_id'], $refs)) {
51
                        $o = $flatArray[$i];
52
                        $refs[$flatArray[$i]['menu_id']] = $o;
53
                        $refs[$flatArray[$i]['parent_id']]['children'][] = &$refs[$flatArray[$i]['menu_id']];
54
                        unset($flatArray[$i]);
55
                        $flatArray = array_values($flatArray);
56
                    }
57
                }
58
            }
59
        }
60
61
        return $result;
62
    }
63
64
    public function __construct(
65
        MenuMapper $menuMapper,
66
        MenuFilter $menuFilter,
67
        CategoryService $categoryService,
68
        PageService $pageService
69
    ) {
70
        $this->menuMapper = $menuMapper;
71
        $this->menuFilter = $menuFilter;
72
        $this->categoryService = $categoryService;
73
        $this->pageService = $pageService;
74
    }
75
76
    public function getNestedAll($isActive = null, $filter = [])
77
    {
78
        $items = $this->menuMapper->selectAll($isActive, $filter)->toArray();
79
80
        return $this->unflattenArray($items);
81
    }
82
83
    public function get($id)
84
    {
85
        return $this->menuMapper->get($id);
86
    }
87
88
    public function addMenuItem($data)
89
    {
90
        $data  = $this->filterMenuItem($data);
91
        $order = $this->getMaxParentsOrderNumber();
92
93
        $data['menu_id'] = Uuid::uuid1()->toString();
94
        $data['menu_uuid'] = (new MysqlUuid($data['menu_id']))->toFormat(new Binary());
95
        $data['order_no'] = (!empty($order)) ? ($order + 1) : 1;
96
97
        return $this->menuMapper->insertMenuItem($data);
98
    }
99
100
    public function updateMenuItem($data, $id)
101
    {
102
        $data = $this->filterMenuItem($data);
103
104
        return $this->menuMapper->updateMenuItem($data, $id);
105
    }
106
107
    public function delete($id)
108
    {
109
        $menu     = $this->menuMapper->select(['menu_id' => $id]);
110
        $children = $this->menuMapper->select(['parent_id' => $id]);
111
112
        if ($children->count()) {
113
            $menu = $menu->current();
114
            $this->menuMapper->update([
115
                'order_no'  => new Expression('order_no + ' . ($children->count() - 1))], [
116
                'parent_id' => $menu->parent_id,
117
                'order_no'  => new Expression('order_no > ' . ($menu->order_no))
118
            ]);
119
120
            foreach ($children->toArray() as $child) {
121
                $child['parent_id'] = $menu->parent_id;
122
                $child['order_no'] += ($menu->order_no - 1);
123
                $this->menuMapper->update($child, [
124
                    'menu_id' => $child['menu_id']
125
                ]);
126
            }
127
        }
128
129
        return $this->menuMapper->delete(['menu_id' => $id]);
130
    }
131
132
    public function getForSelect()
133
    {
134
        return $this->menuMapper->forSelect();
135
    }
136
137
    public function updateMenuOrder($menuOrder)
138
    {
139
        if (!$menuOrder) {
140
            return true;
141
        }
142
143
        try {
144
            $this->menuMapper->getAdapter()->getDriver()->getConnection()->beginTransaction();
145
            $this->updateLevel($menuOrder, null);
146
            $this->menuMapper->getAdapter()->getDriver()->getConnection()->commit();
147
        } catch (\Exception $e) {
148
            $this->menuMapper->getAdapter()->getDriver()->getConnection()->rollback();
149
150
            throw $e;
151
        }
152
153
        return true;
154
    }
155
156
    private function updateLevel($children, $parentId = null)
157
    {
158
        $i=0; foreach ($children as $v) { $i++;
159
            if (isset($v->children)) {
160
                $this->menuMapper->update(['order_no' => $i, 'parent_id' => $parentId], ['menu_id' => $v->id]);
161
                $this->updateLevel($v->children, $v->id);
162
            } else {
163
                $this->menuMapper->update(['order_no' => $i, 'parent_id' => $parentId], ['menu_id' => $v->id]);
164
            }
165
        }
166
    }
167
168
    private function filterMenuItem($data)
169
    {
170
        $filter = $this->menuFilter->getInputFilter()->setData($data);
171
172
        if (!$filter->isValid()) {
173
            throw new FilterException($filter->getMessages());
174
        }
175
176
        if (count(array_filter([$data['page_id'], $data['category_id'], $data['href']])) > 1) {
177
            throw new \Exception('You need to set only one link. Post, Category or Href.');
178
        }
179
180
        $data = $filter->getValues();
181
182
        if ($data['page_id']) {
183
            $page = $this->pageService->getPage($data['page_id']);
184
            $data['page_uuid'] = $page->getPageUuid();
185
            $data['category_uuid'] = null;
186
        } elseif ($data['category_id']) {
187
            $category
188
                = $this->categoryService->getCategory($data['category_id']);
189
            $data['category_uuid'] = $category->category_uuid;
190
            $data['page_uuid'] = null;
191
        } else {
192
            $data['page_uuid'] = null;
193
            $data['category_uuid'] = null;
194
        }
195
196
        unset($data['page_id'], $data['category_id']);
197
198
        return $data;
199
    }
200
201
    /**
202
     * Returns max order number if found.
203
     *
204
     * @return integer|null
205
     */
206
    private function getMaxParentsOrderNumber()
207
    {
208
        $select = $this->menuMapper
209
            ->getSql()
210
            ->setTable('menu')
211
            ->select()
212
            ->where(['parent_id'  => new Expression('NULL')])
213
            ->columns(['order_no' => new Expression('MAX(order_no)')])
214
        ;
215
216
        $result = $this->menuMapper->selectWith($select)->current();
217
        return $result->order_no;
218
    }
219
}
220