phpwordpressmenunavigationshortcode

Custom Block / Shortcode / Search in Wordpress Block-based Navigation Menu?


I have a wordpress site, currently locally hosted with xampp.

Context Structure:

Child of Twenty Twenty-Five theme (block-based, doesn't support menus in Appearance-> Menu). In the site I have a "Products" page with a child "Product Details" page. In products I have a shortcode to display content (a title and a paragraph) dynamically based on a custom table "product_list" in the database. Clicking on a product title I pass a query to "product-detail" page, in this child page I have another shortcode to dynamically display the product details using the query as key. Everything works wonderfully.

The Problem:

This results in a general page structure: Products page (parent) and Product Details page (child), which in the navigation menu shows as a dropdown menu from Products. Example:Navigation Example. I can't seem to find a way to either enter the navigation arbitrarily, replace blocks in it, or pinpoint a shortcode. (See later what I tried).

What I want:

I want to be able to access the navigation menu to dynamically add links in the Products dropdown, so I can add an entry for each product and/or group them by properties, for example each product has a "size" property, and I can group the products based on that property.

What I tried:

I tried to rebuild the entire navigation block, but I just couldn't even intercept it and super messy code. Then I tried to loop blocks in from the rendered page, but that didn't work either.

The approach I wanted to use was to search for the <li> <a> Product Details </a> </li> in the navigation menu and replace it with my own content, probably a shortcode. Right now I was able to add a shortcode to the navigation menu, but only at the end with offsetSet:

function theme_render_shortcode_block() {
    return do_shortcode( '[navigation_product_list]' );
}

function theme_register_shortcode_block() {
    if ( function_exists( 'register_block_type' ) ) {
        register_block_type( 'theme/products-shortcode-block', array(
            'title' => 'Products Shortcode Block',
            'icon' => 'shortcode',
            'render_callback' => 'theme_render_shortcode_block'
        ) );
    }
}
add_action( 'init', 'theme_register_shortcode_block' );

add_filter( 'block_core_navigation_render_inner_blocks', function( $inner_blocks ) {
    $menu_item = new WP_Block( array(
        'blockName' => 'theme/products-shortcode-block'
    ) );

    $inner_blocks->offsetSet( null, $menu_item );

    return $inner_blocks;
} );

This makes me put a shortcode into the navigation block, into ALL navigation blocks, even the other ones I use in the footer for example, that don't use the wp-block-page-list. I want it to put it inside the wp-block-page-list block too, so I tried something like this to replace the Detail Product page link with the shortcode:

add_filter( 'block_core_navigation_render_inner_blocks', function( $inner_blocks ) {
    $target_menu_item_label = 'Product Details';

    foreach ( $inner_blocks as $index => $block ) {
        // Find the block with "Product Details" label.
        if ( isset($block->parsed_block['attrs']['label']) && $block->parsed_block['attrs']['label'] === $target_menu_item_label ) {

            // Replace it with shortcode block.
            $replacement_block = new WP_Block( array(
                'blockName' => 'theme/products-shortcode-block'
            ) );

            $inner_blocks[ $index ] = $replacement_block;

            break;
        }
    }
    return $inner_blocks;
} );

I tried to target the "Product Details" label but it doesn't do anything. For context the products-shortcode-block runs a shortcode that creates a with dynamic content based on the database table. I want to place it in the right spot.


Solution

  • to add dynamically elements in the menu, you start by identifying the parent element. in my example, I add a css class parent_products_link for this element.

    then you can add children elements like that :

    add_filter("block_core_navigation_render_inner_blocks", function ($inner_blocks) {
        
        foreach ($inner_blocks as $index => $block) {
            
            // searching the special css class
            
            $class = $block->parsed_block["attrs"]["className"] ?? NULL;
            
            if ("parent_products_link" !== $class) {
                // not the searched element
                continue;
            }
            
            
            // add the child element
            
            $block->parsed_block["innerBlocks"][] = [
                "blockName" => "core/navigation-link",
                "attrs" => [
                    "label" => "label of the link",
                    "url" => home_url("/special/url"),
                ]
            ];
            
            
            // refresh the block after adding children
            $block->refresh_parsed_block_dependents();
            
        }
        
        
        return $inner_blocks;
        
    });