Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cant't get cart items in block - quote object empty #6392

Closed
bartoszkubicki opened this issue Aug 30, 2016 · 25 comments
Closed

Cant't get cart items in block - quote object empty #6392

bartoszkubicki opened this issue Aug 30, 2016 · 25 comments
Labels
bug report Component: Catalog Issue: Format is valid Gate 1 Passed. Automatic verification of issue format passed

Comments

@bartoszkubicki
Copy link

bartoszkubicki commented Aug 30, 2016

Preconditions

  1. Magento 2.1 CE, php 7.0

Steps to reproduce

  1. In custom module I added block, which should check if provided product is in cart.
  2. Block is added properly, although method doesn't work.
  3. When I try to check if my product is in cart I get info that cart is empty (but in fact it is not). That is my class:

namespace [vendor]\SkinHelper\Block\Catalog\Product;

use \Magento\Catalog\Api\Data\ProductInterface;
use \Magento\Checkout\Model\Session;
use \Magento\Framework\View\Element\Template;
use \Magento\Framework\View\Element\Template\Context;

/** Class provides method to check if given product is in cart
 * @universal
 */
class IsInCart extends Template
{

    /**
     * @var \Magento\Checkout\Model\Session
     */
    private $checkoutSession;



    /**
     * Di.
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param array $data
     */
    public function __construct(
        Session $checkoutSession,
        Context $context,
        array $data
    )
    {
        parent::__construct($context, $data);

        $this->checkoutSession = $checkoutSession;
    }



    /**
     * Method check if provided product is in cart of current customer.
     * Doesn't work dynamically.
     * TODO: There is a bug actually in magento connected with session and full page cache
     * TODO: More details: https://github.com/magento/magento2/issues/3294
     *
     * @param \Magento\Catalog\Api\Data\ProductInterface $product
     *
     * @return bool $inCart
     */
    public function isInCart(ProductInterface $product)
    {
        $productId = $product->getId();
        $cartItems = $this->checkoutSession->getQuote()->getAllVisibleItems();
        $itemsIds = array();
        foreach ($cartItems as $cartItem) {
            $itemsIds[] = $cartItem->getProduct()->getId();
        }

        return in_array($productId, $itemsIds);
    }


    /**
     * Method counts items now available in cart.
     * TODO: There is a bug actually in magento connected with session and full page cache
     * TODO: More details: https://github.com/magento/magento2/issues/3294
     *
     * @return int $cartDataCount
     */
    public function countItemsInCart()
    {
        $cartItems = $this->checkoutSession->getQuote()->getAllVisibleItems();
        $cartItemsCount = count($cartItems);

        return $cartItemsCount;
    }
  1. That's my call for these methods:

`isInCart($_product); ?>

helper([vendor]\SkinHelper\Helper\Test')->isInCart($_product) ?>`

As you can see I was trying the same thing with helper (called from helper), but it failed.

  1. I was trying also the same way on \Magento\Checkout\Model\Cart and it also didn't worked.
  2. The strangest thing is that the same logic placed and called as controller (by url) works and returns correct output.

Expected result

  1. Of course, method in block called inside template returns correct result.

Actual result

  1. When I dump $itemsIds it is empty, so no elements can be retrieved. Products are present in minicart, as well as in quote table as well.
  2. Method works in controller but not inside block or helper called from template. What is even more strange the same method in helper called from controller works.
@bartoszkubicki
Copy link
Author

I think problem may lay in cache, especially Full page cache.

@SerhiyShkolyarenko SerhiyShkolyarenko self-assigned this Sep 21, 2016
@SerhiyShkolyarenko
Copy link
Contributor

@bartek9007 there is a problem in your code

$cartItems = $this->checkoutSession->getQuoteId();
...
foreach ($cartItems as $cartItem) {...

$cartItems never will be an array. This code causes an exception for me. Could you specify the code version which works from controller?

@bartoszkubicki
Copy link
Author

@SerhiyShkolyarenko you are right. I updated my question, I posted chunk of code I have been testing later. Of course, it will cause exception. Now there is a code in question which actually works only when full page cache disabled.

@SerhiyShkolyarenko
Copy link
Contributor

SerhiyShkolyarenko commented Oct 3, 2016

@bartek9007 please also provide DownloadableSampleUrlProviderInterface code for clean experiment.

@bartoszkubicki
Copy link
Author

@SerhiyShkolyarenko
DownloadableSampleUrlProviderInterface defines another method, which is implementing this interface. This method helps to get downloadable sample for provided downloadable product and has nothing to do with IsInCart() method - they are not called together.

I will update it to the code I have now - and it is whole class and also doesn't work.

@SerhiyShkolyarenko
Copy link
Contributor

@bartek9007 I made some minor changes to have your code compiled and injected to controller. Patch with all my changes is attached. Also I disabled page cache to check how it works in debugger.
I ran compilation and the code worked properly: I opened product page twice to check.
The code returned false when cart was empty and the code returned true, when I added product to cart. This check was performed on the latest develop branch. Does it work the same for you?
Github6392.zip

@bartoszkubicki
Copy link
Author

@SerhiyShkolyarenko Controller was just another place to test code.
The code is placed in custom block and this block is injected into a few places, using <referenceBlock> in layout xmls in custom theme. Are you testing on 2.1.1 or on 2.1? I had that bug on 2.1, but not tested yet on 2.1.1.

@SerhiyShkolyarenko
Copy link
Contributor

@bartek9007 that check 5 days ago was performed on latest develop branch. Does the same code in standard Magento themes(Blank or Luma) works properly?

@bartoszkubicki
Copy link
Author

bartoszkubicki commented Oct 17, 2016

I have checked it on 2.1.1. and with FPC disabled and enabled - checking if product is in cart (using checkout session works)

@bartoszkubicki
Copy link
Author

But with full page cache doesn't work block checking if product is in currently logged in customer wishlist. Should I reopen this one or open new issue?

@SerhiyShkolyarenko
Copy link
Contributor

Let's continue here. Please attach all required changes as a patch and describe all steps to reproduce from clean magento installation to avoid different reproducing flows for you and me.

@bartoszkubicki
Copy link
Author

bartoszkubicki commented Oct 21, 2016

Hm, I am not sure how to make a patch, so for now I will post a code of block:

namespace [VENDOR]\SkinHelper\Block\Catalog\Product

use \Magento\Catalog\Api\Data\ProductInterface;
use \Magento\Framework\View\Element\Template;
use \Magento\Framework\View\Element\Template\Context;
use \Magento\Wishlist\Helper\Data;


class IsInWishlist extends Template
{



    private $customerSession;

    private $registry;

    private $wishlistHelper;

    public function __construct(
        Context $context,
        Data $wishlistHelper,
        array $data
    )
    {
        parent::__construct($context, $data);

        $this->wishlistHelper = $wishlistHelper;
    }


    private function getCustomerWishlistItemsCollection()
    {
        $itemsCollection = $this->wishlistHelper->getWishlist()->getItemCollection();

        return $itemsCollection;
    }


    public function isInWishlist(ProductInterface $product)
    {
        $productId = $product->getId();

        $itemsCollection = $this->getCustomerWishlistItemsCollection();
        $itemsIds = $itemsCollection->getColumnValues('product_id');

        return in_array($productId, $itemsIds);
    }

and it is added inside catalog_category_view.xml:

<referenceBlock name="category.products.list"> <block class="[VENDOR]\SkinHelper\Block\Catalog\Product\IsInWishlist" name="name_catalog_isinwishlist" cacheable="false"/> </referenceBlock>

Works for me only with cacheable="false", having FPC enabled.
Part of list.phtml:

$IsInWishlist = $block->getChildBlock('name_catalog_isinwishlist');
...

<?php foreach ($_productCollection as $_product): ?> <?php $is_in_wishlist = $IsInWishlist->isInWishlist($_product) ?> <?php echo $is_in_wishlist?>
......
<php? endforeach; ?>
...

@bartoszkubicki
Copy link
Author

tested on clean Magento instance - 2.1.2 and doesn't work if it's not marked as cacheable="false" or FPC is not disabled.

@SerhiyShkolyarenko
Copy link
Contributor

Your $data parameter is required. What is di.xml config for this class?

@SerhiyShkolyarenko
Copy link
Contributor

Which way $_productCollection is assigned?

@bartoszkubicki
Copy link
Author

I haven't added any configuration for this class in di.xml. Should I add something? What is more I haven't encountered error about $data. $productCollection is assigned natively, I haven't changed the behavior. Template is placed in custom theme: Magento_Catalog\templates\product\list.phtml and only changes are provided above.

@SerhiyShkolyarenko
Copy link
Contributor

SerhiyShkolyarenko commented Nov 4, 2016

@bartek9007 could you please create a separate module with that block and corresponding layout update to activate it on category page? Attaching that module as archive would be handy here. I need it to be sure we have the same code changes.
Your block has obligatory parameter $data in constructor and for me Object Manager fails to instantiate it. It means there are some code/config changes which impact your and my Magento instance in a different way.

@bartoszkubicki
Copy link
Author

Test.zip
Ok, here there is module with two block - unfortunately block checking if product is in cart doesn't work. I was mislead by setting another block as not cacheable. I have tested it on Luma, Magento 2.1.2.

Here with FPC enabled, both blocks cachebale:
image

And here after disabling FPC or setting any of block as not cacheable in xml, as they make all page not cacheable:
image

@SerhiyShkolyarenko
Copy link
Contributor

SerhiyShkolyarenko commented Nov 17, 2016

@bartek9007
I unpacked the archive to "app/code" folder, ran ./bin/magento setup:upgrade for enabling new module, removed content of 'var' directory and opened catalog category in browser. I've got and error

1 exception(s):
Exception #0 (Magento\Framework\Config\Dom\ValidationException): Element 'referenceBlock', attribute 'template': The attribute 'template' is not allowed.
Line: 764


Exception #0 (Magento\Framework\Config\Dom\ValidationException): Element 'referenceBlock', attribute 'template': The attribute 'template' is not allowed.
Line: 764

and stack trace.
I found the line in code/Test/Block/view/frontend/layout/catalog_category_view.xml

<referenceBlock name="category.products.list" template="Test_Block::product/list.phtml">

'template' attributes are not allowed for 'referenceBlock' elements in layouts. How do you make that code run? Am I doing anything wrong?

@bartoszkubicki
Copy link
Author

Test.zip
Of course it is my mistake, apollogies! I could have forgotten about setting template for category grid - I was doing it late at friday.

@SerhiyShkolyarenko
Copy link
Contributor

SerhiyShkolyarenko commented Nov 22, 2016

@bartek9007 I added cacheable="false" to your blocks and everything worked fine:

            <block class="Test\Block\Block\IsInCart" name="test.is.in.cart" cacheable="false"/>
            <block class="Test\Block\Block\IsInWishlist" name="test.is.in.wishlist" cacheable="false"/>

Generally, it is a common rule: blocks with personal data have to be not cacheable.
I'm closing the issue, but feel free to leave your comments if you need something to clarify on this topic.

@bartoszkubicki
Copy link
Author

Yes, that is true - they work if I match them as not 'cacheable'. So I understand it's like this by design. The problem in such a situation is that the whole page is not cacheable. Is there any way to omit that? Shouldn't be this designed other way - to allow not cache part of page? Or is it hard/impossible to achieve?

@kokoc
Copy link
Member

kokoc commented Nov 23, 2016

@bartek9007 the general rule is to move private content in browser. When Magento renders public content (should not contain any personal data), it removes everything from session in order to prevent information leaks.

Take a look on this - http://devdocs.magento.com/guides/v2.1/config-guide/cache/cache-priv-priv.html. "Customer data sections" is preferable way to achieve your goal. If you don't want to rewrite your code to JS, you can try deprecated approach - mark your block as private using _isScopePrivate property. This will tell Magento to load content using separate AJAX call. In this case cache will be invalidated on any POST request.

@AbdulKadir-Agoliya
Copy link

AbdulKadir-Agoliya commented Jul 11, 2017

you need to add cacheable="false" in layout xml to access cart quote in custom module

@BabliDey
Copy link

BabliDey commented Jan 16, 2018

I am using magento 2.1.8. I my custom theme app\design\frontend\Sample\theme-frontend-shop\Magento_Catalog\templates\product\view\form.phtml I have done

`$quoteId = $objectManager->get('Magento\Checkout\Model\Session')->getQuoteId();
$currentItemCount=0;
if(!empty($quoteId)){
$cartItems = $objectManager->get('\Magento\Checkout\Model\Session')->getQuote()->getAllVisibleItems();
$itemsIds = array();
foreach ($cartItems as $cartItem) {
$itemsIds[] = $cartItem->getProduct()->getId();
}

$currentItemCount = count($itemsIds);

}

echo $currentItemCount;`

and in my app\design\frontend\Sample\theme-frontend-shop\Magento_Catalog\layout\catalog_product_view.xml I have added
<referenceBlock name="product.info.addtocart" class="Magento\Checkout\Block\Cart" before="-" template="Blumeidealnew_shop::product/view/form.phtml" cacheable="false" />

But this is not working properly when full page cache is enabled.

Need help to fix this issue.

@magento-engcom-team magento-engcom-team added the Issue: Format is valid Gate 1 Passed. Automatic verification of issue format passed label Jan 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug report Component: Catalog Issue: Format is valid Gate 1 Passed. Automatic verification of issue format passed
Projects
None yet
Development

No branches or pull requests

7 participants