J4 Component URL not SEF Topic is solved

For Joomla! 4.x Coding related discussions, you could also use: http://groups.google.com/group/joomla-dev-general

Moderator: ooffick

Forum rules
Post Reply
yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

J4 Component URL not SEF

Post by yann112 » Fri Sep 30, 2022 7:52 am

I have the same problem that the topic -> viewtopic.php?f=816&t=992747
(I open a new one because it seems that I can not post another message on this topic has been marked on solved)

I take the liberty of repeating the desription of the original problem made by ADT 22 because it describe it well :
"
Hi, I have recently upgraded to J4. However I have just noticed that my components are not loading SEF URLS. SEF URLS are switched on and most items are working fine, I just have a couple of custom components that I have selected as a direct menu item like:

index.php?option=com_componentname&view=componentname

after doing this my menu loads the URL: example.com/mypage?view=componentname I didn't have this problem in J3+ and think this is related to my components, but was wondering if anybody could point me in the right direction to start looking for the fault. Note I am able to load and visit the correct URL it's just the menu that is adding the additional query string parameter to the address. I presume my fault is likely to be caused by a component as some components are loading the URL correctly

"

I have read the links and answers on the original topic but always the same result for me and I think I'm missing something in the understanding of this routing...


-> 1) It's a new problem in J4, in J3 the SEF URL of component work normaly => SEF URL and possibility to pass parameter. Why this change of functioning? What is the advantage? ?

-> 2) In J4 what is the minimum required to have the same behaviour that in J3 ?

I understand that I need to create a new router file with the structure below : (I would before validate the principle and the structure of file before make the example code)

Code: Select all

Basic structure :

my_component
- src
- - Services
- - - Router.php -> register view in Routerview, correct ?
			
In Exemple, another file are added :
			
my_component
- src
- - Services
- - - ComponentnameNomenuRules.php -> clean all parameters from the URL, correct ? If yes, so how to manage the case If I want pass some parameter ?
			Router.php

-> 3) Another point that I don't understand it's why the module menu generate a link with a paramter (?view=viewname) and not just a SEF URL ? This link is generated by Joomla, not the component for me
(I don't have and example of code because it's a question of how it works)

I give you the documentation links of the original topic:
https://docs.joomla.org/Part_1:_The_Site_code
https://docs.joomla.org/URLs_in_Joomla


Thanks in advance for your help

User avatar
ceford
Joomla! Ace
Joomla! Ace
Posts: 1382
Joined: Mon Feb 24, 2014 10:38 pm
Location: Edinburgh, Scotland
Contact:

Re: J4 Component URL not SEF

Post by ceford » Fri Sep 30, 2022 7:59 pm

Do you have a [viewname].xml file in the tmpl folder of the view you wnat to make a menu for? Like this:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
	<layout title="COM_MYWALKS_MYWALKS_VIEW_DEFAULT_TITLE" option="COM_MYWALKS_MYWALKS_VIEW_DEFAULT_OPTION">
		<help
			key="JHELP_MENUS_MENU_ITEM_MYWALKS"
		/>
		<message>
			<![CDATA[COM_MYWALKS_MYWALKS_VIEW_DEFAULT_DESC]]>
		</message>
	</layout>

	<!-- Add fields to the parameters object for the layout. -->
		<fields name="params">
		<fieldset name="request" label="COM_MENUS_BASIC_FIELDSET_LABEL">

		</fieldset>

		<!-- Add fields to the parameters object for the layout. -->

		<!-- Scroll. -->
		<fieldset name="basic">

		</fieldset>

		<!-- Advanced options. -->
		<fieldset name="advanced">

		</fieldset>
	</fields>
</metadata>
You should be able to create a menu item for your component in the normal way. Try it.

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Mon Oct 03, 2022 5:11 am

@ceford, thank you for you message.

Yes, I have a xml file in the tmpl folder.

My xml file named "default.xml".
URL = /alias-menu?view= viewname

Based on you answer I try to rename it "[viewname].xml" and save the menu to be sure to include the change.
New URL = /alias-menu?view= viewname&layout= viewname

And in all cases the Router.php is never called. (I don't see my "die('router');'")


For information, I have remake my default.xml and save again the menu, here is the code of the xml file :

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<metadata>
	<layout title="COM_COMPONENTNAME_TITLE" option="View">
        <message>
                        <![CDATA[COM_COMPONENTNAME_DESC]]>
        </message>
	</layout>
    <fields name="params">
        <fieldset name="PARAMS" label="Categories">
            <field name="categories"
                   type="SQL"
                   class="inputbox"
                   label="Categories"
                   multiple="true"
                   size="15"
                   translate="true"
                   query="SELECT id, title FROM #__categories WHERE `extension` = 'com_content' AND `published` = 1 ORDER BY `lft` ASC"
                   key_field="id"
                   value_field="title"
                   description="Select one or more categories. Use Ctrl-click to select more than one item."
                   default="">
            </field>
        </fieldset>
    </fields>
</metadata>

SharkyKZ
Joomla! Hero
Joomla! Hero
Posts: 2469
Joined: Fri Jul 05, 2013 10:35 am
Location: Parts Unknown

Re: J4 Component URL not SEF

Post by SharkyKZ » Mon Oct 03, 2022 5:52 am

Did you create a services/provider.php file yet?

User avatar
ceford
Joomla! Ace
Joomla! Ace
Posts: 1382
Joined: Mon Feb 24, 2014 10:38 pm
Location: Edinburgh, Scotland
Contact:

Re: J4 Component URL not SEF

Post by ceford » Mon Oct 03, 2022 6:00 am

yann112 wrote:
Mon Oct 03, 2022 5:11 am
@ceford, thank you for you message.

Yes, I have a xml file in the tmpl folder.

My xml file named "default.xml".
URL = /alias-menu?view= viewname
So you should be able to create a menu item from the Menus: Items / New button - select your component. You should not need to create a URL type. But if you do, it should not include a space as you show here

User avatar
Per Yngve Berg
Joomla! Master
Joomla! Master
Posts: 29501
Joined: Mon Oct 27, 2008 9:27 pm
Location: Romerike, Norway

Re: J4 Component URL not SEF

Post by Per Yngve Berg » Mon Oct 03, 2022 6:04 am

Mod. note: Relocated topic to the coding forum.

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Mon Oct 03, 2022 2:12 pm

Yes I'm able to create it from the backend by selecting my component.
Sorry for the space in my example, it's just a copy/paste mistake.

My problem is that the Joomla menu generate the URL with these parameters and I only want the default menu alias.

Actually Joomla generate : /alias-menu?view=viewname
I want that Joomla generate: /alias-menu

ceford wrote:
Mon Oct 03, 2022 6:00 am
yann112 wrote:
Mon Oct 03, 2022 5:11 am
@ceford, thank you for you message.

Yes, I have a xml file in the tmpl folder.

My xml file named "default.xml".
URL = /alias-menu?view= viewname
So you should be able to create a menu item from the Menus: Items / New button - select your component. You should not need to create a URL type. But if you do, it should not include a space as you show here

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Mon Oct 03, 2022 2:14 pm

Yes I have a provider in administrator side (com_mycomponent/services/provider.php)
I have make some research on this and make different tests. It's possible that my URL problem provide from this.

I tried to register the router but error 500.
Do you have an idea what is wrong ?

Thanks in advance

provider.php

Code: Select all

<?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('\\Mycomponent\\Component\\Mycomponent'));
		$container->registerServiceProvider(new ComponentDispatcherFactory('\\Mycomponent\\Component\\Mycomponent'));
		//tried to register Router but Internal error 500
		//$container->registerServiceProvider(new RouterFactory('\\Mycomponent\\Component\\Mycomponent'));

		$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;
			}
		);
	}
};
SharkyKZ wrote:
Mon Oct 03, 2022 5:52 am
Did you create a services/provider.php file yet?

SharkyKZ
Joomla! Hero
Joomla! Hero
Posts: 2469
Joined: Fri Jul 05, 2013 10:35 am
Location: Parts Unknown

Re: J4 Component URL not SEF

Post by SharkyKZ » Mon Oct 03, 2022 2:42 pm

You need to post the exact error. Enable debug to get more information.

User avatar
ceford
Joomla! Ace
Joomla! Ace
Posts: 1382
Joined: Mon Feb 24, 2014 10:38 pm
Location: Edinburgh, Scotland
Contact:

Re: J4 Component URL not SEF

Post by ceford » Mon Oct 03, 2022 2:44 pm

You need a stack trace for the 500 error - in Global Configuration set Debug System to Yes and Error Reporting to Maximum. Uncomment the commented line and go again.

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Mon Oct 03, 2022 3:03 pm

Global Configuration set Debug System to Yes and Error Reporting to Maximum => done.
Uncomment the commented line and go again => done.

Result :
-> The page loading 30sec and error 500, message :
"
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at you@example.com to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.
"
-> blank page (the Debug System at the bottom of the page is not display when error 500)
-> log Apache : Fatal error: Maximum execution time of 30+2 seconds exceeded (terminated) in /website/administrator/components/my_component/services/provider.php on line 35

line 35 :

Code: Select all

$component->setRouterFactory($container->get(RouterFactoryInterface::class));

SharkyKZ
Joomla! Hero
Joomla! Hero
Posts: 2469
Joined: Fri Jul 05, 2013 10:35 am
Location: Parts Unknown

Re: J4 Component URL not SEF

Post by SharkyKZ » Wed Oct 05, 2022 6:08 am

Not sure why this ends up in a timeout but the error occurs because you're calling an undefined method. Joomla\CMS\Extension\MVCComponent doesn't have setRouterFactory() method.

User avatar
ceford
Joomla! Ace
Joomla! Ace
Posts: 1382
Joined: Mon Feb 24, 2014 10:38 pm
Location: Edinburgh, Scotland
Contact:

Re: J4 Component URL not SEF

Post by ceford » Wed Oct 05, 2022 8:17 am

yann112 wrote:
Mon Oct 03, 2022 3:03 pm
Global Configuration set Debug System to Yes and Error Reporting to Maximum => done.
Uncomment the commented line and go again => done.

Result :
-> The page loading 30sec and error 500, message :
"
Internal Server Error
line 35 :

Code: Select all

$component->setRouterFactory($container->get(RouterFactoryInterface::class));
Something is going around in circles, probably a redirect.

Do you have this code on a live site or on test site on localhost?

If the latter, do you know how to debugging? If not, see:

https://docs.joomla.org/Visual_Studio_Code_Primer

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Fri Oct 07, 2022 3:23 pm

Yes you are right, the problem is that the method is undefined, thank you.
I have changed my research in this direction.

SharkyKZ wrote:
Wed Oct 05, 2022 6:08 am
Not sure why this ends up in a timeout but the error occurs because you're calling an undefined method. Joomla\CMS\Extension\MVCComponent doesn't have setRouterFactory() method.

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Fri Oct 07, 2022 3:24 pm

Thank you for the links. For debugging I using an IDE (PHPStorm) in combination with Codesniffer and the Joomla code style and Xdebug as proposed by pe7er on anothe topic on this forum (viewtopic.php?f=812&t=995556).
I'm in localhost for this project.

ceford wrote:
Wed Oct 05, 2022 8:17 am
yann112 wrote:
Mon Oct 03, 2022 3:03 pm
Global Configuration set Debug System to Yes and Error Reporting to Maximum => done.
Uncomment the commented line and go again => done.

Result :
-> The page loading 30sec and error 500, message :
"
Internal Server Error
line 35 :

Code: Select all

$component->setRouterFactory($container->get(RouterFactoryInterface::class));
Something is going around in circles, probably a redirect.

Do you have this code on a live site or on test site on localhost?

If the latter, do you know how to debugging? If not, see:

https://docs.joomla.org/Visual_Studio_Code_Primer

yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 19
Joined: Mon Aug 08, 2022 8:18 am

Re: J4 Component URL not SEF

Post by yann112 » Fri Oct 07, 2022 3:29 pm

Solution found, thank you all for your help :)

I found that a new file (ComponentnameComponent.php) is require as I understand. I don't know is the "best pratice" for J4 but it's work.
To summerize all what I need to create a routing in J4 (with same behavior that J3) :


/com_componentname/src/Service/Router.php

Code: Select all

<?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);

		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) {

		$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) {

		$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

Code: Select all

<?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

Code: Select all

<?php
// no direct access
defined('_JEXEC') or die;


use Joomla\CMS\Extension\MVCComponent;
use Componentname\Component\Componentname\Administrator\Extension\ComponentnameComponent;
use Joomla\CMS\Categories\CategoryFactoryInterface;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\CategoryFactory;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\Extension\Service\Provider\RouterFactory;
use Joomla\CMS\HTML\Registry;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;

return new class implements ServiceProviderInterface
{

	public function register(Container $container)
	{
		$container->registerServiceProvider(new CategoryFactory('\\Componentname\\Component\\Componentname'));
		$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 ComponentnameComponent($container->get(ComponentDispatcherFactoryInterface::class));

				$component->setRegistry($container->get(Registry::class));
				$component->setMVCFactory($container->get(MVCFactoryInterface::class));
				$component->setCategoryFactory($container->get(CategoryFactoryInterface::class));
				$component->setRouterFactory($container->get(RouterFactoryInterface::class));

				return $component;
			}
		);
	}
};
/administrator/components/com_componentname/src/Extension/ComponentnameComponent.php

Code: Select all

<?php
namespace Componentname\Component\Componentname\Administrator\Extension;
defined('_JEXEC') or die;

use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Association\AssociationServiceInterface;
use Joomla\CMS\Association\AssociationServiceTrait;
use Joomla\CMS\Categories\CategoryServiceInterface;
use Joomla\CMS\Categories\CategoryServiceTrait;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use FooNamespace\Component\Foos\Administrator\Service\HTML\AdministratorService;
use FooNamespace\Component\Foos\Administrator\Service\HTML\Icon;
use Psr\Container\ContainerInterface;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Component\Router\RouterServiceInterface;
use Joomla\CMS\Component\Router\RouterServiceTrait;

/**
 * Component class for com_foos
 *
 * @since  __BUMP_VERSION__
 */
class ComponentnameComponent extends MVCComponent implements BootableExtensionInterface, CategoryServiceInterface, AssociationServiceInterface, RouterServiceInterface
{
	use CategoryServiceTrait;
	use AssociationServiceTrait;
	use HTMLRegistryAwareTrait;
	use RouterServiceTrait;

	/**
	 * Booting the extension. This is the function to set up the environment of the extension like
	 * registering new class loaders, etc.
	 *
	 * If required, some initial set up can be done from services of the container, eg.
	 * registering HTML services.
	 *
	 * @param   ContainerInterface  $container  The container
	 *
	 * @return  void
	 *
	 * @since   __BUMP_VERSION__
	 */
	public function boot(ContainerInterface $container)
	{
		//$this->getRegistry()->register('foosadministrator', new AdministratorService);
		//$this->getRegistry()->register('fooicon', new Icon($container->get(SiteApplication::class)));
	}

	/**
	 * Adds Count Items for Category Manager.
	 *
	 * @param   \stdClass[]  $items    The category objects
	 * @param   string       $section  The section
	 *
	 * @return  void
	 *
	 * @since   __BUMP_VERSION__
	 */
	public function countItems(array $items, string $section)
	{
		/*try {
			$config = (object) [
				'related_tbl'   => $this->getTableNameForSection($section),
				'state_col'     => 'published',
				'group_col'     => 'catid',
				'relation_type' => 'category_or_group',
			];

			ContentHelper::countRelations($items, $config);
		} catch (\Exception $e) {
			// Ignore it
		}*/
	}

	/**
	 * Returns the table for the count items functions for the given section.
	 *
	 * @param   string  $section  The section
	 *
	 * @return  string|null
	 *
	 * @since   __BUMP_VERSION__
	 */
	protected function getTableNameForSection(string $section = null)
	{
		//return ($section === 'category' ? 'categories' : 'foos_details');
	}

	/**
	 * Returns the state column for the count items functions for the given section.
	 *
	 * @param   string  $section  The section
	 *
	 * @return  string|null
	 *
	 * @since   __BUMP_VERSION__
	 */
	protected function getStateColumnForSection(string $section = null)
	{
		//return 'published';
	}
}


Post Reply

Return to “Joomla! 4.x Coding”