Elgg  Version master
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 ElggInvalidArgumentException(__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 
107  return $this->sort($menu, $sort_by);
108  }
109 
117  protected function setupSections(MenuItems $items) {
118  $menu = new PreparedMenu();
119 
120  $section_ids = $items->map(function (ElggMenuItem $item) {
121  return $item->getSection();
122  });
123 
124  $section_ids = array_unique(array_values($section_ids));
125 
126  foreach ($section_ids as $index => $section_id) {
127  $section_items = $items->filter(function (ElggMenuItem $item) use ($section_id) {
128  return $item->getSection() == $section_id;
129  });
130 
131  $section = new \Elgg\Menu\MenuSection();
132 
133  $section->setId($section_id);
134  $section->setPriority($index);
135  $section->fill($section_items);
136 
137  $menu->add($section);
138  }
139 
140  return $menu;
141  }
142 
150  protected function setupTrees(PreparedMenu $menu) {
151 
152  return $menu->walk(function (\Elgg\Menu\MenuSection $section) {
153 
154  $parents = [];
155  $children = [];
156  $all_menu_items = [];
157 
158  // divide base nodes from children
159  foreach ($section as $menu_item) {
160  /* @var \ElggMenuItem $menu_item */
161  $parent_name = $menu_item->getParentName();
162  $menu_item_name = $menu_item->getName();
163 
164  if (!$parent_name) {
165  // no parents so top level menu items
166  $parents[$menu_item_name] = $menu_item;
167  } else {
168  $children[$menu_item_name] = $menu_item;
169  }
170 
171  $all_menu_items[$menu_item_name] = $menu_item;
172  }
173 
174  if (empty($all_menu_items)) {
175  // empty sections can be skipped
176  return;
177  }
178 
179  if (empty($parents)) {
180  // menu items without parents? That is sad.. report to the log
181  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:NoParents');
182  _elgg_services()->logger->notice($message);
183 
184  // skip section as without parents menu can not be drawn
185  return;
186  }
187 
188  foreach ($children as $menu_item_name => $menu_item) {
189  $parent_name = $menu_item->getParentName();
190 
191  if (!array_key_exists($parent_name, $all_menu_items)) {
192  // orphaned child, inform authorities and skip to next item
193  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:OrphanedChild', [
194  $menu_item_name,
195  $parent_name
196  ]);
197  _elgg_services()->logger->notice($message);
198 
199  continue;
200  }
201 
202  if (!in_array($menu_item, $all_menu_items[$parent_name]->getData('children'))) {
203  $all_menu_items[$parent_name]->addChild($menu_item);
204  $menu_item->setParent($all_menu_items[$parent_name]);
205  } else {
206  // menu item already existed in parents children, report the duplicate registration
207  $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:DuplicateChild', [$menu_item_name]);
208  _elgg_services()->logger->notice($message);
209  }
210  }
211 
212  // convert keys to indexes for first level of tree
213  $parents = array_values($parents);
214 
215  $section->fill($parents);
216  });
217  }
218 
224  protected function findSelected() {
225  foreach ($this->items as $menu_item) {
226  if ($menu_item->getSelected()) {
227  return $menu_item;
228  }
229  }
230  }
231 
240  protected function sort(PreparedMenu $menu, $sort_by) {
241 
242  $menu->sort(function (\Elgg\Menu\MenuSection $s1, \Elgg\Menu\MenuSection $s2) {
243  return strnatcmp($s1->getID(), $s2->getID());
244  });
245 
246  $sorter = $this->getSortCallback($sort_by);
247 
248  if (!$sorter) {
249  return $menu;
250  }
251 
252  return $menu->walk(function (\Elgg\Menu\MenuSection $section) use ($sorter) {
253  $indices = array_keys($section->all());
254 
255  $section->walk(function (\ElggMenuItem $item) use ($indices, $sorter) {
256  $item->setData('original_order', array_search($item->getID(), $indices));
257  $item->sortChildren($sorter);
258  });
259 
260  $section->sort($sorter);
261  });
262  }
263 
271  protected function getSortCallback($sort_by = null) {
272  switch ($sort_by) {
273  case 'text':
274  return [self::class, 'compareByText'];
275 
276  case 'name':
277  return [self::class, 'compareByName'];
278 
279  case 'priority':
280  return [self::class, 'compareByPriority'];
281  }
282 
283  return $sort_by && is_callable($sort_by) ? $sort_by : null;
284  }
285 
295  public static function compareByText($a, $b) {
296  $at = strip_tags($a->getText());
297  $bt = strip_tags($b->getText());
298 
299  $result = strnatcmp($at, $bt);
300  if ($result === 0) {
301  return $a->getData('original_order') - $b->getData('original_order');
302  }
303 
304  return $result;
305  }
306 
315  public static function compareByName($a, $b) {
316  $an = $a->getName();
317  $bn = $b->getName();
318 
319  $result = strnatcmp($an, $bn);
320  if ($result === 0) {
321  return $a->getData('original_order') - $b->getData('original_order');
322  }
323 
324  return $result;
325  }
326 
336  public static function compareByPriority($a, $b) {
337  $aw = $a->getPriority();
338  $bw = $b->getPriority();
339 
340  if ($aw == $bw) {
341  return $a->getData('original_order') - $b->getData('original_order');
342  }
343 
344  return $aw - $bw;
345  }
346 }
if(!$items) $item
Definition: delete.php:13
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.
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.
$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:196
Elgg Menu Item.
walk(callable $callback)
Walk through members of the collection and apply a callback.
Definition: Collection.php:185
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.
Elgg Menu Builder.
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.
$menu
Form body for setting up site menu.
Definition: save.php:7
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:351
$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:141
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:154
__construct($items)
constructor