Dynamic menus for CodeIgniter 4
- Install with Composer:
> composer require tatter/menus
- Create your menus by extending
Tatter\Menus\Menu
- Add your menu aliases to
Config\Menus
- Apply
MenuFilter
to all routes that need menus
Menus provides dynamic menus across your application. Menus organizes and injects the menu content, so you can focus on building.
Install easily via Composer to take advantage of CodeIgniter 4's autoloading capabilities and always be up-to-date:
> composer require tatter/menus
Or, install manually by downloading the source files and adding the directory to
app/Config/Autoload.php
.
The library's default behavior can be altered by extending its config file. Copy examples/Menus.php to app/Config/ and follow the instructions in the comments. If no config file is found in app/Config the library will use its own.
Menus is built on Spatie\Menu
with all of its wonderful, dynamic and fluent functionality.
Use their documentation to craft your menus as simple or complex as you like:
Create your menus by extending Tatter\Menus\Menu
. You will notice in the source code that
Menu
requires you to provide one method: public function __toString(): string;
. You may use the
supplied $builder
property to access the underlying Spatie\Menu
to build your menu,
or provide your own HTML code or view()
return. Some examples:
class MainMenu extends \Tatter\Menus\Menu
{
public function __toString(): string
{
return $this->builder
->link(site_url('/'), 'Home')
->link(site_url('/about'), 'About')
->html('<hr>')
->link(site_url('/contact'), 'Contact')
->render();
}
}
class FruitMenu extends \Tatter\Menus\Menu
{
public function __toString(): string
{
return view('menus/fruit', ['active' => 'banana']);
}
}
Note: $builder
is initialized with "set active" to the current URL. You may call setActive()
again to remove or change the active menu item. Due to a limitation in Spatie\Menu
with mixing
relative and absolute URLs you must supply full URL values (e.g. with site_url()
) to your
Menu
if you want to use this default "active" URL.
Since Menu
is Stringable
it can be used in your view or layout files as is.
However, Menus also comes with a Controller Filter
that you can use to inject menu content directly into your responses. First you need to create
an alias for each Menu
class you would like to use. Create app/Config/Menus.php (or
start with a copy from the examples folder) and add your menu classes to the $aliases
array. For example:
class Menus extends \Tatter\Menus\Config\Menus
{
/**
* Menu class aliases.
*
* @var array<string, string>
*/
public $aliases = [
'main' => \App\Menus\MainMenu::class,
'fruit' => \ShopModule\FruitMenu::class,
];
}
Once aliases are set up you can pass them as an argument to the MenuFilter
for any route:
$routes->add('shop/(:any)', 'ShopModule\ShopController::show/$1', ['filter' => 'menus:fruit']);
Then in your view or layout put the placeholder token with the name of the alias target in double curly braces:
<html>
<body>
{{main}}
<h1>Fruit Shop</h1>
{{fruit}}
...
Note that sometimes it is preferable to apply the filter in bulk using app/Config/Filters.php.
Unfortunately parameters are not yet supported
in Config\Filters
, but you can work around this by creating your own parameter-specific Filter:
<?php namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Tatter\Menus\Filters\MenusFilter;
class MainMenuFilter extends MenusFilter
{
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): ?ResponseInterface
{
return parent::after($request, $response, ['main', 'fruit']);
}
}
Menus
comes with some pre-made menus which can be used immediately or built on to create
your own variants. All menus are in the Tatter\Menus\Menus
namespace and extend the Menu
class so can be used with the Filter or as any other menu you would make.
The BreadcrumbsMenu
is a special menu, using horizontal-style navigation links for nested
content. This menu comes pre-styled for Bootstrap
and defaults to the segments retrieved from the framework's IncomingRequest::$uri
, but
you may provide your own using the static methods set
, get
, push
, and pop
. Additionally,
BreadcrumbsMenu::discover()
will attempt to create a default menu. All these methods use
the Breadcrumb
class, a simple wrapper for the URL and display value.
For example:
use Tatter\Menus\Breadcrumb;
use Tatter\Menus\Menus\BreadcrumbsMenu;
class Users extends Controller
{
public function show(int $userId)
{
// Get the User
$user = model('UserModel')->find($userId);
// Start with the default breadcrumbs
BreadcrumbsMenu::discover();
// Pop off the numeric last segment
BreadcrumbsMenu::pop();
// Replace it with the user's name
BreadcrumbsMenu::push(new Breadcrumb(current_url(), $user->name));
return view('users/show', ['user' => $user]);
}
}
... if you have the filter in place the rest is handled for you.