{"id":15287239,"url":"https://github.com/braunstetter/menu-bundle","last_synced_at":"2025-04-13T05:08:06.136Z","repository":{"id":62496771,"uuid":"389166834","full_name":"Braunstetter/menu-bundle","owner":"Braunstetter","description":"Create complex menus and let your bundles extend it. Reuse it everywhere.","archived":false,"fork":false,"pushed_at":"2024-08-25T14:16:18.000Z","size":181,"stargazers_count":10,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-13T05:07:59.429Z","etag":null,"topics":["breadcrumb","breadcrumb-navigation","breadcrumbs","menu","symfony","twig","twig-extension","twig-functions"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Braunstetter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-24T18:00:10.000Z","updated_at":"2024-10-19T16:33:30.000Z","dependencies_parsed_at":"2024-08-25T15:30:28.454Z","dependency_job_id":"19a6facf-a862-492a-bd30-81aba9d6cbd6","html_url":"https://github.com/Braunstetter/menu-bundle","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Braunstetter%2Fmenu-bundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Braunstetter%2Fmenu-bundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Braunstetter%2Fmenu-bundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Braunstetter%2Fmenu-bundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Braunstetter","download_url":"https://codeload.github.com/Braunstetter/menu-bundle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665747,"owners_count":21142123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["breadcrumb","breadcrumb-navigation","breadcrumbs","menu","symfony","twig","twig-extension","twig-functions"],"created_at":"2024-09-30T15:27:05.292Z","updated_at":"2025-04-13T05:08:06.118Z","avatar_url":"https://github.com/Braunstetter.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MenuBundle\n\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Braunstetter/menu-bundle/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/Braunstetter/menu-bundle/?branch=main)\n[![Build Status](https://app.travis-ci.com/Braunstetter/menu-bundle.svg?branch=main)](https://app.travis-ci.com/Braunstetter/menu-bundle)\n[![Total Downloads](https://poser.pugx.org/braunstetter/menu-bundle/downloads)](https://packagist.org/packages/braunstetter/menu-bundle)\n[![License](https://poser.pugx.org/braunstetter/menu-bundle/license)](https://packagist.org/packages/braunstetter/menu-bundle)\n\n## Overview\n`braunstetter/menu-bundle` is a powerful tool designed to simplify the process of creating menus in your Symfony projects. It provides an easy and intuitive interface to create and configure various types of menus.\n\nBenefits of using this bundle include:\n\n* **No matcher complexities:** You can easily activate a menu item with the selectedSubnavItem function, without it needing to be a direct child.\n* **No rendering system to struggle with:** You can use the render blocks provided by this bundle, or fetch the raw data and customize it as you wish.\n* **Reusability:** You can reuse your menu classes in different contexts (menu, breadcrumb) across your application.\n* **Expandability:** Grow your ecosystem by letting others extend your finished menus using the MenuEvent class.\n\n## Installation\n\nTo install the MenuBundle, simply run the following command:\n\n`composer require braunstetter/menu-bundle`\n\nSymfony flex does all the rest for you.\n\n## Usage\n\nAfter installation, you can create a menu by creating a class implementing `MenuInterface`. This is an example of how to define a menu:\n\n```php\n\u003c?php\n\n\nnamespace App\\Menu;\n\nuse Braunstetter\\MenuBundle\\Contracts\\MenuInterface;\nuse Braunstetter\\MenuBundle\\Events\\MenuEvent;\nuse Braunstetter\\MenuBundle\\Factory\\MenuItem;\nuse Traversable;\n\nclass MainMenu implements MenuInterface\n{\n    public function define(): Traversable\n    {\n        yield MenuItem::linkToRoute('System', 'route_to_my_system', [], 'images/svg/system.svg')-\u003esetChildren(function () {\n            yield MenuItem::linkToUrl('Section', 'https://my-site.com', MenuItem::TARGET_BLANK, 'images/svg/thunder.svg')-\u003esetChildren(function () {\n                yield MenuItem::linkToRoute('Site', 'site', [], 'images/svg/align_justify.svg');\n                yield MenuItem::linkToRoute('Dashboard', 'cp_dashboard');\n            });\n        });\n    }\n}\n```\n\n### Icons\n\nThe base path for images is just the `templates` folder of your application. But since this value will just be passed\ninto the [Twig source function](https://twig.symfony.com/doc/2.x/functions/source.html) you can also use an alias\nlike `@my_bundle/images/svg/thunder.svg`.\n\n### Twig helper\n\nInside your twig templates you can print the menu by using the `menu()` function and passing it the snake_cased class\nname.\n\n```html\n{{ menu('main_menu') }}\n```\n\nThe formatted result:\n\n![formatted menu output](docs/images/formated_base_menu.png)\n\n\u003e Note: no css is shipped with this bundle. But as you can see, a ready-to-be-styled html markup gets printed once you use the menu() function.\n\n### Types of MenuItems\n\n```\nMenuItem::linkToRoute\nMenuItem::linkToUrl\n```\n\n\u003e There are additional `MenuItem::system` and `MenuItem::section`.\n\u003e These are just convenient static methods to generate `MenuItem::linkToRoute` items with an `attr.class` set to system/section for rendering.\n\u003e `MenuItem::system` and `MenuItem::section` both can have empty routes:\n\n```php\nyield MenuItem::system('System', null, [], 'images/svg/system.svg')\n```\n\n#### LinkToRoute\n\n```php\nyield MenuItem::linkToRoute('Label', 'route-name', [], 'images/svg/align_justify.svg');\n```\n\nParameter:\n1. The shown label - you are free to translate it right here or inside your custom template.\n2. Route name.\n3. Route Parameters.\n4. Icon (optional)\n\n#### LinkToUrl\n\n\n```php\nyield MenuItem::linkToUrl('Some extern resource', 'https://my-site.com', MenuItem::TARGET_BLANK, 'images/svg/thunder.svg');\n```\nParameter:\n1. Label\n2. Absolute url\n3. Target (optional)\n4. icon (optional)\n\n\n\u003e Instead of passing a target as the third argument you can change/set the target using the `$item-\u003esetTarget(MenuItem::TARGET_BLANK)` method. \n\u003e \n\u003eThere are predefined targets to choose from set as constants inside the `'Braunstetter\\MenuBundle\\Items\\MenuItem'` class.\n\n#### Setting targets\n\nAs shown above - the `linkToUrl` item comes with the possibility to set the target attribute by passing it as a third argument to the static method.\nThis is useful because an extern/absolute Link often should be opened as a `target=\"_blank\"` link. \nBut if you want to change the target of any other link you can do this by using the `setTarget` method of any `MenuItem` implementing `MenuItemInterface`.\n\nHere is a full example - and as you can see:\n\n* **Dashboard** with `setTarget`\n* **Shop** with target set by using the third argument of the `linkTourl` method:\n\n```php\nyield MenuItem::system('System', 'test', [], 'images/svg/system.svg')\n    -\u003esetChildren(function () {\n            yield MenuItem::linkToRoute('Dashboard', 'dashboard')-\u003esetTarget(Item::TARGET_BLANK);\n            yield MenuItem::linkToUrl('Shop', 'https://my-online-shop.com', Item::TARGET_BLANK, 'images/svg/thunder.svg');\n    });\n```\n\n### Custom menu items\n\nYou are not limited to use the build in menu types. You can just create a class with a few static methods in order to create your own menu-item Factory. Or just pass a new `Braunstetter\\MenuBundle\\Items\\Item` directly:\n\n```php\nyield (new Item('My label', 'images/svg/system.svg', ['linkAttr' =\u003e ['target' =\u003e Item::TARGET_BLANK]]));\n```\n\nArguments:\n\n1. Label\n2. Icon (optional)\n3. Options (optional)\n\nIf you are using the default rendering blocks just `$options['attr']`, `$options['linkAttr']` and `$options['target']` have any impact on rendering results.\n\n\u003e Passing the target as an option is actually the same as passing it directly to $options['linkAttr]. It is just a shortcut.\n\nIf you need to pass more options it is a good time for creating a custom MenuItem class extending `Braunstetter\\MenuBundle\\Items\\Item`.\nThis way you are able to create additional properties on your class and use the `$options` to fill them inside your constructor. \n\n## Breadcrumbs\n\nThe same menu defined inside the previous chapter can be used as a breadcrumb menu by just using the `breadcrumbs()`\ntwig function.\n\n```html\n {{ breadcrumbs('main_menu') }}\n```\n\nA ready-to-be-styled markup gets rendered - divided by a caret.svg.\n\nThe main difference between the `breadcrumbs()` and the `menu()` function is, that `breadcrumbs()` just outputs a menu\ntree line, as soon as it contains some active route. Then the iteration stops and this active tree leaf gets printed.\n\n## Render menus by your own.\n\nSometimes you want to have complete control over the rendering of the menu with all the information needed in place.\nThat's why you can use the `menu_result()` and the `breadcrumbs_result()` twig functions just the same way as described\nabove. The only difference is, now you have the raw data instead of markup.\n\n```html\n{% set items = menu_result('main_menu') %}\n\n{% for item in items %}\n{# do whatever you want with the data #}\n{% endfor %}\n```\n\nIf you decide to go this way you may find it helpful to use the blocks defined in:\n\n[`menu_blocks.html.twig`](src/Resources/views/menu_blocks.html.twig)\nand [`breadcrumb_menu_blocks.html.twig`](src/Resources/views/breadcrumb_menu_blocks.html.twig)\n\n## Allow others to extend your menus with MenuEvents\n\nIf you build an ecosystem probably you would also like to give other users and / or bundles the option to extend or\nchange your menus.\n\nThis is very easy and straightforward with menu events. After you injected\nthe `Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface` into the constructor of the menu class you are able to\ndispatch events:\n\n```php\n\n$siteLinksEvent = new MenuEvent(\n    yield MenuItem::linkToRoute('Site', 'site', [], 'images/svg/align_justify.svg');\n    yield MenuItem::linkToRoute('Dashboard', 'cp_dashboard');\n);\n\n$this-\u003eeventDispatcher-\u003edispatch($siteLinksEvent, 'app.main_menu');\n\nyield from $siteLinksEvent-\u003eitems;\n```\n\nOnce you saved your menu inside a variable (`$siteLinks` in this case) you can create a `new MenuEvent($siteLinks)`. Now\nyou can dispatch events (e.g. 'app.main_menu')\n\nThe `Braunstetter\\MenuBundle\\Events\\MenuEvent` holds the menu items **and** can prepend / append menu items. You can\ncreate an EventSubscriber:\n\n```php\n\u003c?php\n\nnamespace App\\EventSubscriber;\n\nuse Braunstetter\\MenuBundle\\Events\\MenuEvent;\nuse Braunstetter\\MenuBundle\\Factory\\MenuItem;\nuse Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\nuse Generator;\n\nclass MenuSubscriber implements EventSubscriberInterface\n{\n\n    /**\n     * @param MenuEvent $event\n     */\n    public function onAppMainMenu(MenuEvent $event)\n    {\n\n        $event-\u003eprepend(function () {\n            yield MenuItem::linkToRoute('Prepended', 'other');\n        });\n\n        $event-\u003eappend(function () {\n            yield MenuItem::linkToRoute('Appended', 'other');\n        });\n\n    }\n\n    public static function getSubscribedEvents(): array\n    {\n        return [\n            'app.main_menu' =\u003e 'onAppMainMenu',\n        ];\n    }\n}\n```\n\nThis way it's very easy to build an extensible menu system for your software ecosystem.\n\n## Activate a menu item when it's not a direct children\n\nSometimes it's not just like every route is in a direct leaf of the parent, and we can just rely on these active trail.\nThen you need to tell your menu system 'somehow' to activate a seemingly unrelated menu item (and to activate its active\ntrail).\n\nInstead of going crazy with a custom Matcher you can just do that:\n\n```\n{% set selectedSubnavItem = 'snake_cased_item_label' %}\n\n// you can also pass an array of items to activate multiple items at once\n// selectedSubnavItem = ['snake_cased_item_label', 'another_snake_cased_item_label']\n\n{{ menu('main_menu') }}\n```\n\nThe Menu item matching this route will be active and all parents will be inside the active trail.\n\n\u003e Note: `selectedSubnavItem` has to be inside the global twig scope - therefore define it in between your blocks or pass it as a variable from inside your controller.\n\n\u003e The value of the `selectedSubnavItem` has to be equal to the handle of the MenuItem (snake cased label). \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraunstetter%2Fmenu-bundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbraunstetter%2Fmenu-bundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraunstetter%2Fmenu-bundle/lists"}