3

I am not seeing the &Itemid=, the active menu, being added to the URL in Joomla4 like it is with Joomla3. Is this normal behaviour now with Joomla4 and what is the alternative or is it an issue?

My code does this

echo Route::_('index.php?option=com_sales&task=order.editNC&id='.(int) $item->id)

and under Joomla 3 it gets updated to this via /libraries/src/SiteRouter.php

http://buffalo.test/index.php?option=com_sales&task=order.editNC&id=3&Itemid=156

and you can see the &Itemid=156 is added, with 156 being the id of my currently active menu. Once I select the link, I am then in the record I wanted to edit and the URL has dropped the record id but the &Itemid is still there.

http://buffalo.test/index.php?option=com_sales&view=order&layout=edit&Itemid=156

and this allows the controller to use getActive() to find its way back to calling menu item.

$menu = Factory::getApplication()->getMenu();
$item = $menu->getActive();

Under Joomla 4 the menu id, &Itemid=156, is not added to the URL

http://buffalo4.test/index.php?option=com_sales&task=order.editNC&id=3

and the value stored in Input->Data->Itemid now equals 101, which is the Home menu.

/libraries/src/SiteRouter.php appears to be the source of the problem, with lots of changes between J3 and J4 but from everything I have read I can find nothing that says that Itemid was going to be dropped. Maybe it is part of the Legacy vs Modern router change from 3.8.4, with a similar issue occurring back then.

I think it could also be the related to this issue from a couple of months back, Retrieve Active Menu link in Controller (frontend - Joomla4), where the symptoms are the same.

Any help appreciated.

1
  • I think you're right with a cross over from legacy to modern. If using the new file structure for J4, try setting up your router along the same lines as site/components/com_content/src/Service/Router.php. Hope this helps. Oct 26 '21 at 3:45
3

In Joomla 4 the /library/src/Router/SiteRouter.php has a new method, buildInit, that replaces the createInit method of Joomla 3. In appearance they do the same thing, suffix the URL with the Itemid=(the value of the current/active menu item) to links on the page, however the logic has changed and it no longer handles a missing ItemId= correctly or at least in the same way as Joomla 3.

public function buildInit(&$router, &$uri)
{
    $itemid = $uri->getVar('Itemid');

    // If no Itemid and option given, merge in the current requests data
    if (!$itemid && !$uri->getVar('option'))
    {
        $uri->setQuery(array_merge($this->getVars(), $uri->getQuery(true)));
    }

    // If Itemid is given, but no option, set the option from the menu item
    if ($itemid && !$uri->getVar('option'))
    {
        if ($item = $this->menu->getItem($itemid))
        {
            $uri->setVar('option', $item->component);
        }
    }
}

In the above code if $itemid is Null or not set AND the $uri contains an valid Option param then it will never meet either of the first level IF statements and the method will complete without ever getting the $itemid.

Compared to the Joomla 3 version posted below there seems to be a block of code missing that would at least get to this statement to get the active menu id

$item = $this->menu->getItem($this->getVar('Itemid')); 

and set the $itemid

$uri->setVar('Itemid', $item->id);

In /libraries/src/Router/SiteRouter.php for Joomla 3 the createInit method where I have highlighted, <===, the lines that are missing in Joomla 4.

protected function createUri($url)
{
    // Create the URI
    $uri = parent::createUri($url);

    // Get the itemid form the URI
    $itemid = $uri->getVar('Itemid');

    if ($itemid === null)                                           <==
    {
        if ($option = $uri->getVar('option'))
        {
            $item = $this->menu->getItem($this->getVar('Itemid'));  <==

            if ($item !== null && $item->component === $option)     <==
            {
                $uri->setVar('Itemid', $item->id);                  <==
            }
        }
        else
        {
            if ($option = $this->getVar('option'))
            {
                $uri->setVar('option', $option);
            }

            if ($itemid = $this->getVar('Itemid'))
            {
                $uri->setVar('Itemid', $itemid);
            }
        }
    }
    else
    {
        if (!$uri->getVar('option'))
        {
            if ($item = $this->menu->getItem($itemid))
            {
                $uri->setVar('option', $item->component);
            }
        }
    }

    return $uri;
}

Now I need to report it as an issue with Joomla and find out if it is a problem or a feature of Joomla4

1
  • Turns out this is 'expected behaviour' although it is not documented anywhere that I could find. It is assumed that you will rewrite your Joomla 3 extension to use the service router of Joomla 4 and within that world the MenuRules class will replace the functionality that was removed. There are a couple of okay tutorials on how install a service router in your code but otherwise scare documentation, but it doesn't look trivial to me.
    – Irata
    Oct 29 '21 at 2:02
1

This is really only a temporary solution if have control over the site where your extension(s) operate as it is a hack to the core code and not suitable if your extension is used by others.

If you put this piece of code into /libraries/src/Router/SiteRouter.php at line 414 of the method buildInit you can get your working extension working again under Joomla 4.

if (!$itemid)
{
    $item   = $this->menu->getItem($this->getVar('Itemid'));

    if ($item !== null && $item->component === $uri->getVar('option'))
    {
        $uri->setVar('Itemid', $item->id);
    }
}

Understand that you are updating core code and that updates to Joomla may replace this code at some stage in the future and you may have to 'refit' the extra lines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.