magento / magento2

Prior to making any Submission(s), you must sign an Adobe Contributor License Agreement, available here at: https://opensource.adobe.com/cla.html. All Submissions you make to Adobe Inc. and its affiliates, assigns and subsidiaries (collectively “Adobe”) are subject to the terms of the Adobe Contributor License Agreement.
http://www.magento.com
Open Software License 3.0
11.56k stars 9.32k forks source link

404 when trying to display product view page (product.info.main container label is missing) #4234

Closed leoquijano closed 8 years ago

leoquijano commented 8 years ago

Hi,

I'm developing a custom theme and I was stuck with a 404 error when displaying the product view page:

http://mystore.dev/some-product

I first thought that this was a problem with my own setup, but then I went in to debug some exceptions I was getting. Particularly this one:

[2016-04-19 17:46:50] main.CRITICAL: Exception: Notice: Undefined index: label in /var/www/html/my-store/vendor/magento/framework/View/Layout/Generator/Container.php on line 90 in /var/www/html/my-store/vendor/magento/framework/App/ErrorHandler.php:61
Stack trace:
#0 /var/www/html/my-store/vendor/magento/framework/View/Layout/Generator/Container.php(90): Magento\Framework\App\ErrorHandler->handler(8, 'Undefined index...', '/var/www/html/o...', 90, Array)
#1 /var/www/html/my-store/vendor/magento/framework/View/Layout/Generator/Container.php(67): Magento\Framework\View\Layout\Generator\Container->generateContainer(Object(Magento\Framework\View\Layout\Data\Structure), 'product.info.ma...', Array)
#2 /var/www/html/my-store/vendor/magento/framework/View/Layout/GeneratorPool.php(86): Magento\Framework\View\Layout\Generator\Container->process(Object(Magento\Framework\View\Layout\Reader\Context), Object(Magento\Framework\View\Layout\Generator\Context))
#3 /var/www/html/my-store/vendor/magento/framework/View/Layout.php(327): Magento\Framework\View\Layout\GeneratorPool->process(Object(Magento\Framework\View\Layout\Reader\Context), Object(Magento\Framework\View\Layout\Generator\Context))
#4 /var/www/html/my-store/var/generation/Magento/Framework/View/Layout/Interceptor.php(89): Magento\Framework\View\Layout->generateElements()
#5 /var/www/html/my-store/vendor/magento/framework/View/Layout/Builder.php(129): Magento\Framework\View\Layout\Interceptor->generateElements()
#6 /var/www/html/my-store/vendor/magento/framework/View/Page/Builder.php(55): Magento\Framework\View\Layout\Builder->generateLayoutBlocks()
#7 /var/www/html/my-store/vendor/magento/framework/View/Layout/Builder.php(65): Magento\Framework\View\Page\Builder->generateLayoutBlocks()
#8 /var/www/html/my-store/vendor/magento/framework/View/Page/Config.php(166): Magento\Framework\View\Layout\Builder->build()
#9 /var/www/html/my-store/vendor/magento/framework/View/Page/Config.php(475): Magento\Framework\View\Page\Config->build()
#10 /var/www/html/my-store/vendor/magento/framework/View/Page/Config.php(433): Magento\Framework\View\Page\Config->getElementAttribute('body', 'class')
#11 /var/www/html/my-store/vendor/magento/module-catalog/Helper/Product/View.php(159): Magento\Framework\View\Page\Config->addBodyClass('catalog-product...')
#12 /var/www/html/my-store/vendor/magento/module-catalog/Helper/Product/View.php(214): Magento\Catalog\Helper\Product\View->initProductLayout(Object(Magento\Framework\View\Result\Page\Interceptor), Object(Magento\Catalog\Model\Product\Interceptor), Object(Magento\Framework\DataObject))
#13 /var/www/html/my-store/vendor/magento/module-catalog/Controller/Product/View.php(109): Magento\Catalog\Helper\Product\View->prepareAndRender(Object(Magento\Framework\View\Result\Page\Interceptor), 233, Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Magento\Framework\DataObject))
#14 /var/www/html/my-store/var/generation/Magento/Catalog/Controller/Product/View/Interceptor.php(24): Magento\Catalog\Controller\Product\View->execute()
#15 /var/www/html/my-store/vendor/magento/framework/App/Action/Action.php(102): Magento\Catalog\Controller\Product\View\Interceptor->execute()
#16 [internal function]: Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http))
#17 /var/www/html/my-store/vendor/magento/framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array)
#18 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Catalog\Controller\Product\View\Interceptor->___callParent('dispatch', Array)
#19 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Catalog...', 'dispatch', Object(Magento\Catalog\Controller\Product\View\Interceptor), Array, 'designLoader')
#20 /var/www/html/my-store/vendor/magento/framework/App/Action/Plugin/Design.php(39): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#21 [internal function]: Magento\Framework\App\Action\Plugin\Design->aroundDispatch(Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#22 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#23 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Catalog...', 'dispatch', Object(Magento\Catalog\Controller\Product\View\Interceptor), Array, 'contextPlugin')
#24 /var/www/html/my-store/vendor/magento/module-store/App/Action/Plugin/Context.php(98): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#25 [internal function]: Magento\Store\App\Action\Plugin\Context->aroundDispatch(Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#26 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#27 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Catalog...', 'dispatch', Object(Magento\Catalog\Controller\Product\View\Interceptor), Array, 'customer-app-ac...')
#28 /var/www/html/my-store/vendor/magento/module-customer/Model/App/Action/ContextPlugin.php(61): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#29 [internal function]: Magento\Customer\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#30 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#31 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Catalog...', 'dispatch', Object(Magento\Catalog\Controller\Product\View\Interceptor), Array, 'storeCheck')
#32 /var/www/html/my-store/vendor/magento/module-store/App/Action/Plugin/StoreCheck.php(44): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#33 [internal function]: Magento\Store\App\Action\Plugin\StoreCheck->aroundDispatch(Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#34 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#35 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Catalog...', 'dispatch', Object(Magento\Catalog\Controller\Product\View\Interceptor), Array, 'weee-app-action...')
#36 /var/www/html/my-store/vendor/magento/module-weee/Model/App/Action/ContextPlugin.php(112): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#37 [internal function]: Magento\Weee\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#38 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#39 /var/www/html/my-store/vendor/magento/framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Catalog...', 'dispatch', Object(Magento\Catalog\Controller\Product\View\Interceptor), Array, 'tax-app-action-...')
#40 /var/www/html/my-store/vendor/magento/module-tax/Model/App/Action/ContextPlugin.php(91): Magento\Catalog\Controller\Product\View\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#41 [internal function]: Magento\Tax\Model\App\Action\ContextPlugin->aroundDispatch(Object(Magento\Catalog\Controller\Product\View\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#42 /var/www/html/my-store/vendor/magento/framework/Interception/Interceptor.php(140): call_user_func_array(Array, Array)
#43 /var/www/html/my-store/var/generation/Magento/Catalog/Controller/Product/View/Interceptor.php(39): Magento\Catalog\Controller\Product\View\Interceptor->___callPlugins('dispatch', Array, Array)
#44 /var/www/html/my-store/vendor/magento/framework/App/FrontController.php(55): Magento\Catalog\Controller\Product\View\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#45 [internal function]: Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http))
#46 /var/www/html/my-store/vendor/magento/framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array)
#47 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Framework\App\FrontController\Interceptor->___callParent('dispatch', Array)
#48 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'requestPreproce...')
#49 /var/www/html/my-store/vendor/magento/module-store/App/FrontController/Plugin/RequestPreprocessor.php(89): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#50 [internal function]: Magento\Store\App\FrontController\Plugin\RequestPreprocessor->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#51 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#52 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'install')
#53 /var/www/html/my-store/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(69): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#54 [internal function]: Magento\Framework\Module\Plugin\DbStatusValidator->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#55 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#56 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'front-controlle...')
#57 /var/www/html/my-store/vendor/magento/module-page-cache/Model/App/FrontController/VarnishPlugin.php(55): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
#58 [internal function]: Magento\PageCache\Model\App\FrontController\VarnishPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#59 /var/www/html/my-store/vendor/magento/framework/Interception/Chain/Chain.php(67): call_user_func_array(Array, Array)
#60 /var/www/html/my-store/vendor/magento/framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext('Magento\\Framewo...', 'dispatch', Object(Magento\Framework\App\FrontController\Interceptor), Array, 'front-controlle...')
#61 /var/www/html/my-store/vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php(68): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#62 [internal function]: Magento\PageCache\Model\App\FrontController\BuiltinPlugin->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#63 /var/www/html/my-store/vendor/magento/framework/Interception/Interceptor.php(140): call_user_func_array(Array, Array)
#64 /var/www/html/my-store/var/generation/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins('dispatch', Array, Array)
#65 /var/www/html/my-store/vendor/magento/framework/App/Http.php(115): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#66 /var/www/html/my-store/vendor/magento/framework/App/Bootstrap.php(258): Magento\Framework\App\Http->launch()
#67 /var/www/html/my-store/pub/index.php(37): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))
#68 {main} [] []

Since I'm developing my theme on top of the theme-frontend-blank theme, I suspected that there was an issue with the most basic theme setup, an issue that might not be visible if I were using a theme that overrides some of the containers.

So I went to debug /vendor/magento/framework/View/Layout/Generator/Container.php line 90 (as described in the extension), and found this:

public function generateContainer(
    Layout\Data\Structure $structure,
    $elementName,
    $options
) {
    $structure->setAttribute(
        $elementName,
        Layout\Element::CONTAINER_OPT_LABEL,
        $options[Layout\Element::CONTAINER_OPT_LABEL]
    );
    unset($options[Layout\Element::CONTAINER_OPT_LABEL]);
    unset($options['type']);
     $this->validateOptions($options);
     foreach ($options as $key => $value) {
        $structure->setAttribute($elementName, $key, $value);
    }
}

There you can see how some attributes are set to a container. The CONTAINER_OPT_LABEL attribute (i.e. "label") gets a special treatment. It is directly retrieved from the $options array, and then set. The other attributes are set later.

I then found out that even though most containers have labels, this one doesn't:

/vendor/magento/module-catalog/view/frontend/layout/catalog_product_view.xml (line 30):

<container name="product.info.main" htmlTag="div" htmlClass="product-info-main" before="-">

Now: not all PHP configurations are so strict as to throw an exception due to an invalid index error. Mine is. I'm using PHP 7.0.5-2 on top of an Ubuntu setup (Homestead). It's clear that there's an issue with this layout file. I'm not sure if all containers should have labels and this just doesn't, or if the container process should handle missing labels well.

As a workaround, I changed Container.php like this, and that fixed the problem for me. This, of course, will only work until the next update comes around, so I hope an official fix can be made?

public function generateContainer(
    Layout\Data\Structure $structure,
    $elementName,
    $options
) {
    if(isset($options[Layout\Element::CONTAINER_OPT_LABEL])) {
      $structure->setAttribute(
          $elementName,
          Layout\Element::CONTAINER_OPT_LABEL,
          $options[Layout\Element::CONTAINER_OPT_LABEL]
      );
      unset($options[Layout\Element::CONTAINER_OPT_LABEL]);
    }
    unset($options['type']);
     $this->validateOptions($options);
     foreach ($options as $key => $value) {
        $structure->setAttribute($elementName, $key, $value);
    }
}

Why didn't I just override the container and added the label? I wanted to avoid any other issues like this with other containers. But that workaround is also good.

I'm using Magento 2.0.4, but I took a look at the HEAD source code and the issue seems to be still there at the moment I write this:

https://github.com/magento/magento2/blob/develop/lib/internal/Magento/Framework/View/Layout/Generator/Container.php https://github.com/magento/magento2/blob/develop/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml

Lastly, I would like to point out that the 404 error caused by this error is a bit misleading and might cause confusion with some developers. Perhaps there's a better way to display these errors to developers?

Thanks in advance for your attention.

leoquijano commented 8 years ago

An additional item of information here. I just noticed that for some reason I had a referenceBlock in my Magento_Theme/layout/default.xml file:

<referenceContainer name="product.info.main">
  <arguments>
    <argument name="htmlClass" xsi:type="string">product-info-main fixed-sidebar</argument>
  </arguments>
</referenceContainer>

(I was adding a CSS class to the block)

When I remove it (or move it to catalog_product_view.xml), the 404 error goes away, even without my core code changes. So this makes this issue less critical. But it may be a good idea to check anyway.

shiftedreality commented 8 years ago

Hi @leoquijano

Thank you for reporting. Created internal ticket MAGETWO-52343 to investigate and fix this issue.

leoquijano commented 8 years ago

Thanks @shiftedreality.

It was probably a mix of my own development bugs and some small problems in M2. I would advise to investigate two things:

  1. Whether or not the product.info.main container should have a label attribute.
  2. How to improve the error reporting in case there's an error rendering the product page. 404 was a bit confusing.
shiftedreality commented 8 years ago
  1. Per devdocs "label" attribute is optional for container, so it should be fixed in your way.
  2. Magento 2 has logging abilities from the box. Layout errors are being stored in var/log/system.log in developer mode
leoquijano commented 8 years ago
  1. Good, thanks.
  2. I did check for logs. They did help somewhat, but it was a bit more complex.

For me it is working now. Please do check the Generator/Container.php code I mentioned before. If labels are optional for containers, that code section will throw a NOTICE that might break in some PHP configurations.

piotrekkaminski commented 8 years ago

Fix was merged to develop branch.