<?php
declare(strict_types=1);
namespace Mw\ProductCustomerInputs\Storefront\Subscriber;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\Struct\ArrayEntity;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent;
use Shopware\Storefront\Page\Account\Overview\AccountOverviewPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoadedEvent;
use Shopware\Storefront\Page\Navigation\NavigationPageLoadedEvent;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Swag\CmsExtensions\Storefront\Pagelet\Quickview\QuickviewPageletLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Contracts\Translation\TranslatorInterface;
use Mw\ProductCustomerInputs\MwProductCustomerInputs;
class FrontendSubscriber implements EventSubscriberInterface
{
const PRODUCT_LINE_ITEM_TYPE = 'product';
/**
* @var Session
*/
private $session;
/**
* @var TranslatorInterface
*/
private $translator;
/**
* @var SystemConfigService
*/
private $systemConfigService;
/**
* @var EntityRepositoryInterface
*/
private $orderLineItemRepository;
public function __construct(
Session $session,
TranslatorInterface $translator,
SystemConfigService $systemConfigService,
EntityRepositoryInterface $orderLineItemRepository
) {
$this->session = $session;
$this->translator = $translator;
$this->systemConfigService = $systemConfigService;
$this->orderLineItemRepository = $orderLineItemRepository;
}
public static function getSubscribedEvents(): array
{
if(class_exists('\\Swag\\CmsExtensions\\Storefront\\Pagelet\\Quickview\\QuickviewPageletLoadedEvent')) {
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded',
OffcanvasCartPageLoadedEvent::class => 'onOffcanvasCartPageLoaded',
CheckoutCartPageLoadedEvent::class => 'onCheckoutCartPageLoaded',
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded',
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishPageLoaded',
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlaced',
AccountOrderPageLoadedEvent::class => 'onAccountOrderPageLoaded',
AccountOverviewPageLoadedEvent::class => 'onAccountOverviewPageLoaded',
NavigationPageLoadedEvent::class => 'onNavigationPageLoaded',
QuickviewPageletLoadedEvent::class => 'onQuickviewPageletLoaded',
];
}else{
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded',
OffcanvasCartPageLoadedEvent::class => 'onOffcanvasCartPageLoaded',
CheckoutCartPageLoadedEvent::class => 'onCheckoutCartPageLoaded',
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded',
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishPageLoaded',
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlaced',
AccountOrderPageLoadedEvent::class => 'onAccountOrderPageLoaded',
AccountOverviewPageLoadedEvent::class => 'onAccountOverviewPageLoaded',
NavigationPageLoadedEvent::class => 'onNavigationPageLoaded',
];
}
}
/**
* provide the customer input on the product detail page
*/
public function onProductPageLoaded(ProductPageLoadedEvent $event): SalesChannelProductEntity
{
$mwCustomerInputShowOnProductDetailPage = $this->systemConfigService->get('MwProductCustomerInputs.config.mwCustomerInputShowOnProductDetailPage');
$product = $event->getPage()->getProduct();
if ($mwCustomerInputShowOnProductDetailPage == 'yes') {
for ($i = 1; $i <= MwProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$productExtensionData['value'] = $this->getCustomerInputFromSession($product->getProductNumber(), 'value', $i);
$productExtension = $this->createArrayEntity($productExtensionData);
$product->addExtension('mwCustomerInput' . $i, $productExtension);
}
}
$mwCustomerInputCount['value'] = MwProductCustomerInputs::CUSTOMER_INPUT_COUNT;
$mwCustomerInputCountValue = $this->createArrayEntity($mwCustomerInputCount);
$product->addExtension('mwCustomerInputCountValue', $mwCustomerInputCountValue);
return $product;
}
/**
* provide the customer input on the offcanvas cart page
*/
public function onOffcanvasCartPageLoaded(OffcanvasCartPageLoadedEvent $event): array
{
return $this->getLineItemsCustomerInput($event);
}
/**
* provide the customer input on the checkout cart page
*/
public function onCheckoutCartPageLoaded(CheckoutCartPageLoadedEvent $event): array
{
return $this->getLineItemsCustomerInput($event);
}
/**
* provide the customer input on the checkout confirm page
*/
public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): array
{
return $this->getLineItemsCustomerInput($event);
}
/**
* save the customer inputs in the order line item custom fields after a successful order
*/
public function onCheckoutFinishPageLoaded(CheckoutFinishPageLoadedEvent $event): void
{
$lineItems = $event->getPage()->getOrder()->getLineItems();
$this->saveCustomerInputsInLineItemCustomFields($event, $lineItems, true);
$event->getPage()->assign([
'mwCustomerInputCountValue' => MwProductCustomerInputs::CUSTOMER_INPUT_COUNT,
]);
}
/**
* save the customer inputs in the order line item custom fields when order is placed
*/
public function onCheckoutOrderPlaced(CheckoutOrderPlacedEvent $event): void
{
$lineItems = $event->getOrder()->getLineItems();
$this->saveCustomerInputsInLineItemCustomFields($event, $lineItems, false);
}
/**
* assign the customer input count value to the account order page
*/
public function onAccountOrderPageLoaded(AccountOrderPageLoadedEvent $event): void
{
$event->getPage()->assign([
'mwCustomerInputCountValue' => MwProductCustomerInputs::CUSTOMER_INPUT_COUNT,
]);
}
/**
* assign the customer input count value to the account overview page
*/
public function onAccountOverviewPageLoaded(AccountOverviewPageLoadedEvent $event): void
{
$event->getPage()->assign([
'mwCustomerInputCountValue' => MwProductCustomerInputs::CUSTOMER_INPUT_COUNT,
]);
}
/**
* assign the customer input count value to the navigation page
*/
public function onNavigationPageLoaded(NavigationPageLoadedEvent $event): void
{
$event->getPage()->assign([
'mwCustomerInputCountValue' => MwProductCustomerInputs::CUSTOMER_INPUT_COUNT,
]);
}
/**
* provide the customer input on the quickview
* @param QuickviewPageletLoadedEvent
*/
public function onQuickviewPageletLoaded($event): ProductEntity
{
$mwCustomerInputShowOnProductDetailPage = $this->systemConfigService->get('MwProductCustomerInputs.config.mwCustomerInputShowOnProductDetailPage');
$product = $event->getPagelet()->getProduct();
if ($mwCustomerInputShowOnProductDetailPage == 'yes') {
for ($i = 1; $i <= MwProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$productExtensionData['value'] = $this->getCustomerInputFromSession($product->getProductNumber(), 'value', $i);
$productExtension = $this->createArrayEntity($productExtensionData);
$product->addExtension('mwCustomerInput' . $i, $productExtension);
}
}
$mwCustomerInputCount['value'] = MwProductCustomerInputs::CUSTOMER_INPUT_COUNT;
$mwCustomerInputCountValue = $this->createArrayEntity($mwCustomerInputCount);
$product->addExtension('mwCustomerInputCountValue', $mwCustomerInputCountValue);
return $product;
}
/**
* save the customer inputs in the order line item custom fields
*/
protected function saveCustomerInputsInLineItemCustomFields($event, $lineItems, $isCheckoutFinishPage): void
{
$mwCustomerInputTransferUnselectedCheckboxFieldsAsValue = $this->systemConfigService->get('MwProductCustomerInputs.config.mwCustomerInputTransferUnselectedCheckboxFieldsAsValue');
foreach ($lineItems as $lineItem) {
if ($lineItem->getType() === self::PRODUCT_LINE_ITEM_TYPE) {
$productNumber = $lineItem->getPayload()['productNumber'] ?? null;
if ($lineItem->getType() === self::PRODUCT_LINE_ITEM_TYPE && $productNumber) {
$mwCustomerInputValueArray = [];
$mwCustomerInputHasValue = 0;
for ($i = 1; $i <= MwProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$mwCustomerInputValue = $this->getCustomerInputFromSession($productNumber, 'value', $i);
$mwCustomerInputLabel = $this->getCustomerInputFromSession($productNumber, 'label', $i);
$mwCustomerInputPlaceholder = $this->getCustomerInputFromSession($productNumber, 'placeholder', $i);
$mwCustomerInputFieldType = $this->getCustomerInputFromSession($productNumber, 'fieldtype', $i);
if ($mwCustomerInputValue || (($mwCustomerInputTransferUnselectedCheckboxFieldsAsValue) && ($mwCustomerInputFieldType == 'boolean') && ($mwCustomerInputValue == 0))) {
$mwCustomerInputValueArray[$i]['value'] = ($mwCustomerInputFieldType == 'boolean' ? ($mwCustomerInputValue == 1 ? $this->translator->trans('mw.mwCustomerInput.selectedValue') : $this->translator->trans('mw.mwCustomerInput.unselectedValue')) : $mwCustomerInputValue);
$mwCustomerInputValueArray[$i]['label'] = $mwCustomerInputLabel;
$mwCustomerInputValueArray[$i]['placeholder'] = $mwCustomerInputPlaceholder;
$mwCustomerInputValueArray[$i]['fieldtype'] = $mwCustomerInputFieldType;
$mwCustomerInputHasValue = $mwCustomerInputHasValue + 1;
} elseif ($mwCustomerInputTransferUnselectedCheckboxFieldsAsValue) {
if ($lineItem->getCustomFields() and (!isset($lineItem->getCustomFields()['mw_customer_input_' . $i . '_value']))) {
$productCustomFields = $lineItem->getPayload()['customFields'];
if (isset($productCustomFields['mw_customer_input_' . $i . '_active'])) {
if ($productCustomFields['mw_customer_input_' . $i . '_active'] && $productCustomFields['mw_customer_input_' . $i . '_fieldtype'] == 'boolean') {
$mwCustomerInputValue = $this->getCustomerInputFromSession($productNumber, 'value', $i);
if ($mwCustomerInputValue == '') {
$mwCustomerInputValue = $this->translator->trans('mw.mwCustomerInput.unselectedValue');
$mwCustomerInputLabel = (isset($productCustomFields['mw_customer_input_' . $i . '_title']) ? ($productCustomFields['mw_customer_input_' . $i . '_title'] !== '' ? $productCustomFields['mw_customer_input_' . $i . '_title'] : $this->translator->trans('mw.mwCustomerInput.titleLabel')) : $this->translator->trans('mw.mwCustomerInput.titleLabel'));
$mwCustomerInputPlaceholder = (isset($productCustomFields['mw_customer_input_' . $i . '_placeholder']) ? ($productCustomFields['mw_customer_input_' . $i . '_placeholder'] !== '' ? $productCustomFields['mw_customer_input_' . $i . '_placeholder'] : $this->translator->trans('mw.mwCustomerInput.placeholderLabel')) : $this->translator->trans('mw.mwCustomerInput.placeholderLabel'));
$mwCustomerInputFieldType = $productCustomFields['mw_customer_input_' . $i . '_fieldtype'];
$mwCustomerInputValueArray[$i]['value'] = $mwCustomerInputValue;
$mwCustomerInputValueArray[$i]['label'] = $mwCustomerInputLabel;
$mwCustomerInputValueArray[$i]['placeholder'] = $mwCustomerInputPlaceholder;
$mwCustomerInputValueArray[$i]['fieldtype'] = $mwCustomerInputFieldType;
$mwCustomerInputHasValue = $mwCustomerInputHasValue + 1;
}
}
}
}
}
}
$mwCustomerInputValueCustomFieldArray = [];
$mwCustomerInputValueTypeArray = array('value', 'label', 'placeholder', 'fieldtype');
for ($i = 1; $i <= MwProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
foreach ($mwCustomerInputValueTypeArray as $ta) {
if (isset($mwCustomerInputValueArray[$i][$ta])) {
$mwCustomerInputValueCustomFieldArray['mw_customer_input_' . $i . '_' . $ta] = $mwCustomerInputValueArray[$i][$ta];
}
}
}
$lineItem->setCustomFields(
$mwCustomerInputValueCustomFieldArray
);
if ($isCheckoutFinishPage) {
for ($i = 1; $i <= MwProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$this->removeCustomerInputFromSession($productNumber, $i);
}
}
if ($mwCustomerInputHasValue > 0) {
$this->orderLineItemRepository->upsert(
[
[
'id' => $lineItem->getId(),
'customFields' => $lineItem->getCustomFields(),
],
],
$event->getContext() ?? Context::createDefaultContext()
);
}
}
}
}
}
/**
* get the customer input for each line item
*/
private function getLineItemsCustomerInput($event): array
{
$lineItems = $event->getPage()->getCart()->getLineItems()->getElements();
foreach ($lineItems as $lineItem) {
if ($lineItem->getType() === self::PRODUCT_LINE_ITEM_TYPE) {
$productNumber = $lineItem->getPayloadValue('productNumber') ?? null;
if ($lineItem->getType() === self::PRODUCT_LINE_ITEM_TYPE && $productNumber) {
for ($i = 1; $i <= MwProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$productExtensionData['value'] = $this->getCustomerInputFromSession($productNumber, 'value', $i);
$lineItemExtension = $this->createArrayEntity($productExtensionData);
$lineItem->addExtension('mwLineItemCustomerInput' . $i, $lineItemExtension);
}
$mwLineItemCustomerInputCount['value'] = MwProductCustomerInputs::CUSTOMER_INPUT_COUNT;
$mwLineItemCustomerInputCountValue = $this->createArrayEntity($mwLineItemCustomerInputCount);
$lineItem->addExtension('mwLineItemCustomerInputCountValue', $mwLineItemCustomerInputCountValue);
}
}
}
return $lineItems;
}
/**
* create an ArrayEntity with the given data
*/
private function createArrayEntity($extensionData): ArrayEntity
{
return new ArrayEntity($extensionData);
}
/**
* get the customer input from the session based on the product number
*/
private function getCustomerInputFromSession($productNumber, $type, $count): string
{
$sessionCustomerInput = '';
$mwCustomerInput = sprintf('mw_customer_input_%d_%s', $count, $productNumber);
if ($this->session->has($mwCustomerInput)) {
if (in_array($type, ['value', 'label', 'placeholder', 'fieldtype'])) {
$sessionCustomerInput = $this->session->get($mwCustomerInput)['mw_customer_input_' . $type];
}
if ($type == 'value' && $sessionCustomerInput == null) {
$sessionCustomerInput = '';
}
}
return $sessionCustomerInput;
}
/**
* removes the customer input from the session based on the product number
*/
private function removeCustomerInputFromSession($productNumber, $count): void
{
$this->session->remove('mw_customer_input_' . $count . '_' . $productNumber);
}
}