Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
],
"require": {
"php": "^8.1",
"knplabs/knp-menu": "^3.6",
"knplabs/knp-menu": "^3.8",
"symfony/config": "^5.4 | ^6.0 | ^7.0",
"symfony/dependency-injection": "^5.4 | ^6.0 | ^7.0",
"symfony/deprecation-contracts": "^2.5 | ^3.3",
Expand Down
50 changes: 25 additions & 25 deletions docs/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,39 @@ to allow other parts of your application to add more stuff to it.

.. code-block:: php

// src/Menu/MainBuilder.php
// src/Menu/MainBuilder.php

namespace App\Menu;
namespace App\Menu;

use App\Event\ConfigureMenuEvent;
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;

class MainBuilder implements ContainerAwareInterface
{
use ContainerAwareTrait;
use App\Event\ConfigureMenuEvent;
use Knp\Menu\FactoryInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface:

public function build(FactoryInterface $factory)
{
$menu = $factory->createItem('root');
class MainBuilder
{
private $factory;
private $eventDispatcher

$menu->addChild('Dashboard', ['route' => '_acp_dashboard']);
public function __construct(FactoryInterface $factory, EventDispatcherInterface $eventDispatcher)
{
$this->factory = $factory;
$this->eventDispatcher = $eventDispatcher;
}

$this->container->get('event_dispatcher')->dispatch(
new ConfigureMenuEvent($factory, $menu),
ConfigureMenuEvent::CONFIGURE
);
public function build(array $options)
{
$menu = $this->factory->createItem('root');

return $menu;
}
}
$menu->addChild('Dashboard', ['route' => '_acp_dashboard']);

.. note::
$this->eventDispatcher->dispatch(
new ConfigureMenuEvent($this->factory, $menu),
ConfigureMenuEvent::CONFIGURE
);

This implementation assumes you use the ``BuilderAliasProvider`` (getting
your menu as ``App:MainBuilder:build``) but you could also define
it as a service and inject the ``event_dispatcher`` service as a dependency.
return $menu;
}
}

Create the Event object
-----------------------
Expand Down
134 changes: 22 additions & 112 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,145 +113,54 @@ Create your first menu!
There are two ways to create a menu: the "easy" way, and the more flexible
method of creating a menu as a service.

Method a) The Easy Way (yay)!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To create a menu, first create a new class in the ``Menu`` directory of one
of your bundles. This class - called ``Builder`` in our example - will have
one method for each menu that you need to build.

An example builder class would look like this:

.. code-block:: php

// src/Menu/Builder.php
namespace App\Menu;

use App\Entity\Blog;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;

final class Builder
{
public function __construct(
private EntityManagerInterface $em,
) {
}

public function mainMenu(FactoryInterface $factory, array $options): ItemInterface
{
$menu = $factory->createItem('root');

$menu->addChild('Home', ['route' => 'homepage']);

// findMostRecent and Blog are just imaginary examples
$blog = $this->em->getRepository(Blog::class)->findMostRecent();

$menu->addChild('Latest Blog Post', [
'route' => 'blog_show',
'routeParameters' => ['id' => $blog->getId()]
]);

// create another menu item
$menu->addChild('About Me', ['route' => 'about']);
// you can also add sub levels to your menus as follows
$menu['About Me']->addChild('Edit profile', ['route' => 'edit_profile']);

// ... add more children

return $menu;
}
}

With the standard ``knp_menu.html.twig`` template and your current page being
'Home', your menu would render with the following markup:

.. code-block:: html

<ul>
<li class="current first">
<a href="#route_to/homepage">Home</a>
</li>
<li class="current_ancestor">
<a href="#route_to/page_show/?id=42">About Me</a>
<ul class="menu_level_1">
<li class="current first last">
<a href="#route_to/edit_profile">Edit profile</a>
</li>
</ul>
</li>
</ul>

.. note::

The menu builder can be overwritten using the bundle inheritance.

To actually render the menu, just do the following from anywhere in any template:

.. configuration-block::

.. code-block:: html+jinja

{{ knp_menu_render('App:Builder:mainMenu') }}

.. code-block:: html+php

<?php echo $view['knp_menu']->render('App:Builder:mainMenu') ?>

With this method, you refer to the menu using a three-part string:
**bundle**:**class**:**method**.

If you needed to create a second menu, you'd simply add another method to
the ``Builder`` class (e.g. ``sidebarMenu``), build and return the new menu,
then render it via ``App:Builder:sidebarMenu``.

That's it! The menu is *very* configurable. For more details, see the
`KnpMenu documentation`_.

Method b) A menu builder as a service
Method a) A menu builder as a service
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For information on how to register a menu builder as a service, read
:doc:`Creating Menu Builders as Services <menu_builder_service>`.


Method c) A menu as a service
Method b) A menu as a service
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For information on how to register a service and tag it as a menu, read
:doc:`Creating Menus as Services <menu_service>`.

Method c) A menu discovered by convention
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For information on how to use menu based on the bundle alias convention,
read :doc:`Creating Menu via Naming Convention <menu_convention>`.

.. note::

To improve performances, you can :doc:`disable providers you don't need <disabling_providers>`.

Rendering Menus
---------------

Once you've set up your menu, rendering it is easy. If you've used the "easy"
way, then do the following:
Once you've set up your menu, rendering it is easy.

.. configuration-block::

.. code-block:: html+jinja

{{ knp_menu_render('App:Builder:mainMenu') }}
{{ knp_menu_render('my_main_menu') }}

.. code-block:: html+php

<?php echo $view['knp_menu']->render('App:Builder:mainMenu') ?>
<?php echo $view['knp_menu']->render('my_main_menu') ?>

Additionally, you can pass some options to the renderer:

.. configuration-block::

.. code-block:: html+jinja

{{ knp_menu_render('App:Builder:mainMenu', {'depth': 2, 'currentAsLink': false}) }}
{{ knp_menu_render('my_main_menu', {'depth': 2, 'currentAsLink': false}) }}

.. code-block:: html+php

<?php echo $view['knp_menu']->render('App:Builder:mainMenu', [
<?php echo $view['knp_menu']->render('my_main_menu', [
'depth' => 2,
'currentAsLink' => false,
]) ?>
Expand All @@ -265,12 +174,12 @@ You can also "get" a menu, which you can use to render later:

.. code-block:: html+jinja

{% set menuItem = knp_menu_get('App:Builder:mainMenu') %}
{% set menuItem = knp_menu_get('my_main_menu') %}
{{ knp_menu_render(menuItem) }}

.. code-block:: html+php

<?php $menuItem = $view['knp_menu']->get('App:Builder:mainMenu') ?>
<?php $menuItem = $view['knp_menu']->get('my_main_menu') ?>
<?php echo $view['knp_menu']->render($menuItem) ?>

If you want to only retrieve a certain branch of the menu, you can do the
Expand All @@ -281,13 +190,13 @@ beneath it.

.. code-block:: html+jinja

{% set menuItem = knp_menu_get('App:Builder:mainMenu', ['Contact']) %}
{{ knp_menu_render(['App:Builder:mainMenu', 'Contact']) }}
{% set menuItem = knp_menu_get('my_main_menu', ['Contact']) %}
{{ knp_menu_render(['my_main_menu', 'Contact']) }}

.. code-block:: html+php

<?php $menuItem = $view['knp_menu']->get('App:Builder:mainMenu', ['Contact']) ?>
<?php echo $view['knp_menu']->render(['App:Builder:mainMenu', 'Contact']) ?>
<?php $menuItem = $view['knp_menu']->get('my_main_menu', ['Contact']) ?>
<?php echo $view['knp_menu']->render(['my_main_menu', 'Contact']) ?>

If you want to pass some options to the builder, you can use the third parameter
of the ``knp_menu_get`` function:
Expand All @@ -296,12 +205,12 @@ of the ``knp_menu_get`` function:

.. code-block:: html+jinja

{% set menuItem = knp_menu_get('App:Builder:mainMenu', [], {'some_option': 'my_value'}) %}
{% set menuItem = knp_menu_get('my_main_menu', [], {'some_option': 'my_value'}) %}
{{ knp_menu_render(menuItem) }}

.. code-block:: html+php

<?php $menuItem = $view['knp_menu']->get('App:Builder:mainMenu', [], [
<?php $menuItem = $view['knp_menu']->get('my_main_menu', [], [
'some_option' => 'my_value'
]) ?>
<?php echo $view['knp_menu']->render($menuItem) ?>
Expand All @@ -314,6 +223,7 @@ More Advanced Stuff

menu_service
menu_builder_service
menu_convention
i18n
events
custom_renderer
Expand Down
41 changes: 13 additions & 28 deletions docs/menu_builder_service.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
Creating Menu Builders as Services
==================================

This bundle gives you a really convenient way to create menus by following
a convention.

However, if you want to, you can instead choose to create a service for your
menu builder. The advantage of this method is that you can inject the exact
dependencies that your menu builder needs.
This can lead to code that is more testable and also potentially
more reusable. The disadvantage is that it needs just a little more setup.

Start by creating a builder for your menu. You can stick as many menus into
a builder as you want, so you may only have one (or just a few) of these
builder classes in your application:
Expand All @@ -20,6 +11,7 @@ builder classes in your application:

namespace App\Menu;

use Knp\Menu\Attribute\AsMenuBuilder;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;

Expand All @@ -35,6 +27,7 @@ builder classes in your application:
$this->factory = $factory;
}

#[AsMenuBuilder(name: 'main')] // The name is what is used to retrieve the menu
public function createMainMenu(array $options): ItemInterface
{
$menu = $this->factory->createItem('root');
Expand All @@ -46,7 +39,13 @@ builder classes in your application:
}
}

Next, register your menu builder as service and register its ``createMainMenu`` method as a menu builder:
That's it! The menu is *very* configurable. For more details, see the
`KnpMenu documentation`_.

Next, register your menu builder as service and register its ``createMainMenu``
method as a menu builder. When using autoconfiguration, the ``#[AsMenuBuilder]``
attribute takes care of it. When not using autoconfiguration, you need to
register the menu builder by add the ``knp_menu.menu_builder`` tag:

.. code-block:: yaml

Expand All @@ -60,8 +59,7 @@ Next, register your menu builder as service and register its ``createMainMenu``

# ...

You can now render the menu directly in a template via the name given in the
``alias`` key above:
You can now render the menu directly in a template via the its name:

.. code-block:: html+jinja

Expand All @@ -80,6 +78,7 @@ is simple! Start by adding a new method to your builder:
{
// ...

#[AsMenuBuilder(name: 'sidebar')]
public function createSidebarMenu(array $options): ItemInterface
{
$menu = $this->factory->createItem('sidebar');
Expand All @@ -94,25 +93,11 @@ is simple! Start by adding a new method to your builder:
}
}

Now, create a service for *just* your new menu, giving it a new name, like
``sidebar``:

.. code-block:: yaml

# config/services.yaml
services:
app.menu_builder:
class: App\Menu\MenuBuilder
arguments: ["@knp_menu.factory"]
tags:
- { name: knp_menu.menu_builder, method: createMainMenu, alias: main } # the previous menu
- { name: knp_menu.menu_builder, method: createSidebarMenu, alias: sidebar } # Named "sidebar" this time

# ...

It can now be rendered, just like the other menu:

.. code-block:: html+jinja

{% set menu = knp_menu_get('sidebar', [], {include_homepage: false}) %}
{{ knp_menu_render(menu) }}

.. _`KnpMenu documentation`: https://github.com/KnpLabs/KnpMenu/blob/master/doc/01-Basic-Menus.md
Loading