Elgg  Version 4.x
ElggMenuBuilder.php
Go to the documentation of this file.
1 <?php
2 
6 
13 
17  protected $items;
18 
22  protected $selected_item = null;
23 
31  public function __construct($items) {
32  if (is_array($items)) {
33  $items = new MenuItems($items);
34  }
35 
36  if (!$items instanceof MenuItems) {
37  throw new InvalidParameterException(__CLASS__ . ' expects an instanceof of ' . MenuItems::class);
38  }
39 
40  $this->items = $items;
41  }
42 
50  public function getMenu($sort_by = 'priority') {
51 
52  $this->selected_item = $this->findSelected();
53 
54  return $this->prepare($this->filterByContext(), $sort_by);
55  }
56 
64  public function setSelected(string $item_name) {
65  $menu_item = $this->items->get($item_name);
66  if (!$menu_item instanceof ElggMenuItem) {
67  return false;
68  }
69 
70  $menu_item->setSelected();
71 
72  return true;
73  }
74 
80  public function getSelected() {
81  return $this->selected_item;
82  }
83 
89  protected function filterByContext() {
90  return $this->items->filter(function (ElggMenuItem $item) {
91  return $item->inContext();
92  });
93  }
94 
103  protected function prepare(MenuItems $items, $sort_by = 'priority') {
104  $menu = $this->setupSections($items);
105  $menu = $this->setupTrees($menu);
106  $menu = $this->sort($menu, $sort_by);
107 
108  return $menu;
109  }
110 
118  protected function setupSections(MenuItems $items) {
119  $menu = new PreparedMenu();
120 
121  $section_ids = $items->map(function (ElggMenuItem $item) {
122  return $item->getSection();
123  });
124 
125  $section_ids = array_unique(array_values($section_ids));
126 
127  foreach ($section_ids as $index => $section_id) {
128  $section_items = $items->filter(function (ElggMenuItem $item) use ($section_id) {
129  return $item->getSection() == $section_id;
130  });
131 
132  $section = new \Elgg\Menu\MenuSection();
133 
134  $section->setId($section_id);
135  $section->setPriority($index);
136  $section->fill($section_items);
137 
138  $menu->add($section);
139  }
140 
141  return $menu;
142  }
143 
151  protected function setupTrees(PreparedMenu $menu) {
152 
153  return $menu->walk(function (\Elgg\Menu\MenuSection $section) {
154 
155  $parents = [];
156  $children = [];
157  $all_menu_items = [];
158 
159  // divide base nodes from children
160  foreach ($section as $menu_item) {
161  /* @var \ElggMenuItem $menu_item */
162  $parent_name = $menu_item->getParentName();
163  $menu_item_name = $menu_item->getName();
164 
165  if (!$parent_name) {
166  // no parents so top level menu items
167  $parents[$menu_item_name] = $menu_item;
168  } else {
169  $children[$menu_item_name] = $menu_item;
170  }
171 
172  $all_menu_items[$menu_item_name] = $menu_item;
173  }
174 
175  if (empty($all_menu_items)) {
176  // empty sections can be skipped
177  return;
178  }
179 
180  if (empty($parents)) {
181  // menu items without parents? That is sad.. report to the log
182  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:NoParents');
183  _elgg_services()->logger->notice($message);
184 
185  // skip section as without parents menu can not be drawn
186  return;
187  }
188 
189  foreach ($children as $menu_item_name => $menu_item) {
190  $parent_name = $menu_item->getParentName();
191 
192  if (!array_key_exists($parent_name, $all_menu_items)) {
193  // orphaned child, inform authorities and skip to next item
194  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:OrphanedChild', [
195  $menu_item_name,
196  $parent_name
197  ]);
198  _elgg_services()->logger->notice($message);
199 
200  continue;
201  }
202 
203  if (!in_array($menu_item, $all_menu_items[$parent_name]->getData('children'))) {
204  $all_menu_items[$parent_name]->addChild($menu_item);
205  $menu_item->setParent($all_menu_items[$parent_name]);
206  } else {
207  // menu item already existed in parents children, report the duplicate registration
208  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:DuplicateChild', [$menu_item_name]);
209  _elgg_services()->logger->notice($message);
210 
211  continue;
212  }
213  }
214  // convert keys to indexes for first level of tree
215  $parents = array_values($parents);
216 
217  $section->fill($parents);
218  });
219  }
220 
226  protected function findSelected() {
227  foreach ($this->items as $menu_item) {
228  if ($menu_item->getSelected()) {
229  return $menu_item;
230  }
231  }
232  }
233 
242  protected function sort(PreparedMenu $menu, $sort_by) {
243 
244  $menu->sort(function (\Elgg\Menu\MenuSection $s1, \Elgg\Menu\MenuSection $s2) {
245  return strnatcmp($s1->getID(), $s2->getID());
246  });
247 
248  $sorter = $this->getSortCallback($sort_by);
249 
250  if (!$sorter) {
251  return $menu;
252  }
253 
254  return $menu->walk(function (\Elgg\Menu\MenuSection $section) use ($sorter) {
255  $indices = array_keys($section->all());
256 
257  $section->walk(function (\ElggMenuItem $item) use ($indices, $sorter) {
258  $item->setData('original_order', array_search($item->getID(), $indices));
259  $item->sortChildren($sorter);
260  });
261 
262  $section->sort($sorter);
263  });
264  }
265 
273  protected function getSortCallback($sort_by = null) {
274  switch ($sort_by) {
275  case 'text':
276  return [\ElggMenuBuilder::class, 'compareByText'];
277 
278  case 'name':
279  return [\ElggMenuBuilder::class, 'compareByName'];
280 
281  case 'priority':
282  return [\ElggMenuBuilder::class, 'compareByPriority'];
283  }
284 
285  return $sort_by && is_callable($sort_by) ? $sort_by : null;
286  }
287 
297  public static function compareByText($a, $b) {
298  $at = strip_tags($a->getText());
299  $bt = strip_tags($b->getText());
300 
301  $result = strnatcmp($at, $bt);
302  if ($result === 0) {
303  return $a->getData('original_order') - $b->getData('original_order');
304  }
305 
306  return $result;
307  }
308 
317  public static function compareByName($a, $b) {
318  $an = $a->getName();
319  $bn = $b->getName();
320 
321  $result = strnatcmp($an, $bn);
322  if ($result === 0) {
323  return $a->getData('original_order') - $b->getData('original_order');
324  }
325 
326  return $result;
327  }
328 
338  public static function compareByPriority($a, $b) {
339  $aw = $a->getPriority();
340  $bw = $b->getPriority();
341 
342  if ($aw == $bw) {
343  return $a->getData('original_order') - $b->getData('original_order');
344  }
345 
346  return $aw - $bw;
347  }
348 }
setupTrees(PreparedMenu $menu)
Create trees for each menu section.
getID()
Get unique item identifier within a collection.
sort(PreparedMenu $menu, $sort_by)
Sort the menu sections and trees.
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:17
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:199
walk(callable $callback)
Walk through members of the collection and apply a callback.
Definition: Collection.php:188
getSelected()
Get the selected menu item.
inContext($context= '')
Should this menu item be used given the current context.
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.
setSelected(string $item_name)
Set a menu item as selected.
prepare(MenuItems $items, $sort_by= 'priority')
Prepare a menu.
setupSections(MenuItems $items)
Group the menu items into sections.
sortChildren($sortFunction)
Sort the children.
_elgg_services()
Get the global service provider.
Definition: elgglib.php:777
$index
Definition: gallery.php:40
filter(callable $callback=null)
{Filter collection items using a custom filter Returns a new collection instance.Filterstatic} ...
Definition: Collection.php:144
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:157
__construct($items)
constructor