Magento 2 – Create Custom 404 Page for Not-Found Products

404-pagelayoutmagento2template

In Magento 2 I am looking to create a module that adds a custom 404 page specifically for not-found products in the catalog, however keep the default 404 page for all other pages.

My initial thoughts are to create a controller with di on \Magento\Catalog\Controller\Product\View that checks if the product does not exist and changes the template to a custom 404 response if returned true.

Something like (not actual code)

public function execute()
{
    if (!$this->productExists()) {
        $this->setTemplate('custom-404');
    }
}

Is there a better way to do this? Let me know if you need additional info.

Best Answer

Magento 2 provides some classes to handle no router: vendor/magento/framework/App/Router/NoRouteHandler.php vendor/magento/framework/App/Router/NoRouteHandlerInterface.php vendor/magento/framework/App/Router/NoRouteHandlerList.php

Create an example module:

app/code/Boolfly/NotFoundPages/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Boolfly_NotFoundPages" setup_version="0.0.1" />
</config>

app/code/Boolfly/NotFoundPages/etc/frontend/routes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="notfoundpages" frontName="notfoundpages">
            <module name="Boolfly_NotFoundPages" />
        </route>
    </router>
</config>

app/code/Boolfly/NotFoundPages/etc/frontend/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\App\Router\NoRouteHandlerList">
        <arguments>
            <argument name="handlerClassesList" xsi:type="array">
                <item name="notfoundpages" xsi:type="array">
                    <item name="class" xsi:type="string">Boolfly\NotFoundPages\Controller\NoRouteHandler</item>
                    <item name="sortOrder" xsi:type="string">50</item>
                </item>
            </argument>
        </arguments>
    </type>
</config>

app/code/Boolfly/NotFoundPages/Controller/NoRoute.php

<?php

namespace Boolfly\NotFoundPages\Controller;

use Magento\Framework\App\Action\Context as Context;
use Magento\Framework\View\Result\PageFactory as PageFactory;

class NoRoute extends \Magento\Framework\App\Action\Action
{
    protected $resultPageFactory;

    /**
     * NoRoute constructor.
     * @param Context $context
     * @param PageFactory $resultPageFactory
     */
    public function __construct(Context $context, PageFactory $resultPageFactory)
    {
        $this->resultPageFactory = $resultPageFactory;
        parent::__construct($context);
    }

    /**
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $resultLayout = $this->resultPageFactory->create();
        $resultLayout->setStatusHeader(404, '1.1', 'Not Found');
        $resultLayout->setHeader('Status', '404 File not found');
        return $resultLayout;
    }
}

app/code/Boolfly/NotFoundPages/Controller/NoRouteHandler.php

<?php

namespace Boolfly\NotFoundPages\Controller;

use Magento\Framework\App\RequestInterface as Request;

class NoRouteHandler implements \Magento\Framework\App\Router\NoRouteHandlerInterface
{
    /**
     * @param Request $request
     * @return bool
     */
    public function process(Request $request)
    {
        $pathInfo = $request->getPathInfo();
        $parts = explode('/', trim($pathInfo, '/'));

        $moduleName = isset($parts[0]) ? $parts[0] : '';
        $actionPath = isset($parts[1]) ? $parts[1] : '';
        $actionName = isset($parts[2]) ? $parts[2] : '';

        if($moduleName == 'catalog' && $actionPath == 'product' && $actionName == 'view') {
            $request->setModuleName('notfoundpages')
                    ->setControllerName('noroute')
                    ->setActionName('product');
        } else {
            $request->setModuleName('notfoundpages')
                ->setControllerName('noroute')
                ->setActionName('other');
        }
        return false;
    }
}

app/code/Boolfly/NotFoundPages/Controller/NoRoute/Product.php

<?php

namespace Boolfly\NotFoundPages\Controller\NoRoute;

class Product extends \Boolfly\NotFoundPages\Controller\NoRoute
{

}

app/code/Boolfly/NotFoundPages/Controller/NoRoute/Other.php

<?php

namespace Boolfly\NotFoundPages\Controller\NoRoute;

class Other extends \Boolfly\NotFoundPages\Controller\NoRoute
{

}

app/code/Boolfly/NotFoundPages/view/frontend/layout/notfoundpages_noroute_product.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template" name="no.route.product" as="no.route.product"
                   template="Boolfly_NotFoundPages::product.phtml" />
        </referenceContainer>
    </body>
</page>

app/code/Boolfly/NotFoundPages/view/frontend/layout/notfoundpages_noroute_other.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template" name="no.route.other" as="no.route.other" template="Boolfly_NotFoundPages::other.phtml" />
        </referenceContainer>
    </body>
</page>

app/code/Boolfly/NotFoundPages/view/frontend/templates/product.phtml

<?php echo __('Product Page Not Found.'); ?>

app/code/Boolfly/NotFoundPages/view/frontend/templates/other.phtml

<?php echo __('Other Page Not Found.') ;?>

Now, we can try:

Other pages: url: magen2.loc/dddddd

enter image description here

Product Page: url: magen2.loc/catalog/view/45555

enter image description here

Note: we will catch the url of product with format: {base url}/catalog/product/view/{....}

Related Topic