Integrate a new resource to return a nested (tree) structure of menu items

Created on 16 May 2024, over 1 year ago

Problem/Motivation

I'd like to initiate a discussion to gather opinions on the utility and feasibility of introducing a new feature, potentially controlled by a new configuration option or as a submodule. This feature would provide a new resource capable of returning a response with nested menu items that match the tree hierarchy from Drupal.

Let's consider a scenario with four links: A, B, C, and D. In this case, A is a first-level element and the parent of B; B is the parent of C; and D is another first-level element, a sibling of A, without children.

  • A
    • B
      • C
  • D

Currently, the JSON:API resource provided by the module returns a flattened representation of the menu tree. The response might look like this:

"data": [
    {
        "type": "menu_link_content--menu_id",
        "id": "menu_link_content:A",
        "attributes": {
            "menu_name": "main",
            "title": "A",
            "url": "/node/1",
            "parent": ""
        }
    },
    {
        "type": "menu_link_content--menu_id",
        "id": "menu_link_content:B",
        "attributes": {
            "menu_name": "main",
            "title": "B",
            "url": "/node/2",
            "parent": "menu_link_content:A"
        }
    },
    {
        "type": "menu_link_content--menu_id",
        "id": "menu_link_content:C",
        "attributes": {
            "menu_name": "main",
            "title": "C",
            "url": "/node/3",
            "parent": "menu_link_content:B"
        }
    },
    {
        "type": "menu_link_content--menu_id",
        "id": "menu_link_content:D",
        "attributes": {
            "menu_name": "main",
            "title": "D",
            "url": "/node/4",
            "parent": ""
        }
    }
]

This approach is great and very aligned with JSON:API; however, it places the burden of sorting out the menu hierarchy and constructing the tree on the consumers or frontend applications. To streamline this process, we could consider the introduction of a new route and a new resource that returns a nested tree structure directly from Drupal, instead of a flattened array, simplifying menu building for external applications by eliminating the need to handle hierarchy on the client side. Here's an example of how the new resource could look:

"data": [
    {
        "type": "menu_link_content--menu_id",
        "id": "menu_link_content:A",
        "attributes": {
            "menu_name": "main",
            "title": "A",
            "url": "/node/1",
            "parent": "",
            "children": [
                {
                    "type": "menu_link_content--menu_id",
                    "id": "menu_link_content:B",
                    "attributes": {
                        "menu_name": "main",
                        "title": "B",
                        "url": "/node/2",
                        "parent": "menu_link_content:A",
                        "children": [
                            {
                                "type": "menu_link_content--menu_id",
                                "id": "menu_link_content:C",
                                "attributes": {
                                    "menu_name": "main",
                                    "title": "C",
                                    "url": "/node/3",
                                    "parent": "menu_link_content:B"
                                }
                            }
                        ]
                    }
                }
            ]
        }
    },
    {
        "type": "menu_link_content--menu_id",
        "id": "menu_link_content:D",
        "attributes": {
            "menu_name": "main",
            "title": "D",
            "url": "/node/4",
            "parent": ""
        }
    }
]

In this structure, child menu items are nested within each parent menu item under the children attribute.

Proposed resolution

There was a real use case in one of my projects where I already implemented this feature, and it was highly appreciated by frontend developers. That's why instead of maintaining that separate module for this functionality, I'm proposing to incorporate it within this module. Since this module is actively maintained, I believe incorporating this new feature into it is the right approach, rather than maintaining a separate forked module. Integrating the feature here offers several advantages. For example, the new "nested items" resource will seamlessly integrate with existing functionality, leveraging what has already been built. For example, it could benefit from aspects such as compatibility with the Menu Item Extras module ✨ Add support for the Menu Item Extras module Needs review , which was recently added in version 1.2.5 β†’ .

Remaining tasks

Use this issue to discuss the approach.

I'd like to gather opinions on this proposal, particularly regarding its feasibility and usefulness. Would incorporating this feature into the module be beneficial for others? Are there any potential challenges or concerns with this approach? Your feedback will help determine the best path forward.

✨ Feature request
Status

Active

Version

1.2

Component

Code

Created by

πŸ‡¨πŸ‡΄Colombia camilo.escobar

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @camilo.escobar
  • πŸ‡ΊπŸ‡ΈUnited States kevinquillen

    +1 to this, would you be willing to share the code in a fork?

  • πŸ‡¦πŸ‡·Argentina leofishman

    I just been asked for that functionality from the frontenders, can you share your solution?

  • First commit to issue fork.
  • πŸ‡¨πŸ‡΄Colombia camilo.escobar

    camilo.escobar β†’ changed the visibility of the branch 3447727-introducing-a-new to hidden.

  • Pipeline finished with Success
    5 months ago
    #476305
  • Pipeline finished with Success
    5 months ago
    Total: 208s
    #476308
  • πŸ‡¨πŸ‡΄Colombia camilo.escobar

    I’ve created a new merge request introducing the following:

    A new submodule jsonapi_menu_items_tree, which provides a new JSON:API resource /jsonapi/menu_items_tree/{menu} (MenuTreeResource.php).

    This resource extends the original one provided by the parent module, leveraging its injected services and existing methods for handling filters, retrieving resource types and other internal logic. The only overridden method is getMenuItems(), which has been rewritten to return a tree-structured response.

    Pending:
    Add support for fields added via Menu Item Extras. This was implemented in the original resource as of version 1.2.5.
    I’m currently analyzing how to properly handle entity reference fields and the best way to support includes within the new tree-like structure.

  • πŸ‡¨πŸ‡΄Colombia camilo.escobar

    Update:

    Setting the merge request to Draft.

    I'm currently working on the following updates:

    • Add support for fields provided by Menu Item Extras, leveraging the existing implementation in the main module.
    • Integration with JSON:API Include, so that entity reference fields (includes) are added directly to the top-level resource. It makes the tree-structured response easier to consume by providing direct access to the related data.
  • πŸ‡¬πŸ‡§United Kingdom unqunq

    I only needed the text version of this and it works pretty well.

  • Status changed to Needs review 25 days ago
  • πŸ‡¨πŸ‡·Costa Rica alemadlei

    This seems to be working good for me.

  • πŸ‡¨πŸ‡·Costa Rica alemadlei
  • Pipeline finished with Success
    20 days ago
    #569638
  • Pipeline finished with Success
    20 days ago
    Total: 172s
    #570195
  • πŸ‡¨πŸ‡΄Colombia camilo.escobar

    I've worked on the points below, and the merge request is now marked as Ready. I invite maintainers to review and share their feedback.

    Menu Items Tree Resource refactored to leverage MenuItemsResource.php

    The new resource /jsonapi/menu_items_tree/{menu} now uses the same resource class as the default resource provided by the parent module (Drupal\jsonapi_menu_items\Resource\MenuItemsResource). This approach leverages existing functionality without duplicating code. Initially, it will rely on the flat array of menu items generated for the default resource, which will then be transformed into a nested tree structure by a new response subscriber ResponseSubscriber.php.

    This ensures the tree resource remains consistent with the logic built for the default resource and continues to benefit from any future updates made to that resource.

    Verified compatibility with JSON:API Include

    I have also tested compatibility with the JSON:API Include β†’ module, and it works as expected. For example, if you're using the Menu Item Extras β†’ module to add fields to your menus, such as a taxonomy reference field field_tags, you can display the referenced taxonomy terms directly in the data instead of under the "included" key. This is exactly what the JSON:API Include module is designed for, and that functionality continues to work properly even within the nested tree menu structure. For instance, suppose you have a menu item "Technology" under the parent "News", and the "Technology" item references several taxonomy terms via field_tags. Requesting /jsonapi/menu_items_tree/main?include=field_tags will yield a structure like this:

      {
          "type": "menu_link_content--main",
          "id": "menu_link_content:A",
          "title": "News",
          "url": "/news",
    	 "other fields"...
          "children": [
            {
              "type": "menu_link_content--main",
              "id": "menu_link_content:B",
              "title": "Technology",
              "url": "/news/technology",
              "field_tags": [
                {
                  "type": "taxonomy_term--tags",
                  "id": "ec5810abcded",
                  "drupal_internal__tid": 4,
                  "drupal_internal__revision_id": 4,
                  "status": true,
                  "name": "Space Travel",
                  "changed": "2025-08-11T04:43:49+00:00",
                  "other fields"...
    
  • πŸ‡¨πŸ‡·Costa Rica alemadlei

    Updete continues working for me

  • this is working for me!

  • πŸ‡ΊπŸ‡ΈUnited States jimmyfikes

    This is also working for me!

Production build 0.71.5 2024