Context: I have a component that works fine in J3 and I want to migrate it to J4.
Problem: When I create a menu (Joomla main menu) on the frontend view of the component, in J4 only, the URL is not SEF => http://mysite.com/alias-menu?view=view-name
instead of http://mysite.com/alias-menu
(without parameter by default)
The URL works if I manually remove the parameter, but I want for the main menu of Joomla to generate a link properly (SEF) without any parameter by default.
Based on documentation and other Joomla components, I have added a Router.php, but no change and it is never called. Here is the code of the router.php
/com_componentname/src/Service/Router.php
<?php
namespace Componentname\Component\Componentname\Site\Service;
defined('_JEXEC') or die;
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Component\Router\Rules\MenuRules;
//use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Componentname\Component\Componentname\Site\Service\ComponentnameNomenuRules as NomenuRules;
use Joomla\CMS\Factory;
use Joomla\CMS\Categories\Categories;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Categories\CategoryFactoryInterface;
use Joomla\CMS\Categories\CategoryInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\CMS\Menu\AbstractMenu;
class Router extends RouterView
{
protected $noIDs = false;
private $categoryFactory;
private $categoryCache = [];
private $db;
public function __construct(SiteApplication $app, AbstractMenu $menu, CategoryFactoryInterface $categoryFactory, DatabaseInterface $db)
{
$params = Factory::getApplication()->getParams('com_componentname');
$this->noIDs = (bool) $params->get('sef_ids');
$this->categoryFactory = $categoryFactory;
$this->db = $db;
$viewname = new RouterViewConfiguration('viewname');
$this->registerView($viewname);
die('here router construct'); //--> never called
parent::__construct($app, $menu);
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
}
/**
* @param array A named array
* @return array
*/
function ViewnameBuildRoute(&$query) {
die('here router ViewnameBuildRoute'); //--> never called
$segments = array();
if (isset($query['task'])) {
$segments[] = implode('/', explode('.', $query['task']));
unset($query['task']);
}
if (isset($query['view'])) {
$segments[] = $query['view'];
unset($query['view']);
}
if (isset($query['id'])) {
$segments[] = $query['id'];
unset($query['id']);
}
return $segments;
}
function ViewnameParseRoute($segments) {
die('here router ViewnameParseRoute'); //--> never called
$vars = array();
// view is always the first element of the array
$vars['view'] = array_shift($segments);
while(!empty($segments)) {
$segment = array_pop($segments);
if (is_numeric($segment)) {
$vars['id'] = $segment;
} else {
$vars['task'] = $vars['view'] . '.' . $segment;
}
}
return $vars;
}
}
/com_componentname/src/Service/ComponentnameNomenuRules.php
<?php
namespace Componentname\Component\Componentname\Site\Service;
defined('_JEXEC') or die;
use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\Rules\RulesInterface;
/**
* Rule to process URLs without a menu item
*
* @since 3.4
*/
class ComponentnameNomenuRules implements RulesInterface
{
protected $router;
public function __construct(RouterView $router)
{
$this->router = $router;
}
public function preprocess(&$query)
{
//$test = 'Test';
}
/**
* Parse a menu-less URL
*
* @param array &$segments The URL segments to parse
* @param array &$vars The vars that result from the segments
*
* @return void
*
* @since 3.4
*/
public function parse(&$segments, &$vars)
{
$vars['view'] = 'viewname';
$vars['id'] = substr($segments[0], strpos($segments[0], '-') + 1);
array_shift($segments);
array_shift($segments);
return;
}
public function build(&$query, &$segments)
{
if (!isset($query['view']) || (isset($query['view']) && $query['view'] !== 'viewname') || isset($query['format']))
{
return;
}
$segments[] = $query['view'] . '-' . $query['id'];
// the last part of the url may be missing
if (isset($query['slug'])) {
$segments[] = $query['slug'];
unset($query['slug']);
}
unset($query['view']);
unset($query['id']);
}
}
/administrator/components/com_componentname/services/provider.php
<?php
// no direct access
defined('_JEXEC') or die;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Joomla\CMS\Extension\Service\Provider\RouterFactory;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->registerServiceProvider(new MVCFactory('\\Componentname\\Component\\Componentname'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Componentname\\Component\\Componentname'));
$container->registerServiceProvider(new RouterFactory('\\Componentname\\Component\\Componentname'));
$container->set(
ComponentInterface::class,
function (Container $container)
{
$component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
//tried to register Router but Internal error 500
//$component->setRouterFactory($container->get(RouterFactoryInterface::class));
return $component;
}
);
}
};
I have an error 500 if I uncomment line "$component->setRouterFactory($container->get(RouterFactoryInterface::class));" in provider.php above. Appache error : Fatal error: Maximum execution time of 30+2 seconds exceeded (terminated) in /website/administrator/components/my_component/services/provider.php on line 35
In general terms, what triggers the router to call ? I guess that is just the call of component view. There is a difference bewteen the Router.php in J3 and J4, except the location of file into the component structure folder?
I think that there is some logic that I missed on this specific operation in J4.
For information, result of my research:
- Remove component's view name from url joomla 4 (J4)
- https://stackoverflow.com/questions/18108683/jooma-custom-component-router-php-not-being-called (J3)
- Supporting SEF URLs in custom component
- https://docs.joomla.org/J3.x:Supporting_SEF_URLs_in_your_component
- https://forum.joomla.org/viewtopic.php?f=816&t=992747
- https://docs.joomla.org/Part_1:_The_Site_code
- https://docs.joomla.org/URLs_in_Joomla
NomenuRules
class in the same namespace as the router (Componentname\Component\Componentname\Site\Service
), you should be getting the same error. If you're not getting it, it means the router isn't used at all.