Skip to content

Commit

Permalink
feat: Add subscription functionality on grouped products.
Browse files Browse the repository at this point in the history
Refs: #207
  • Loading branch information
eugene-shab committed Dec 27, 2023
1 parent 5b035ef commit 5f31561
Show file tree
Hide file tree
Showing 9 changed files with 471 additions and 0 deletions.
101 changes: 101 additions & 0 deletions Block/Product/Grouped.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace Swarming\SubscribePro\Block\Product;

use Magento\Framework\Exception\NoSuchEntityException;
use SubscribePro\Exception\HttpException;
use SubscribePro\Exception\InvalidArgumentException;
use SubscribePro\Service\Product\ProductInterface as ProductInterfaceAlias;
use Swarming\SubscribePro\Helper\Product;

class Grouped extends Subscription
{
private $helper;
public function __construct(
\Magento\Catalog\Block\Product\Context $context,
\Swarming\SubscribePro\Model\Config\SubscriptionDiscount $subscriptionDiscountConfig,
\Swarming\SubscribePro\Platform\Manager\Product $platformProductManager,
\Magento\Tax\Api\TaxCalculationInterface $taxCalculation,
\Swarming\SubscribePro\Ui\ConfigProvider\PriceConfig $priceConfigProvider,
\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
\Swarming\SubscribePro\Helper\Product $productHelper,
\Magento\Checkout\Model\Cart $cart,
\Swarming\SubscribePro\Helper\QuoteItem $quoteItemHelper,
\Psr\Log\LoggerInterface $logger,
Product $helper,
array $data = []
) {
$this->helper = $helper;
parent::__construct(
$context,
$subscriptionDiscountConfig,
$platformProductManager,
$taxCalculation,
$priceConfigProvider,
$priceCurrency,
$productHelper,
$cart,
$quoteItemHelper,
$logger,
$data
);
}

/**
* @return void
* @throws NoSuchEntityException
*/
protected function initJsLayout(): void
{
try {
$platformProduct = $this->getPlatformProduct()->toArray();
} catch (InvalidArgumentException|HttpException $e) {
$this->logger->debug('Could not load product from Subscribe Pro platform.');
$this->logger->info($e->getMessage());
$platformProduct = [];
}

$data = [
'components' => [
'subscription-container-' . $this->getProduct()->getId() => [
'component' => 'Swarming_SubscribePro/js/view/product/grouped-subscription',
'config' => [
'oneTimePurchaseOption' => ProductInterfaceAlias::SO_ONETIME_PURCHASE,
'subscriptionOption' => ProductInterfaceAlias::SO_SUBSCRIPTION,
'subscriptionOnlyMode' => ProductInterfaceAlias::SOM_SUBSCRIPTION_ONLY,
'subscriptionAndOneTimePurchaseMode' => ProductInterfaceAlias::SOM_SUBSCRIPTION_AND_ONETIME_PURCHASE,
'product' => $platformProduct,
'product_id' => $this->getProduct()->getId(),
'priceConfig' => $this->priceConfigProvider->getConfig(),
'template' => 'Swarming_SubscribePro/product/grouped-subscription',
'priceBoxSelector' => '.price-box',
'messages' => [
'component' => 'Magento_Ui/js/view/messages',
'displayArea' => 'messages',
],
]
]
]
];

$jsLayout = array_replace_recursive($this->jsLayout, $data);
if ($this->isPriceHidden()) {
$class = 'Swarming_SubscribePro/js/view/product/subscription-msrp';
$jsLayout['components']['subscription-container-' . $this->getProduct()->getId()]['component'] = $class;
$jsLayout['components']['subscription-container-' . $this->getProduct()->getId()]['config']['msrpPrice'] = $this->getMsrpPrice();
}

$this->jsLayout = $jsLayout;
}

/**
* @param $item
* @return bool
*/
public function isSubscriptionEnabled($item): bool
{
return $this->helper->isSubscriptionEnabled($item);
}
}
85 changes: 85 additions & 0 deletions Model/Product/Type/Grouped.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace Swarming\SubscribePro\Model\Product\Type;

use Magento\Framework\DataObject;
use Magento\Framework\Phrase;

class Grouped extends \Magento\GroupedProduct\Model\Product\Type\Grouped
{
/**
* @param DataObject $buyRequest
* @param $product
* @param $processMode
* @return Phrase|array|string
*/
protected function _prepareProduct(DataObject $buyRequest, $product, $processMode): Phrase|array|string
{
$products = [];
$associatedProductsInfo = [];
$isStrictProcessMode = $this->_isStrictProcessMode($processMode);
$subscriptionConfig = $buyRequest->getData('subscription_option');
$productsInfo = $this->getProductInfo($buyRequest, $product, $isStrictProcessMode);
if (is_string($productsInfo)) {
return $productsInfo;
}
$associatedProducts = !$isStrictProcessMode || !empty($productsInfo)
? $this->getAssociatedProducts($product)
: false;

foreach ($associatedProducts as $subProduct) {
$qty = $productsInfo[$subProduct->getId()];
if (!is_numeric($qty) || empty($qty)) {
continue;
}

$_result = $subProduct->getTypeInstance()->_prepareProduct($buyRequest, $subProduct, $processMode);

if (is_string($_result)) {
return $_result;
} elseif (!isset($_result[0])) {
return __('Cannot process the item.')->render();
}

if ($isStrictProcessMode) {
$subscription = [];
if (isset($subscriptionConfig[$subProduct->getId()])) {
$subscription = $subscriptionConfig[$subProduct->getId()];
}
$_result[0]->setCartQty($qty);
$_result[0]->addCustomOption('product_type', self::TYPE_CODE, $product);
$_result[0]->addCustomOption(
'info_buyRequest',
$this->serializer->serialize(
[
'super_product_config' => [
'product_type' => self::TYPE_CODE,
'product_id' => $product->getId(),
],
'subscription_option' => $subscription
]
)
);
$products[] = $_result[0];
} else {
$associatedProductsInfo[] = [$subProduct->getId() => $qty];
$product->addCustomOption('associated_product_' . $subProduct->getId(), $qty);
}
}

if (!$isStrictProcessMode || count($associatedProductsInfo)) {
$product->addCustomOption('product_type', self::TYPE_CODE, $product);
$product->addCustomOption('info_buyRequest', $this->serializer->serialize($buyRequest->getData()));

$products[] = $product;
}

if (count($products)) {
return $products;
}

return __('Please specify the quantity of product(s).')->render();
}
}
31 changes: 31 additions & 0 deletions ViewModel/Grouped.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
namespace Swarming\SubscribePro\ViewModel;

use Magento\Framework\View\Element\Block\ArgumentInterface;
use Swarming\SubscribePro\Helper\Product;

class Grouped implements ArgumentInterface
{
/**
* @var Product
*/
private Product $helper;

/**
* @param Product $helper
*/
public function __construct(
Product $helper
) {
$this->helper = $helper;
}

/**
* @param $item
* @return bool
*/
public function isSubscriptionEnabled($item): bool
{
return $this->helper->isSubscriptionEnabled($item);
}
}
2 changes: 2 additions & 0 deletions etc/frontend/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
<plugin name="subscribepro_subscription" type="Swarming\SubscribePro\Plugin\Product\Subscription"/>
</type>

<preference for="Magento\GroupedProduct\Model\Product\Type\Grouped" type="Swarming\SubscribePro\Model\Product\Type\Grouped" />

<type name="Swarming\SubscribePro\Platform\Webhook\HandlerPool">
<arguments>
<argument name="handlers" xsi:type="array">
Expand Down
12 changes: 12 additions & 0 deletions view/frontend/layout/catalog_product_view_type_grouped.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="product.info.grouped" template="Swarming_SubscribePro::product/view/type/grouped.phtml">
<arguments>
<argument name="view_model" xsi:type="object">Swarming\SubscribePro\ViewModel\Grouped</argument>
</arguments>
<block class="Swarming\SubscribePro\Block\Product\Grouped" name="product.info.subscription.options.grouped" as="product_info_subscription_options_grouped" template="Swarming_SubscribePro::product/grouped-subscription.phtml">
</block>
</referenceBlock>
</body>
</page>
20 changes: 20 additions & 0 deletions view/frontend/templates/product/grouped-subscription.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* @var $block \Swarming\SubscribePro\Block\Product\Grouped
*/

?>

<div class="subscription-container" data-bind="scope: 'subscription-container-<?= (int)$block->getProduct()->getId() ?>'">
<!-- ko template: getTemplate() --><!-- /ko -->
</div>

<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app": <?= $block->getJsLayout() ?>
}
}
</script>


98 changes: 98 additions & 0 deletions view/frontend/templates/product/view/type/grouped.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
/**
* Grouped product data template
*
* @var $block \Magento\Catalog\Block\Product\View\BaseImage
* @var $block \Swarming\SubscribePro\Model\Product\Type\Grouped
* @var \Swarming\SubscribePro\ViewModel\Grouped $viewModel
*/
?>
<?php $viewModel = $block->getData('view_model'); ?>
<?php $block->setPreconfiguredValue(); ?>
<?php $_product = $block->getProduct(); ?>
<?php $_associatedProducts = $block->getAssociatedProducts($_product); ?>
<?php $_hasAssociatedProducts = count($_associatedProducts) > 0; ?>

<div class="table-wrapper grouped">
<table class="table data grouped"
id="super-product-table"
data-mage-init='{ "Magento_GroupedProduct/js/product-ids-resolver": {} }'>
<caption class="table-caption"><?= $block->escapeHtml(__('Grouped product items')) ?></caption>
<thead>
<tr>
<th class="col item" scope="col"><?= $block->escapeHtml(__('Product Name')) ?></th>
<?php if ($_product->isSaleable()) : ?>
<th class="col qty" scope="col"><?= $block->escapeHtml(__('Qty')) ?></th>
<?php endif; ?>
</tr>
</thead>

<?php if ($_hasAssociatedProducts) : ?>
<tbody>
<?php foreach ($_associatedProducts as $_item) : ?>
<tr>
<td data-th="<?= $block->escapeHtml(__('Product Name')) ?>" class="col item">
<strong class="product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong>
<?php if ($block->getCanShowProductPrice($_product)) : ?>
<?php if ($block->getCanShowProductPrice($_item)) : ?>
<?= /* @noEscape */ $block->getProductPrice($_item) ?>
<?php endif; ?>
<?php endif; ?>
<?php if ($viewModel->isSubscriptionEnabled($_item)): ?>
<div data-id="<?= $_item->getId()?>">
<?php echo $block->getChildBlock('product_info_subscription_options_grouped')->setProduct($_item)->toHtml()?>
</div>
<?php endif; ?>
</td>

<?php if ($_product->isSaleable()) : ?>
<td data-th="<?= $block->escapeHtml(__('Qty')) ?>" class="col qty">
<?php if ($_item->isSaleable()) : ?>
<div class="control qty">
<input type="number"
name="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]"
data-selector="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]"
value="<?= $block->escapeHtmlAttr($_item->getQty() * 1) ?>"
title="<?= $block->escapeHtmlAttr(__('Qty')) ?>"
class="input-text qty"
data-validate="{'validate-grouped-qty':'#super-product-table'}"
data-errors-message-box="#validation-message-box"/>
</div>
<?php else : ?>
<div class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>">
<span><?= $block->escapeHtml(__('Out of stock')) ?></span>
</div>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php if ($block->getCanShowProductPrice($_product)
&& $block->getCanShowProductPrice($_item)
&& trim($block->getProductPriceHtml(
$_item,
\Magento\Catalog\Pricing\Price\TierPrice::PRICE_CODE
))) : ?>
<tr class="row-tier-price">
<td colspan="2">
<?= $block->getProductPriceHtml(
$_item,
\Magento\Catalog\Pricing\Price\TierPrice::PRICE_CODE
) ?>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
<?php else : ?>
<tbody>
<tr>
<td class="unavailable"
colspan="<?php if ($_product->isSaleable()) : ?>4<?php else : ?>3<?php endif; ?>">
<?= $block->escapeHtml(__('No options of this product are available.')) ?>
</td>
</tr>
</tbody>
<?php endif; ?>
</table>
</div>
<div id="validation-message-box"></div>
Loading

0 comments on commit 5f31561

Please sign in to comment.