Resolvers & Active State
Resolvers apply cross-cutting state without mixing request/session logic into menu construction. Each resolver inspects every item and sets its active and/or visible state; chained together with a ResolverCollection, they run in order, so later resolvers see what earlier ones decided.
URL Resolvers
use Menu\Resolver\Psr7UrlResolver;
use Menu\Resolver\UrlArrayResolver;
$menu->resolve(new Psr7UrlResolver($request));
$menu->resolve(new UrlArrayResolver($request));UrlArrayResolver supports fuzzy matching, so a route like:
['controller' => 'Articles', 'action' => 'view']can match requests with additional passed parameters such as /articles/view/42 when the item uses fuzzy => true.
It also supports named routes:
$menu->addItem('View', ['_name' => 'articles:view']);Section Resolver
SectionResolver activates items from request parameter subsets:
use Menu\Resolver\SectionResolver;
$menu->addItem('Admin Articles', '/admin/articles', [
'data' => [
'section' => [
'prefix' => 'Admin',
'controller' => 'Articles',
],
],
]);
$menu->resolve(new SectionResolver($request));Regex Resolver
RegexResolver activates items whose regular expression (stored in data['match']) matches the current request path — handy for lighting up a whole URL section that a route-array match can't express. A value may be a single pattern or a list; invalid patterns are ignored.
use Menu\Resolver\RegexResolver;
$menu->addItem('Admin', '/admin', [
'data' => ['match' => '#^/admin/(users|roles)#'],
]);
$menu->resolve(new RegexResolver($request->getUri()->getPath()));Pass a second argument to read patterns from a different data key, e.g. new RegexResolver($path, 'activePattern').
Login Visibility Resolver
Mark items with metadata:
$menu->addItem('Login', '/login', ['data' => ['auth' => 'loggedOut']]);
$menu->addItem('Profile', '/profile', ['data' => ['auth' => 'loggedIn']]);Then resolve:
use Menu\Resolver\LoggedInResolver;
$menu->resolve(new LoggedInResolver($identity !== null));Authorization and Callback Resolvers
use Menu\Item\ItemInterface;
use Menu\Resolver\AuthorizationResolver;
use Menu\Resolver\CallbackResolver;
use Menu\Resolver\ResolverContext;
$menu->resolve(new AuthorizationResolver(
static function (ItemInterface $item, ResolverContext $context): ?bool {
if ($item->getData('permission') === null) {
return null;
}
return $authorization->can($identity, (string)$item->getData('permission'));
}
));
$menu->resolve(new CallbackResolver(
static function (ItemInterface $item, ResolverContext $context): void {
if ($context->getDepth() > 1) {
$item->setExpanded();
}
}
));Permission Resolver
For Authorization-style can() services there is also a convenience resolver:
use Menu\Resolver\PermissionResolver;
$menu->addItem('Admin', '/admin', [
'data' => ['permission' => 'admin.access'],
]);
$menu->resolve(new PermissionResolver($authorization, $identity));Multiple Resolvers
use Menu\Resolver\ResolverCollection;
$menu->resolve(
(new ResolverCollection())
->add(new UrlArrayResolver($request))
->add(new LoggedInResolver($identity !== null))
);Adding Resolvers to the Defaults
A custom resolver replaces the defaults
Passing a resolver option replaces the built-in URL resolvers, so you lose automatic active-state matching. To keep the defaults and add your own (for example a visibility resolver), use additionalResolvers instead — they run after the URL resolvers.
use Menu\Item\ItemInterface;
use Menu\Resolver\AuthorizationResolver;
echo $this->Menu->render('main', [
'additionalResolvers' => [
new AuthorizationResolver(static function (ItemInterface $item): ?bool {
return $item->getData('adminOnly') ? $isAdmin : null;
}),
],
]);Depth-Limited Resolution
When rendering through the helper, you can limit how deep automatic URL resolution should scan:
echo $this->Menu->render('main', [
'resolveDepth' => 1,
]);