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
|
|
|
public function __construct( |
25
|
|
|
MenuMapper $menuMapper, |
26
|
|
|
MenuFilter $menuFilter, |
27
|
|
|
CategoryService $categoryService, |
28
|
|
|
PageService $pageService |
29
|
|
|
) { |
30
|
|
|
$this->menuMapper = $menuMapper; |
31
|
|
|
$this->menuFilter = $menuFilter; |
32
|
|
|
$this->categoryService = $categoryService; |
33
|
|
|
$this->pageService = $pageService; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* We store menu items in DB as flat structure, |
38
|
|
|
* but we need nested(tree) structure to show in the menu. |
39
|
|
|
* |
40
|
|
|
* @param array $flatArray Array from DB |
41
|
|
|
* @param int|bool $parent |
42
|
|
|
* |
43
|
|
|
* @return array Return same array with tree structure |
44
|
|
|
*/ |
45
|
|
|
private function buildTree(array $flatArray, $parent = null) |
46
|
|
|
{ |
47
|
|
|
$result = []; |
48
|
|
|
|
49
|
|
|
foreach ($flatArray as $element) { |
50
|
|
|
if ($element['parent_id'] == $parent) { |
51
|
|
|
$children = $this->buildTree($flatArray, $element['menu_id']); |
52
|
|
|
$element['children'] = ($children) ? $children : []; |
53
|
|
|
$result[] = $element; |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
return $result; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
public function getNestedAll($isActive = null, $filter = []) |
61
|
|
|
{ |
62
|
|
|
$items = $this->menuMapper->selectAll($isActive, $filter)->toArray(); |
63
|
|
|
|
64
|
|
|
return $this->buildTree($items); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
public function get($id) |
68
|
|
|
{ |
69
|
|
|
return $this->menuMapper->get($id); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
public function addMenuItem($data) |
73
|
|
|
{ |
74
|
|
|
$data = $this->filterMenuItem($data); |
75
|
|
|
$order = $this->getMaxParentsOrderNumber(); |
76
|
|
|
|
77
|
|
|
$data['menu_id'] = Uuid::uuid1()->toString(); |
78
|
|
|
$data['menu_uuid'] = (new MysqlUuid($data['menu_id']))->toFormat(new Binary()); |
79
|
|
|
$data['order_no'] = (!empty($order)) ? ($order + 1) : 1; |
80
|
|
|
|
81
|
|
|
return $this->menuMapper->insertMenuItem($data); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
public function updateMenuItem($data, $id) |
85
|
|
|
{ |
86
|
|
|
$data = $this->filterMenuItem($data); |
87
|
|
|
|
88
|
|
|
return $this->menuMapper->updateMenuItem($data, $id); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
public function delete($id) |
92
|
|
|
{ |
93
|
|
|
$menu = $this->menuMapper->select(['menu_id' => $id]); |
94
|
|
|
$children = $this->menuMapper->select(['parent_id' => $id]); |
95
|
|
|
|
96
|
|
|
if ($children->count()) { |
97
|
|
|
$menu = $menu->current(); |
98
|
|
|
$this->menuMapper->update([ |
99
|
|
|
'order_no' => new Expression('order_no + ' . ($children->count() - 1))], [ |
100
|
|
|
'parent_id' => $menu->parent_id, |
101
|
|
|
'order_no' => new Expression('order_no > ' . ($menu->order_no)) |
102
|
|
|
]); |
103
|
|
|
|
104
|
|
|
foreach ($children->toArray() as $child) { |
105
|
|
|
$child['parent_id'] = $menu->parent_id; |
106
|
|
|
$child['order_no'] += ($menu->order_no - 1); |
107
|
|
|
$this->menuMapper->update($child, [ |
108
|
|
|
'menu_id' => $child['menu_id'] |
109
|
|
|
]); |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
return $this->menuMapper->delete(['menu_id' => $id]); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
public function getForSelect() |
117
|
|
|
{ |
118
|
|
|
return $this->menuMapper->forSelect(); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
public function updateMenuOrder($menuOrder) |
122
|
|
|
{ |
123
|
|
|
if (!$menuOrder) { |
124
|
|
|
return true; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
try { |
128
|
|
|
$this->menuMapper->getAdapter()->getDriver()->getConnection()->beginTransaction(); |
129
|
|
|
$this->updateLevel($menuOrder, null); |
130
|
|
|
$this->menuMapper->getAdapter()->getDriver()->getConnection()->commit(); |
131
|
|
|
} catch (\Exception $e) { |
132
|
|
|
$this->menuMapper->getAdapter()->getDriver()->getConnection()->rollback(); |
133
|
|
|
|
134
|
|
|
throw $e; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
return true; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
private function updateLevel($children, $parentId = null) |
141
|
|
|
{ |
142
|
|
|
$i=0; foreach ($children as $v) { $i++; |
143
|
|
|
if (isset($v->children)) { |
144
|
|
|
$this->menuMapper->update(['order_no' => $i, 'parent_id' => $parentId], ['menu_id' => $v->id]); |
145
|
|
|
$this->updateLevel($v->children, $v->id); |
146
|
|
|
} else { |
147
|
|
|
$this->menuMapper->update(['order_no' => $i, 'parent_id' => $parentId], ['menu_id' => $v->id]); |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
private function filterMenuItem($data) |
153
|
|
|
{ |
154
|
|
|
$filter = $this->menuFilter->getInputFilter()->setData($data); |
155
|
|
|
|
156
|
|
|
if (!$filter->isValid()) { |
157
|
|
|
throw new FilterException($filter->getMessages()); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
if (count(array_filter([$data['page_id'], $data['category_id'], $data['href']])) > 1) { |
161
|
|
|
throw new \Exception('You need to set only one link. Post, Category or Href.'); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$data = $filter->getValues(); |
165
|
|
|
|
166
|
|
|
if ($data['page_id']) { |
167
|
|
|
$page = $this->pageService->getPage($data['page_id']); |
168
|
|
|
$data['page_uuid'] = $page->getPageUuid(); |
169
|
|
|
$data['category_uuid'] = null; |
170
|
|
|
} elseif ($data['category_id']) { |
171
|
|
|
$category |
172
|
|
|
= $this->categoryService->getCategory($data['category_id']); |
173
|
|
|
$data['category_uuid'] = $category->category_uuid; |
174
|
|
|
$data['page_uuid'] = null; |
175
|
|
|
} else { |
176
|
|
|
$data['page_uuid'] = null; |
177
|
|
|
$data['category_uuid'] = null; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
unset($data['page_id'], $data['category_id']); |
181
|
|
|
|
182
|
|
|
return $data; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Returns max order number if found. |
187
|
|
|
* |
188
|
|
|
* @return integer|null |
189
|
|
|
*/ |
190
|
|
|
private function getMaxParentsOrderNumber() |
191
|
|
|
{ |
192
|
|
|
$select = $this->menuMapper |
193
|
|
|
->getSql() |
194
|
|
|
->setTable('menu') |
195
|
|
|
->select() |
196
|
|
|
->where('parent_id IS NULL') |
197
|
|
|
->columns(['order_no' => new Expression('MAX(order_no)')]) |
198
|
|
|
; |
199
|
|
|
|
200
|
|
|
$result = $this->menuMapper->selectWith($select)->current(); |
201
|
|
|
return $result->order_no; |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|