How to return 404 status if an item is not found?

How to configure and adapt Aimeos based shops as developer
Forum rules
Always add your Aimeos and PHP version as well as your environment (Linux/Mac/Win)
Spam and unrelated posts will be removed immediately!
mr.zherart
Posts: 27
Joined: 04 Jun 2020, 14:00

How to return 404 status if an item is not found?

Post by mr.zherart » 02 Sep 2021, 16:57

Hi, seems that right now exists no mechanic to return 404 properly for SEO need. For example, if product, page, custom cms post domain is not found by URL I want to return 404 status. But it is hard, because, aimeos has a block structure with exceptions on each level.

I didn`t find a better solution as roughly and dirty iterate view of each page component in each controller method

Code: Select all

if ( $this->is404( Shop::get( $component )->getView() ) ) return abort(404);
and iterate each ErrorList

Code: Select all

private function is404( $view ) {
        $name = last(explode('/', $view->get('component')));

        $regex = [
            '/^Unable to find (product|post|page|supplier)/',
            '/^No item found/'
        ];

        $messages = $view->get("${name}ErrorList", []);

        foreach( $regex as $pattern ) {
            $matches = preg_grep($pattern, $messages);
            if ( $matches ) return true;
        }

        return false;
}
It of course has broken with translation

Code: Select all

$msg = $this->getContext()->getI18n()->dt( 'controller/frontend', 'Unable to find product "%1$s"' );
in 2021.07.

Maybe, you have an idea how to do it in a proper way? I should be grateful for a reply.

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: How to return 404 status if an item is not found?

Post by aimeos » 04 Sep 2021, 06:50

That's difficult indeed. A better way would be if the Aimeos HTML components would throw an exception with a status 404 in that case which could be handled in the controller actions properly.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

mr.zherart
Posts: 27
Joined: 04 Jun 2020, 14:00

Re: How to return 404 status if an item is not found?

Post by mr.zherart » 04 Sep 2021, 09:02

Are you planning to provide this feature in the next releases?

mr.zherart
Posts: 27
Joined: 04 Jun 2020, 14:00

Re: How to return 404 status if an item is not found?

Post by mr.zherart » 04 Sep 2021, 09:28

What about add code to Exceptions?

For example,
app/vendor/aimeos/aimeos-core/lib/mshoplib/src/MShop/Common/Manager/Base.php

Code: Select all

$msg = $this->getContext()->i18n()->dt( 'mshop', 'No item found for conditions: %1$s' );
throw new \Aimeos\MShop\Exception( sprintf( $msg, print_r( $pairs, true ) ), 404 );
il-grapejs is a fork of ai-cms-grapejs (is just for example of concept)
app/ext/il-grapesjs/client/html/src/Client/Html/Cms/Page/Standard.php

Code: Select all

catch( \Aimeos\MShop\Exception $e ) {
                if ( $e->getCode() == 404 ) abort(404);

		$tplconf = 'client/html/cms/page/template-error';
		$error = array( $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
		$view->pageErrorList = array_merge( $view->get( 'pageErrorList', [] ), $error );
}
Or for example,
app/vendor/aimeos/ai-controller-frontend/controller/frontend/src/Controller/Frontend/Product/Standard.php

Code: Select all

$msg = $this->getContext()->getI18n()->dt( 'controller/frontend', 'Unable to find product "%1$s"' );
throw new \Aimeos\Controller\Frontend\Product\Exception( sprintf( $msg, $name ), 404 );
app/vendor/aimeos/ai-client-html/client/html/src/Client/Html/Catalog/Detail/Standard.php

Code: Select all

catch( \Aimeos\Controller\Frontend\Exception $e ) {
                if ( 404 == $e->getCode() ) abort(404);

		$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
		$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
}
By code from Exception client/html can understand how to return, with $view->ErrorList message or with something to Controller or anyway else. And you can add it only to specific client/html

I don`t want to create self solution because it will break with updates. We can do it together via pull request

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: How to return 404 status if an item is not found?

Post by aimeos » 05 Sep 2021, 12:22

Yes, something similar would be the way to go. Instead of using Laravel specific "abort()" method, a custom "NotFound" exception should be thrown and caught by the Aimeos Laravel controller which can then call "abort(404)".

If you create a pull request, we will review and merge it into the core :-)
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

mr.zherart
Posts: 27
Joined: 04 Jun 2020, 14:00

Re: How to return 404 status if an item is not found?

Post by mr.zherart » 14 Sep 2021, 12:24

Hi, please, check the conception, and I will make the pull request.

1. All controller frontends find/resolve methods need to check item null and have 404 code.

Example: app/vendor/aimeos/ai-controller-frontend/controller/frontend/src/Controller/Frontend/Product/Standard.php:347

Code: Select all

if( ( $item = $this->manager->search( $search, $this->domains )->first() ) === null )
{
	$msg = $this->getContext()->getI18n()->dt( 'controller/frontend', 'Unable to find product "%1$s"' );
	throw new \Aimeos\Controller\Frontend\Product\Exception( sprintf( $msg, $name ), 404 );
}
2. Aimeos\Client\Html\NotFound Exception needs to be created.

3. Aimeos\Client\Html\Base needs to be extended by the throw method.
Example: app/vendor/aimeos/ai-client-html/client/html/src/Client/Html/Base.php

Code: Select all

protected function throwHttpException( \Exception $e, $domain )
{
        $code = $e->getCode();
        $config = $this->getContext()->getConfig();
        if ( $config->get("client/html/common/http/$code/$domain", false) )
            throw $e;
}
Which will be added to all client/html which can return 404 responses, for example:
app/vendor/aimeos/ai-client-html/client/html/src/Client/Html/Catalog/Product/Standard.php

Code: Select all

catch( \Aimeos\Controller\Frontend\Exception $e )
{
	$this->throwHttpException(
                    new \Aimeos\Client\Html\NotFound( $e->getMessage(), $e->getCode() ), 'product'
        );

	$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
	$view->productErrorList = array_merge( $view->get( 'productErrorList', [] ), $error );
}
It will be disabled by default, and those who want to enable it, will make config adjustments:
client/html/common/http/404/product => true

4. Then, the controller may catch it.

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: How to return 404 status if an item is not found?

Post by aimeos » 15 Sep 2021, 17:46

Just simplify that to:

Code: Select all

catch( \Aimeos\Controller\Frontend\Exception $e )
{
	if( $e->getCode() === 404 ) { throw $e; }

	$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
	$view->productErrorList = array_merge( $view->get( 'productErrorList', [] ), $error );
}
Then, you can add the try/catch in the controller action.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

mr.zherart
Posts: 27
Joined: 04 Jun 2020, 14:00

Re: How to return 404 status if an item is not found?

Post by mr.zherart » 15 Sep 2021, 19:02

Ok, I thought about those who don`t want to handle 404 and want to keep it as is.
Maybe for those is better change the order:

Code: Select all

catch( \Aimeos\Controller\Frontend\Exception $e )
{
	$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
	$view->productErrorList = array_merge( $view->get( 'productErrorList', [] ), $error );
	
	if( $e->getCode() === 404 ) { throw $e; }
}
Those who want to keep $view->ErrorList just will add empty try/catch in the controller.

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: How to return 404 status if an item is not found?

Post by aimeos » 15 Sep 2021, 19:12

If you re-throw the exception, the error message is never displayed.
We would like to change the 404 handling in the upcoming 2021.10 LTS for all.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

User avatar
PedroLópezAndradas
Posts: 22
Joined: 16 Jan 2023, 11:30

Re: How to return 404 status if an item is not found?

Post by PedroLópezAndradas » 10 Feb 2023, 11:53

With 2022 package, when the url does not match a product, it throws an exception.

Finally, ¿did you set an option to return a 404 page instead a 500?

Image

Thanks!

Post Reply