<?php
declare(strict_types=1);
namespace CioProductCustomerInputs\Storefront\Subscriber;
use CioProductCustomerInputs\CioProductCustomerInputs;
use CioProductCustomerInputs\Service\SessionService;
use Shopware\Core\Checkout\Cart\Error\IncompleteLineItemError;
use Shopware\Core\Checkout\Cart\Event\AfterLineItemAddedEvent;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Order\OrderConverter;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
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\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Struct\ArrayEntity;
use Shopware\Core\System\SalesChannel\Context\AbstractSalesChannelContextFactory;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextRestorer;
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
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\Product\ProductPageLoadedEvent;
use Swag\CmsExtensions\Storefront\Pagelet\Quickview\QuickviewPageletLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Contracts\Translation\TranslatorInterface;
class FrontendSubscriber implements EventSubscriberInterface
{
/**
* @var TranslatorInterface
*/
private TranslatorInterface $translator;
/**
* @var SystemConfigService
*/
private SystemConfigService $systemConfigService;
/**
* @var EntityRepositoryInterface
*/
private EntityRepositoryInterface $orderLineItemRepository;
private EntityRepositoryInterface $productRepository;
/**
* @var SessionService
*/
private SessionService $sessionService;
private CartService $cartService;
private AbstractSalesChannelContextFactory $salesChannelContextFactory;
public function __construct(
TranslatorInterface $translator,
SystemConfigService $systemConfigService,
EntityRepositoryInterface $orderLineItemRepository,
EntityRepositoryInterface $productRepository,
SessionService $sessionService,
CartService $cartService,
AbstractSalesChannelContextFactory $salesChannelContextFactory
) {
$this->translator = $translator;
$this->systemConfigService = $systemConfigService;
$this->orderLineItemRepository = $orderLineItemRepository;
$this->productRepository = $productRepository;
$this->sessionService = $sessionService;
$this->cartService = $cartService;
$this->salesChannelContextFactory = $salesChannelContextFactory;
}
public static function getSubscribedEvents(): array
{
if (class_exists('\\Swag\\CmsExtensions\\Storefront\\Pagelet\\Quickview\\QuickviewPageletLoadedEvent')) {
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded',
CheckoutCartPageLoadedEvent::class => 'onCheckoutCartPageLoaded',
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded',
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishPageLoaded',
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlaced',
AccountOrderPageLoadedEvent::class => 'onAccountOrderPageLoaded',
AccountOverviewPageLoadedEvent::class => 'onAccountOverviewPageLoaded',
QuickviewPageletLoadedEvent::class => 'onQuickviewPageletLoaded',
OffcanvasCartPageLoadedEvent::class => 'onOffcanvasCartPageLoadedEvent',
AfterLineItemAddedEvent::class => 'onLineItemAdded'
];
} else {
return [
ProductPageLoadedEvent::class => 'onProductPageLoaded',
CheckoutCartPageLoadedEvent::class => 'onCheckoutCartPageLoaded',
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded',
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishPageLoaded',
CheckoutOrderPlacedEvent::class => 'onCheckoutOrderPlaced',
AccountOrderPageLoadedEvent::class => 'onAccountOrderPageLoaded',
AccountOverviewPageLoadedEvent::class => 'onAccountOverviewPageLoaded',
OffcanvasCartPageLoadedEvent::class => 'onOffcanvasCartPageLoadedEvent',
AfterLineItemAddedEvent::class => 'onLineItemAdded'
];
}
}
/**
* provide the customer input on the product detail page
*/
public function onProductPageLoaded(ProductPageLoadedEvent $event): SalesChannelProductEntity
{
$customerInputShowOnProductDetailPage = $this->systemConfigService->get('CioProductCustomerInputs.config.customerInputShowOnProductDetailPage');
$product = $event->getPage()->getProduct();
$lineItemId = $event->getRequest()->get('lineItemId');
$lineItem = null;
if (!is_null($lineItemId)) {
$cart = $this->cartService->getCart($event->getSalesChannelContext()->getToken(), $event->getSalesChannelContext());
if (!$cart->has($lineItemId)) {
$request = $event->getRequest();
$response = new RedirectResponse($request->getRequestUri());
$response->send();
exit;
}
$lineItem = $cart->get($lineItemId);
}
if ($customerInputShowOnProductDetailPage == 'yes') {
if ($product->getCustomFields() && is_array($product->getCustomFields()) && isset($product->getCustomFields()['cio_customer_input_count_rows'])) {
for ($row = 1; $row <= $product->getCustomFields()['cio_customer_input_count_rows']; ++$row) {
for ($i = 1; $i <= CioProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$productExtensionData['value'] = $this->sessionService->getCustomerInputFromSession($product->getProductNumber(), 'value', $row, $i, $lineItemId, $lineItem);
$productExtensionData['checkboxValue'] = $this->sessionService->getCustomerInputFromSession($product->getProductNumber(), 'checkbox_value', $row, $i, $lineItemId, $lineItem);
$productExtension = $this->sessionService->createArrayEntity($productExtensionData);
$product->addExtension('cioCustomerInput' . $row . $i, $productExtension);
}
}
}
}
$cioCustomerInputCount['value'] = CioProductCustomerInputs::CUSTOMER_INPUT_COUNT;
$cioCustomerInputCountValue = $this->sessionService->createArrayEntity($cioCustomerInputCount);
$product->addExtension('cioCustomerInputCountValue', $cioCustomerInputCountValue);
return $product;
}
/**
* provide the customer input on the checkout cart page
*/
public function onCheckoutCartPageLoaded(CheckoutCartPageLoadedEvent $event): array
{
return $this->getLineItemsCustomerInput($event->getPage()->getCart()->getLineItems()->getElements(), $event->getPage()->getCart());
}
/**
* provide the customer input on the off-canvas cart page
*/
public function onOffcanvasCartPageLoadedEvent(OffcanvasCartPageLoadedEvent $event): array
{
return $this->getLineItemsCustomerInput($event->getPage()->getCart()->getLineItems()->getElements(), $event->getPage()->getCart());
}
/**
* provide the customer input on the checkout confirm page
*/
public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): array
{
$result = $this->getLineItemsCustomerInput($event->getPage()->getCart()->getLineItems()->getElements(), $event->getPage()->getCart());
foreach ($result as $lineItem) {
if (!($lineItem instanceof LineItem) || $lineItem->getType() !== LineItem::PRODUCT_LINE_ITEM_TYPE) {
continue;
}
$product = $this->productRepository->search(new Criteria([$lineItem->getReferencedId()]), Context::createDefaultContext())->first();
/** @var array $customFields */
$customFields = $product->getCustomFields();
if (is_null($customFields)) {
continue;
}
$checkIndexes = [];
$numberOfRows = array_key_exists('cio_customer_input_count_rows', $customFields) ? (int)$customFields['cio_customer_input_count_rows'] : 1;
for ($i = 1; $i <= 10; $i++) {
if (isset($customFields['cio_customer_input_' . $i . '_active']) && $customFields['cio_customer_input_' . $i . '_active'] === true &&
isset($customFields['cio_customer_input_' . $i . '_required']) && $customFields['cio_customer_input_' . $i . '_required'] === true) {
// add to $checkIndexes for every row
for ($j = 1; $j <= $numberOfRows; $j++) {
$checkIndexes[] = 'cioLineItemCustomerInput' . $j . $i;
}
}
}
foreach ($checkIndexes as $checkIndex) {
$lineItemExtension = $lineItem->getExtension($checkIndex);
if (!$lineItemExtension instanceof ArrayEntity) {
//$event->getPage()->getCart()->getErrors()->add(new IncompleteLineItemError('', 'missingRequiredCustomFields'));
break;
}
if (empty($lineItemExtension->get('value'))) {
//$event->getPage()->getCart()->getErrors()->add(new IncompleteLineItemError('', 'missingRequiredCustomFields'));
break;
}
}
}
return $result;
}
/**
* save the customer inputs in the order line item custom fields after a successful order
*/
public function onCheckoutFinishPageLoaded(CheckoutFinishPageLoadedEvent $event): void
{
$this->getLineItemsCustomerInput($event->getPage()->getOrder()->getLineItems()->getElements());
$event->getPage()->assign([
'cioCustomerInputCountValue' => CioProductCustomerInputs::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([
'cioCustomerInputCountValue' => CioProductCustomerInputs::CUSTOMER_INPUT_COUNT,
]);
}
/**
* assign the customer input count value to the account overview page
*/
public function onAccountOverviewPageLoaded(AccountOverviewPageLoadedEvent $event): void
{
$event->getPage()->assign([
'cioCustomerInputCountValue' => CioProductCustomerInputs::CUSTOMER_INPUT_COUNT,
]);
}
/**
* provide the customer input on the quickview
* @return ProductEntity
*/
public function onQuickviewPageletLoaded($event): ProductEntity
{
$customerInputShowOnProductDetailPage = $this->systemConfigService->get('CioProductCustomerInputs.config.customerInputShowOnProductDetailPage');
$product = $event->getPagelet()->getProduct();
if ($customerInputShowOnProductDetailPage == 'yes') {
for ($row = 1; $row <= $product->getCustomFields()['cio_customer_input_count_rows']; ++$row) {
for ($i = 1; $i <= CioProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$productExtensionData['value'] = $this->sessionService->getCustomerInputFromSession($product->getProductNumber(), 'value', $row, $i);
$productExtensionData['checkboxValue'] = $this->sessionService->getCustomerInputFromSession($product->getProductNumber(), 'checkbox_value', $row, $i);
$productExtension = $this->sessionService->createArrayEntity($productExtensionData);
$product->addExtension('cioCustomerInput' . $row . $i, $productExtension);
}
}
}
$cioCustomerInputCount['value'] = CioProductCustomerInputs::CUSTOMER_INPUT_COUNT;
$cioCustomerInputCountValue = $this->sessionService->createArrayEntity($cioCustomerInputCount);
$product->addExtension('cioCustomerInputCountValue', $cioCustomerInputCountValue);
return $product;
}
public function onLineItemAdded(AfterLineItemAddedEvent $event)
{
foreach ($event->getLineItems() as $lineItem) {
$this->sessionService->cloneSessionToLineItemSession($lineItem);
}
}
/**
* save the customer inputs in the order line item custom fields
*/
protected function saveCustomerInputsInLineItemCustomFields($event, $lineItems, $isCheckoutFinishPage): void
{
$customerInputTransferUnselectedCheckboxFieldsAsValue = $this->systemConfigService->get('CioProductCustomerInputs.config.customerInputTransferUnselectedCheckboxFieldsAsValue');
foreach ($lineItems as $item) {
/** @var OrderLineItemEntity $item */
if ($item->getType() !== LineItem::PROMOTION_LINE_ITEM_TYPE) {
$productNumber = $item->getPayload()['productNumber'];
$cioCustomerInputValueArray = [];
$cioCustomerInputHasValue = 0;
if (isset($item->getPayload()['customFields']['cio_customer_input_count_rows'])) {
for ($row = 1; $row <= $item->getPayload()['customFields']['cio_customer_input_count_rows']; ++$row) {
for ($i = 1; $i <= CioProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$lineItem = null;
if (isset($item->getPayload()['cartToken'])) {
$token = $item->getPayload()['cartToken'];
$salesChannelContext = $this->salesChannelContextFactory->create($token, $event->getOrder()->getSalesChannelId(), [SalesChannelContextService::LANGUAGE_ID => $event->getOrder()->getLanguageId()]);
$lineItem = $this->cartService->getCart($token, $salesChannelContext)->get($item->getIdentifier());
}
$cioCustomerInputValue = $this->sessionService->getCustomerInputFromSession($productNumber, 'value', $row, $i, $item->getIdentifier(), $lineItem);
$cioCustomerInputCheckboxValue = $this->sessionService->getCustomerInputFromSession($productNumber, 'checkbox_value', $row, $i, $item->getIdentifier(), $lineItem);
$cioCustomerInputLabel = $this->sessionService->getCustomerInputFromSession($productNumber, 'label', $row, $i, $item->getIdentifier(), $lineItem);
$cioCustomerInputPlaceholder = $this->sessionService->getCustomerInputFromSession($productNumber, 'placeholder', $row, $i, $item->getIdentifier(), $lineItem);
$cioCustomerInputFieldType = $this->sessionService->getCustomerInputFromSession($productNumber, 'fieldtype', $row, $i, $item->getIdentifier(), $lineItem);
if ($cioCustomerInputValue || (($customerInputTransferUnselectedCheckboxFieldsAsValue) && ($cioCustomerInputFieldType == 'boolean') && ($cioCustomerInputValue == 0))) {
$cioCustomerInputValueArray[$row][$i]['value'] = ($cioCustomerInputFieldType == 'boolean' ? ($cioCustomerInputValue == 1 ? $this->translator->trans('cio.customerInput.selectedValue') : $this->translator->trans('cio.customerInput.unselectedValue')) : $cioCustomerInputValue);
$cioCustomerInputValueArray[$row][$i]['checkbox_value'] = $cioCustomerInputCheckboxValue;
$cioCustomerInputValueArray[$row][$i]['label'] = $cioCustomerInputLabel;
$cioCustomerInputValueArray[$row][$i]['placeholder'] = $cioCustomerInputPlaceholder;
$cioCustomerInputValueArray[$row][$i]['fieldtype'] = $cioCustomerInputFieldType;
$cioCustomerInputHasValue = $cioCustomerInputHasValue + 1;
} elseif ($customerInputTransferUnselectedCheckboxFieldsAsValue) {
if ($item->getCustomFields() and (!isset($item->getCustomFields()['cio_customer_input_' . $i . '_value']))) {
$productCustomFields = $item->getPayload()['customFields'];
if (isset($productCustomFields['cio_customer_input_' . $i . '_active'])) {
if ($productCustomFields['cio_customer_input_' . $i . '_active'] && $productCustomFields['cio_customer_input_' . $i . '_fieldtype'] == 'boolean') {
$cioCustomerInputValue = $this->sessionService->getCustomerInputFromSession($productNumber, 'value', $row, $i, $item->getId(), $lineItem);
if ($cioCustomerInputValue == '') {
$cioCustomerInputValue = $this->translator->trans('cio.customerInput.unselectedValue');
$cioCustomerInputLabel = (isset($productCustomFields['cio_customer_input_' . $i . '_title']) ? ($productCustomFields['cio_customer_input_' . $i . '_title'] !== '' ? $productCustomFields['cio_customer_input_' . $i . '_title'] : $this->translator->trans('cio.customerInput.titleLabel')) : $this->translator->trans('cio.customerInput.titleLabel'));
$cioCustomerInputPlaceholder = (isset($productCustomFields['cio_customer_input_' . $i . '_placeholder']) ? ($productCustomFields['cio_customer_input_' . $i . '_placeholder'] !== '' ? $productCustomFields['cio_customer_input_' . $i . '_placeholder'] : $this->translator->trans('cio.customerInput.placeholderLabel')) : $this->translator->trans('cio.customerInput.placeholderLabel'));
$cioCustomerInputFieldType = $productCustomFields['cio_customer_input_' . $i . '_fieldtype'];
$cioCustomerInputValueArray[$row][$i]['value'] = $cioCustomerInputValue;
$cioCustomerInputValueArray[$row][$i]['label'] = $cioCustomerInputLabel;
$cioCustomerInputValueArray[$row][$i]['placeholder'] = $cioCustomerInputPlaceholder;
$cioCustomerInputValueArray[$row][$i]['fieldtype'] = $cioCustomerInputFieldType;
$cioCustomerInputHasValue = $cioCustomerInputHasValue + 1;
}
}
}
}
}
}
}
}
$cioCustomerInputValueCustomFieldArray = [];
$cioCustomerInputValueTypeArray = array('value', 'checkbox_value', 'label', 'placeholder', 'fieldtype');
if (isset($item->getPayload()['customFields']['cio_customer_input_count_rows'])) {
for ($row = 1; $row <= $item->getPayload()['customFields']['cio_customer_input_count_rows']; ++$row) {
for ($i = 1; $i <= CioProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
foreach ($cioCustomerInputValueTypeArray as $ta) {
if (isset($cioCustomerInputValueArray[$row][$i][$ta])) {
$cioCustomerInputValueCustomFieldArray['cio_customer_input_' . $row . '_' . $i . '_' . $ta] = $cioCustomerInputValueArray[$row][$i][$ta];
}
}
}
}
}
$item->setCustomFields(
$cioCustomerInputValueCustomFieldArray
);
if ($isCheckoutFinishPage) {
for ($row = 1; $row <= $item->getPayload()['customFields']['cio_customer_input_count_rows']; ++$row) {
for ($i = 1; $i <= CioProductCustomerInputs::CUSTOMER_INPUT_COUNT; ++$i) {
$this->sessionService->removeCustomerInputFromSession($productNumber, $row, $i, $item->getId());
}
}
}
if ($cioCustomerInputHasValue > 0) {
$this->orderLineItemRepository->upsert(
[
[
'id' => $item->getId(),
'customFields' => $item->getCustomFields(),
],
],
$event->getContext() ?? Context::createDefaultContext()
);
}
}
}
}
/**
* get the customer input for each line item
*/
private function getLineItemsCustomerInput($lineItems, $cart = null): array
{
foreach ($lineItems as $lineItem) {
$this->sessionService->addLineItemsCustomerInput($lineItem, $cart);
}
return $lineItems;
}
}