Elgg  Version 3.0
ElggMenuBuilder.php
Go to the documentation of this file.
1 <?php
2 
5 
14 
18  protected $items;
19 
23  protected $selected_item = null;
24 
32  public function __construct($items) {
33  if (is_array($items)) {
34  $items = new MenuItems($items);
35  }
36 
37  if (!$items instanceof MenuItems) {
38  throw new InvalidParameterException(__CLASS__ . ' expects an instanceof of ' . MenuItems::class);
39  }
40 
41  $this->items = $items;
42  }
43 
51  public function getMenu($sort_by = 'priority') {
52 
53  $this->selected_item = $this->findSelected();
54 
55  return $this->prepare($this->filterByContext(), $sort_by);
56  }
57 
63  public function getSelected() {
64  return $this->selected_item;
65  }
66 
72  protected function filterByContext() {
73  return $this->items->filter(function (ElggMenuItem $item) {
74  return $item->inContext();
75  });
76  }
77 
86  protected function prepare(MenuItems $items, $sort_by = 'priority') {
87  $menu = $this->setupSections($items);
88  $menu = $this->setupTrees($menu);
89  $menu = $this->sort($menu, $sort_by);
90 
91  return $menu;
92  }
93 
101  protected function setupSections(MenuItems $items) {
102  $menu = new PreparedMenu();
103 
104  $section_ids = $items->map(function (ElggMenuItem $item) {
105  return $item->getSection();
106  });
107 
108  $section_ids = array_unique(array_values($section_ids));
109 
110  foreach ($section_ids as $index => $section_id) {
111  $section_items = $items->filter(function (ElggMenuItem $item) use ($section_id) {
112  return $item->getSection() == $section_id;
113  });
114 
115  $section = new \Elgg\Menu\MenuSection();
116 
117  $section->setId($section_id);
118  $section->setPriority($index);
119  $section->fill($section_items);
120 
121  $menu->add($section);
122  }
123 
124  return $menu;
125  }
126 
134  protected function setupTrees(PreparedMenu $menu) {
135 
136  return $menu->walk(function (\Elgg\Menu\MenuSection $section) {
137 
138  $parents = [];
139  $children = [];
140  $all_menu_items = [];
141 
142  // divide base nodes from children
143  foreach ($section as $menu_item) {
144  /* @var \ElggMenuItem $menu_item */
145  $parent_name = $menu_item->getParentName();
146  $menu_item_name = $menu_item->getName();
147 
148  if (!$parent_name) {
149  // no parents so top level menu items
150  $parents[$menu_item_name] = $menu_item;
151  } else {
152  $children[$menu_item_name] = $menu_item;
153  }
154 
155  $all_menu_items[$menu_item_name] = $menu_item;
156  }
157 
158  if (empty($all_menu_items)) {
159  // empty sections can be skipped
160  return;
161  }
162 
163  if (empty($parents)) {
164  // menu items without parents? That is sad.. report to the log
165  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:NoParents');
166  _elgg_services()->logger->notice($message);
167 
168  // skip section as without parents menu can not be drawn
169  return;
170  }
171 
172  foreach ($children as $menu_item_name => $menu_item) {
173  $parent_name = $menu_item->getParentName();
174 
175  if (!array_key_exists($parent_name, $all_menu_items)) {
176  // orphaned child, inform authorities and skip to next item
177  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:OrphanedChild', [
178  $menu_item_name,
179  $parent_name
180  ]);
181  _elgg_services()->logger->notice($message);
182 
183  continue;
184  }
185 
186  if (!in_array($menu_item, $all_menu_items[$parent_name]->getData('children'))) {
187  $all_menu_items[$parent_name]->addChild($menu_item);
188  $menu_item->setParent($all_menu_items[$parent_name]);
189  } else {
190  // menu item already existed in parents children, report the duplicate registration
191  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:DuplicateChild', [$menu_item_name]);
192  _elgg_services()->logger->notice($message);
193 
194  continue;
195  }
196  }
197  // convert keys to indexes for first level of tree
198  $parents = array_values($parents);
199 
200  $section->fill($parents);
201  });
202  }
203 
209  protected function findSelected() {
210  foreach ($this->items as $menu_item) {
211  if ($menu_item->getSelected()) {
212  return $menu_item;
213  }
214  }
215  }
216 
225  protected function sort(PreparedMenu $menu, $sort_by) {
226 
227  $menu->sort(function (\Elgg\Menu\MenuSection $s1, \Elgg\Menu\MenuSection $s2) {
228  return strnatcmp($s1->getId(), $s2->getId());
229  });
230 
231  $sorter = $this->getSortCallback($sort_by);
232 
233  if (!$sorter) {
234  return $menu;
235  }
236 
237  return $menu->walk(function (\Elgg\Menu\MenuSection $section) use ($sorter) {
238  $indices = array_keys($section->all());
239 
240  $section->walk(function (\ElggMenuItem $item) use ($indices, $sorter) {
241  $item->setData('original_order', array_search($item->getId(), $indices));
242  $item->sortChildren($sorter);
243  });
244 
245  $section->sort($sorter);
246  });
247  }
248 
256  protected function getSortCallback($sort_by = null) {
257  switch ($sort_by) {
258  case 'text':
259  return [\ElggMenuBuilder::class, 'compareByText'];
260 
261  case 'name':
262  return [\ElggMenuBuilder::class, 'compareByName'];
263 
264  case 'priority':
265  return [\ElggMenuBuilder::class, 'compareByPriority'];
266  }
267 
268  return $sort_by && is_callable($sort_by) ? $sort_by : null;
269  }
270 
280  public static function compareByText($a, $b) {
281  $at = strip_tags($a->getText());
282  $bt = strip_tags($b->getText());
283 
284  $result = strnatcmp($at, $bt);
285  if ($result === 0) {
286  return $a->getData('original_order') - $b->getData('original_order');
287  }
288 
289  return $result;
290  }
291 
300  public static function compareByName($a, $b) {
301  $an = $a->getName();
302  $bn = $b->getName();
303 
304  $result = strnatcmp($an, $bn);
305  if ($result === 0) {
306  return $a->getData('original_order') - $b->getData('original_order');
307  }
308 
309  return $result;
310  }
311 
321  public static function compareByPriority($a, $b) {
322  $aw = $a->getPriority();
323  $bw = $b->getPriority();
324 
325  if ($aw == $bw) {
326  return $a->getData('original_order') - $b->getData('original_order');
327  }
328 
329  return $aw - $bw;
330  }
331 }
setupTrees(PreparedMenu $menu)
Create trees for each menu section.
sort(PreparedMenu $menu, $sort_by)
Sort the menu sections and trees.
getId()
Get unique item identifier within a collection.
getSortCallback($sort_by=null)
Get callback function for sorting.
if(!$items) $item
Definition: delete.php:13
filterByContext()
Select menu items for the current context.
setData($key, $value=null)
Set a data key/value pair or a set of key/value pairs.
$menu
Admin sidebar – just outputs the page menus.
Definition: sidebar.php:6
$children
Definition: item.php:20
static compareByText($a, $b)
Compare two menu items by their display text HTML tags are stripped before comparison.
map(callable $callback)
{Walk through all items in the collection and apply a callback.Mappermixed}
Definition: Collection.php:192
walk(callable $callback)
Walk through members of the collection and apply a callback.
Definition: Collection.php:181
getSelected()
Get the selected menu item.
inContext($context= '')
Should this menu item be used given the current context.
Configuration exception.
static compareByName($a, $b)
Compare two menu items by their identifiers.
getSection()
Get the section identifier.
Represents a menu that has been broken down into sections, with menu hierarchy trees setup...
findSelected()
Find the menu item that is currently selected.
prepare(MenuItems $items, $sort_by= 'priority')
Prepare a menu.
setupSections(MenuItems $items)
Group the menu items into sections.
sortChildren($sortFunction)
Sort the children.
class
Definition: placeholder.php:21
_elgg_services()
Get the global service provider.
Definition: elgglib.php:1292
$index
Definition: gallery.php:47
filter(callable $callback=null)
{Filter collection items using a custom filter Returns a new collection instance.Filterstatic} ...
Definition: Collection.php:137
getMenu($sort_by= 'priority')
Get a prepared menu.
A collection of menu items.
Definition: MenuItems.php:10
static compareByPriority($a, $b)
Compare two menu items by their priority.
sort(callable $callback=null)
{Sort fields using custom callable If not provided, will sort items by priority.Sorterstatic} ...
Definition: Collection.php:150
__construct($items)
constructor