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

Moderators: ooffick, General Support Moderators

Forum rules
Forum Rules
Absolute Beginner's Guide to Joomla! <-- please read before posting, this means YOU.
Forum Post Assistant - If you are serious about wanting help, you will use this tool to help you post.
Windows Defender SmartScreen Issues <-- please read this if using Windows 10.
Locked
yann112
Joomla! Apprentice
Joomla! Apprentice
Posts: 26
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! Hero
Joomla! Hero
Posts: 2654
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: 26
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: 2897
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! Hero
Joomla! Hero
Posts: 2654
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: 30890
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: 26
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: 26
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: 2897
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! Hero
Joomla! Hero
Posts: 2654
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: 26
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 [email protected] 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: 2897
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! Hero
Joomla! Hero
Posts: 2654
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: 26
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: 26
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: 26
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';
	}
}


Locked

Return to “Joomla! 4.x Coding”