View source
<?php
namespace Drupal\colossal_menu\Menu;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Menu\MenuTreeStorageInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
class MenuTreeStorage implements MenuTreeStorageInterface {
const MAX_DEPTH = 20;
protected $connection;
protected $storage;
protected $currentRouteMatch;
protected $table;
public function __construct(Connection $connection, EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $current_route_match, string $entity_type, string $table) {
$this->connection = $connection;
$this->storage = $entity_type_manager
->getStorage($entity_type);
$this->currentRouteMatch = $current_route_match;
$this->table = $table;
}
public function maxDepth() {
return self::MAX_DEPTH;
}
public function resetDefinitions() {
}
public function rebuild(array $definitions) {
}
public function load($id) {
return $this->storage
->load($id);
}
public function loadMultiple(array $ids) {
return $this->storage
->loadMultiple($ids);
}
public function loadByProperties(array $properties) {
return $this->storage
->loadByProperties($properties);
}
public function loadByRoute($route_name, array $route_parameters = [], $menu_name = NULL) {
$url = new Url($route_name, $route_parameters);
$query = $this->storage
->getQuery();
$query
->condition('link__uri', $url
->getUri());
if ($menu_name) {
$query
->condition('menu', $menu_name);
}
return $query
->execute();
}
public function save(array $definition) {
return $this->storage
->create($definition);
}
public function delete($id) {
return $this->storage
->delete($this->storage
->load($id));
}
public function loadTreeData($menu_name, MenuTreeParameters $parameters) {
$query = $this->connection
->select($this->table, 't')
->fields('t', [
'ancestor',
'descendant',
'depth',
])
->condition('e.menu', $menu_name)
->orderBy('t.depth', 'ASC')
->orderBy('e.weight', 'ASC');
$query
->innerJoin($this->storage
->getEntityType()
->get('base_table'), 'e', 't.ancestor = e.id');
if ($parameters->root) {
$query
->condition('t.ancestor', $parameters->root);
}
if ($parameters->minDepth > 1) {
$query
->condition('t.depth', '>=', $parameters->minDepth - 1);
}
if ($parameters->maxDepth) {
$query
->condition('t.depth', '<=', $parameters->maxDepth);
}
$result = $query
->execute();
$flat = [];
$depth = [];
while ($row = $result
->fetchObject()) {
$flat[$row->ancestor][] = $row->descendant;
if (isset($depth[$row->descendant]) && $row->depth > $depth[$row->descendant]) {
$depth[$row->descendant] = $row->depth;
}
elseif (!isset($depth[$row->descendant])) {
$depth[$row->descendant] = $row->depth;
}
}
$links = $this
->loadMultiple(array_keys($flat));
$routes = [];
foreach ($links as $link) {
if (!$link
->isExternal() && ($name = $link
->getRouteName())) {
$routes[$link
->id()] = $name;
}
}
$tree = $this
->treeDataRecursive($flat, $links, $depth, $routes);
return [
'tree' => $tree,
'route_names' => $routes,
];
}
protected function treeDataRecursive(array $flat, array $links, array $depth, array $routes) {
uasort($flat, function ($a, $b) {
return count($a) - count($b);
});
$tree = [];
foreach ($flat as $id => $decendents) {
foreach ($decendents as $decendent) {
if ($id == $decendent) {
$active = FALSE;
if (isset($routes[$id]) && $this->currentRouteMatch
->getRouteName() == $routes[$id]) {
$active = TRUE;
}
$tree[$id] = [
'link' => $links[$id],
'has_children' => FALSE,
'subtree' => [],
'depth' => $depth[$id] + 1,
'in_active_trail' => $active,
];
}
else {
if (isset($tree[$decendent])) {
$tree[$id]['has_children'] = TRUE;
$tree[$id]['in_active_trail'] = $tree[$decendent]['in_active_trail'];
$tree[$id]['subtree'][$decendent] = $tree[$decendent];
unset($tree[$decendent]);
if (count($tree[$id]['subtree']) > 1) {
uasort($tree[$id]['subtree'], function ($a, $b) {
return $a['link']
->getWeight() < $b['link']
->getWeight() ? -1 : 1;
});
}
}
}
}
}
if (count($tree) > 1) {
uasort($tree, function ($a, $b) {
return $a['link']
->getWeight() < $b['link']
->getWeight() ? -1 : 1;
});
}
return $tree;
}
public function loadAllChildren($id, $max_relative_depth = NULL) {
$query = $this->connection
->select($this->table, 't');
$query
->fields('t', [
'descendant',
]);
$query
->condition('t.ancestor', $id);
if ($max_relative_depth) {
$query
->condition('t.depth', '<=', $max_relative_depth);
}
$query
->orderBy('t.depth', 'ASC');
$ids = $query
->execute()
->fetchCol();
return $this->storage
->getQuery()
->condition('id', $ids)
->orderBy('weight', 'ASC');
}
public function getAllChildIds($id) {
return $this->connection
->select($this->table, 't')
->fields('t', [
'descendant',
])
->condition('t.ancestor', $id)
->execute()
->fetchCol();
}
public function loadSubtreeData($id, $max_relative_depth = NULL) {
$link = $this
->load($id);
$params = new MenuTreeParameters();
$params->root = $id;
$params
->setMaxDepth($max_relative_depth);
return $this
->loadTreeData($link
->getMenuName(), $params);
}
public function getRootPathIds($id) {
return $this->connection
->select($this->table, 't')
->fields('t', [
'ancestor',
])
->condition('t.descendant', $id)
->orderBy('t.depth', 'DESC')
->execute()
->fetchCol();
}
public function getExpanded($menu_name, array $parents) {
$query = $this->connection
->select($this->table, 't')
->fields('t', [
'descendant',
])
->condition('t.ancestor', $parents)
->condition('e.menu', $menu_name)
->orderBy('t.depth', 'ASC')
->orderBy('e.weight', 'ASC');
$query
->innerJoin($this->storage
->getEntityType()
->get('base_table'), 'e', 't.ancestor = e.id');
return $query
->execute()
->fetchCol();
}
public function getSubtreeHeight($id) {
return $this->conneciton
->select($this->table, 't')
->fields('t', [
'depth',
])
->condition('t.descendant', $id)
->orderBy('t.depth', 'DESC')
->limit(0, 1)
->execute()
->fetchField();
}
public function menuNameInUse($menu_name) {
$links = $this->storage
->loadByProperties([
'menu' => $menu_name,
]);
return empty($links);
}
public function getMenuNames() {
return $this->connection
->select($this->storage
->getEntityType()
->get('base_table'), 'e')
->distinct()
->fields('e', [
'menu',
])
->execute()
->fetchCol();
}
public function countMenuLinks($menu_name = NULL) {
$query = $this->connection
->select($this->storage
->getEntityType()
->get('base_table'), 'e')
->count();
if ($menu_name) {
$query
->condition('e.menu', $menu_name);
}
return $query
->execute();
}
}