Magento 2 private content – fazendo ajax do jeito certo.

Este tutorial tem como base demonstrar um dos métodos para atualização de dados dinâmicos em interfaces Magento 2, estas interfaces são desenvolvidas usando um conjunto de frameworks javascripts orquestrados para melhor atender o Magento como um todo, vamos fazer uso da biblioteca Knockout para atualização de dados dinâmicos vindos do backend via json.

Primeiramente vamos criar a estrutura Básica do módulo:

app/code/Joridos/CheckoutAjax/etc/module.xml

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

app/code/Joridos/Checkout/registration.php

\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Joridos_CheckoutAjax',
__DIR__
);

Agora habilitamos nosso módulo

$ magento setup:upgrade
$ magento setup:di:compile
$ magento cache:clean
$ magento cache:flush

O Magento 2 trás um novo conceito que é o de conteúdos privados e conteúdos públicos, conteúdos públicos são os dados em que todos os clientes veem da mesma forma sem alterações, já os privados são dados que ficam armazenados na sessão do browser.

Conteúdo público: Estes tipos de dados são armazenados no cache configurado que pode ser em arquivos do sistema, MySQL, ou Redis, armazenados até mesmo no Varnish. Exemplos desses conteúdos são header, footer e catálogo.

Conteúdo privado: Esses tipos de dados são armazenados no cache do navegador do cliente, o famoso localstorage. Exemplos desses conteúdos são lista de presentes, nome do cliente, endereço do cliente, informações do carrinho de compras. Lembrando que esse tipo de conteúdo deve representar apenas uma pequena parte da página.

Mais informações sobre sections e conteúdo público/privado podem ser vista na documentatação http://devdocs.magento.com/guides/v2.0/config-guide/cache/cache-priv-priv.html
OBS: usarei apenas o código necessário nos exemplos, de qualquer modo só usar o fallback do luma ou blank

Com o módulo já criado, precisa ser informado ao Magento 2 que queremos adicionar um novo provedor de dados para o cliente, lembrando que o magento 2 usa dependency injection através de arquivos xml de configurações, precisamos criar então nosso di.xml em app/code/Joridos/CheckoutAjax/etc/frontend/di.xml conforme o seguinte exemplo.

app/code/Joridos/CheckoutAjax/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\Customer\CustomerData\SectionPoolInterface">
<arguments>
<argument name="sectionSourceMap" xsi:type="array">
<item name="ajax-cart" xsi:type="string">Joridos\CheckoutAjax\CustomerData\AjaxCart</item>
</argument>
</arguments>
</type>
</config>

Agora que o Magento 2 sabe que criamos um provedor vamos criar a classe que implementa a interface SectionSourceInterface para podermos prover os dados atualizados.

app/code/Joridos/CheckoutAjax/CustomerData/AjaxCart.php

<?php
namespace Joridos\CheckoutAjax\CustomerData;

use Magento\Customer\CustomerData\SectionSourceInterface;

/**
 * AjaxCart source
 */
class AjaxCart extends \Magento\Framework\DataObject implements SectionSourceInterface
{
 /**
 * @var \Magento\Customer\Model\Session
 */
 protected $checkoutSession;

 /**
 * @var \Magento\Checkout\Model\Cart
 */
 protected $checkoutCart;

 /**
 * @var \Magento\Catalog\Model\ResourceModel\Url
 */
 protected $catalogUrl;

 /**
 * @var \Magento\Quote\Model\Quote|null
 */
 protected $quote = null;

 /**
 * @var \Magento\Checkout\Helper\Data
 */
 protected $checkoutHelper;

 /**
 * @var \\Magento\Checkout\CustomerData\ItemPoolInterface
 */
 protected $itemPoolInterface;

 /**
 * @var int|float
 */
 protected $summeryCount;

 /**
 * @var \Magento\Framework\View\LayoutInterface
 */
 protected $layout;

 /**
 * @param \Magento\Checkout\Model\Session $checkoutSession
 * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl
 * @param \Magento\Checkout\Model\Cart $checkoutCart
 * @param \Magento\Checkout\Helper\Data $checkoutHelper
 * @param ItemPoolInterface $itemPoolInterface
 * @param \Magento\Framework\View\LayoutInterface $layout
 * @param array $data
 * @codeCoverageIgnore
 */
 public function __construct(
 \Magento\Checkout\Model\Session $checkoutSession,
 \Magento\Catalog\Model\ResourceModel\Url $catalogUrl,
 \Magento\Checkout\Model\Cart $checkoutCart,
 \Magento\Checkout\Helper\Data $checkoutHelper,
 \Magento\Checkout\CustomerData\ItemPoolInterface $itemPoolInterface,
 \Magento\Framework\View\LayoutInterface $layout,
 array $data = []
 ) {
 parent::__construct($data);
 $this->checkoutSession = $checkoutSession;
 $this->catalogUrl = $catalogUrl;
 $this->checkoutCart = $checkoutCart;
 $this->checkoutHelper = $checkoutHelper;
 $this->itemPoolInterface = $itemPoolInterface;
 $this->layout = $layout;
 }

 /**
 * {@inheritdoc}
 */
 public function getSectionData()
 {
 $totals = $this->getQuote()->getTotals();
 return [
 'summary_count' => $this->getSummaryCount(),
 'subtotal' => isset($totals['subtotal'])
 ? $this->checkoutHelper->formatPrice($totals['subtotal']->getValue())
 : 0,
 'items' => $this->getRecentItems(),
 'shipping' => $this->checkoutHelper->formatPrice($this->getQuote()->getShippingAddress()->getShippingAmount()),
 'grand_total' => $this->checkoutHelper->formatPrice($this->getQuote()->getGrandTotal())
 ];
 }

 /**
 * Get active quote
 *
 * @return \Magento\Quote\Model\Quote
 */
 protected function getQuote()
 {
 if (null === $this->quote) {
 $this->quote = $this->checkoutSession->getQuote();
 }
 return $this->quote;
 }

 /**
 * Get shopping cart items qty based on configuration (summary qty or items qty)
 *
 * @return int|float
 */
 protected function getSummaryCount()
 {
 if (!$this->summeryCount) {
 $this->summeryCount = $this->checkoutCart->getSummaryQty() ?: 0;
 }
 return $this->summeryCount;
 }

 /**
 * Check if one page checkout is available
 *
 * @return bool
 */
 protected function isPossibleOnepageCheckout()
 {
 return $this->checkoutHelper->canOnepageCheckout() && !$this->getQuote()->getHasError();
 }

 /**
 * Get array of last added items
 *
 * @return \Magento\Quote\Model\Quote\Item[]
 */
 protected function getRecentItems()
 {
 $items = [];
 if (!$this->getSummaryCount()) {
 return $items;
 }

 foreach (array_reverse($this->getAllQuoteItems()) as $item) {
 /* @var $item \Magento\Quote\Model\Quote\Item */
 if (!$item->getProduct()->isVisibleInSiteVisibility()) {
 $product = $item->getOptionByCode('product_type') !== null
 ? $item->getOptionByCode('product_type')->getProduct()
 : $item->getProduct();

 $products = $this->catalogUrl->getRewriteByProductStore([$product->getId() => $item->getStoreId()]);
 if (!isset($products[$product->getId()])) {
 continue;
 }
 $urlDataObject = new \Magento\Framework\DataObject($products[$product->getId()]);
 $item->getProduct()->setUrlDataObject($urlDataObject);
 }
 $items[$item->getProduct()->getSku()] = $this->itemPoolInterface->getItemData($item);
 }
 return $items;
 }

 /**
 * Return customer quote items
 *
 * @return \Magento\Quote\Model\Quote\Item[]
 */
 protected function getAllQuoteItems()
 {
 if ($this->getCustomQuote()) {
 return $this->getCustomQuote()->getAllVisibleItems();
 }
 return $this->getQuote()->getAllVisibleItems();
 }

 /**
 * Check if guest checkout is allowed
 *
 * @return bool
 */
 public function isGuestCheckoutAllowed()
 {
 return $this->checkoutHelper->isAllowedGuestCheckout($this->checkoutSession->getQuote());
 }
}

Nesta classe o que precisamos ter atenção é o método getSectionData que é obrigado pelo contrato com a interface SectionSourceInterface a implementarmos, esse método retorna as informações privadas a serem atualizadas.
Agora vamos atualizar nossa página de checkout com o bloco do nosso template Knockout que ira renderizar os dados na tela buscando da nossa section.

app/code/Joridos/CheckoutAjax/view/frontend/layout/checkout_cart_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock class="Magento\Checkout\Block\Cart" name="checkout.cart.form" as="cart-items" template="cart/form.phtml" after="cart.summary">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="ajaxCart" xsi:type="array">
<item name="component" xsi:type="string">Joridos_Checkout/js/view/ajax-cart</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>

Vamos agora criar o conteúdo do nosso template que será atualizado via section.

app/code/Joridos/CheckoutAjax/view/frontend/templates/cart/form.phtml

<!-- Aqui adicionamos nosso componente -->
.......
<div class="cart-form" data-bind="scope: 'ajaxCart'" data-role="checkout-cart-ajax">
<div class="col-md-3 summary-qty">
<p class="summary-item-label"><?php echo __('Quantity') ?></p>
<p class="summary-item-info">
<!-- ko if: ajaxCart().summary_count -->
<span class="cart-qtd" data-bind="text: ajaxCart().summary_count + ' Unid.'"></span>
<!-- /ko -->
</p>
</div>
<div class="col-md-3 summary-subtotal">
<p class="summary-item-label"><?php echo __('Subtotal') ?></p>
<p class="summary-item-info">
<!-- ko if: ajaxCart().subtotal -->
<span class="cart-qtd" data-bind="html: ajaxCart().subtotal"></span>
<!-- /ko -->
</p>
</div>
<div class="col-md-3 summary-shipping">
<p class="summary-item-label"><?php echo __('Shipping') ?></p>
<p class="summary-item-info">
<!-- ko if: ajaxCart().shipping -->
<span class="cart-qtd" data-bind="html: ajaxCart().shipping"></span>
<!-- /ko -->
</p>
</div>
<div class="col-md-3 summary-total">
<p class="summary-item-label"><?php echo __('Total') ?></p>
<p class="summary-item-info">
<!-- ko if: ajaxCart().grand_total -->
<span class="cart-qtd" data-bind="html: ajaxCart().grand_total"></span>
<!-- /ko -->
</p>
</div>
.......
</div>

<!-- Aqui inicializamos nosso componente -->
<script type="text/x-magento-init">
{"[data-role=checkout-cart-ajax]": {"Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout();?>}}
</script>

Como podem ver nosso componente é chamado e inicializado buscando da configuração do xml pelo <?php /* @escapeNotVerified */ echo $block->getJsLayout();?>, vamos agora definir nosso componente javascript Knockout em
app/code/Joridos/CheckoutAjax/view/frontend/web/js/view/ajax-cart.js

define([
'jquery',
'uiComponent',
'Magento_Customer/js/customer-data'
], function ($, Component, customerData) {
'use strict';

return Component.extend({
defaults: {
ajaxCart: []
}
initialize: function () {
this._super();

this.ajaxCart = customerData.get('ajax-cart');
}
});
});

Através do this.ajaxCart definimos que nossos dados vão vir apartir da section ajax-cart, no template através do ajaxCart().grand_total por exemplo podemos agora fazer bind do dado direto no backend através das sections.

Bem esses foi um post bem básico sobre sections, tem muita coisa que pode ser feita com ela, vou deixar abaixo alguns links para consultarem e entenderem mais sobre as sections.

2017-04-27T20:02:45+00:00

RECEBA DICAS VALIOSAS NO SEU EMAIL

x