vendor/store.shopware.com/reschannableconnector/src/Subscriber/Subscriber.php line 160

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Res\ResChannableConnector\Subscriber;
  3. use Doctrine\DBAL\Connection;
  4. use Res\ResChannableConnector\Setting\Service\Config\ResChannableConfigService;
  5. use Shopware\Core\Content\Product\ProductDefinition;
  6. use Shopware\Core\Content\Product\ProductEntity;
  7. use Shopware\Core\Framework\Context;
  8. use Shopware\Core\Framework\DataAbstractionLayer\EntityWriteResult;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  10. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Pricing\PriceCollection;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  14. use Shopware\Core\Framework\Uuid\Uuid;
  15. use Shopware\Core\System\Language\LanguageCollection;
  16. use Shopware\Core\System\SalesChannel\SalesChannelEntity;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;
  18. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  19. use Shopware\Core\Content\Product\ProductEvents;
  20. use Shopware\Core\Checkout\Order\OrderEvents;
  21. class Subscriber implements EventSubscriberInterface
  22. {
  23.     /**
  24.      * @var ResChannableConfigService
  25.      */
  26.     private $resChannableConfigService;
  27.     /**
  28.      * @var EntityRepository
  29.      */
  30.     private $salesChannelRepository;
  31.     /**
  32.      * @var EntityRepository
  33.      */
  34.     private $productRepository;
  35.     /**
  36.      * @var Connection
  37.      */
  38.     private $connection;
  39.     /**
  40.      * Subscriber constructor.
  41.      *
  42.      * @param ResChannableConfigService $resChannableConfigService
  43.      * @param EntityRepository $salesChannelRepository
  44.      * @param EntityRepository $productRepository
  45.      * @param Connection $connection
  46.      */
  47.     public function __construct(
  48.         ResChannableConfigService $resChannableConfigService,
  49.         # No type declaration for reasons of compatibility
  50.         $salesChannelRepository,
  51.         EntityRepository $productRepository,
  52.         Connection $connection
  53.     ) {
  54.         $this->resChannableConfigService = $resChannableConfigService;
  55.         $this->salesChannelRepository = $salesChannelRepository;
  56.         $this->productRepository = $productRepository;
  57.         $this->connection = $connection;
  58.     }
  59.     /**
  60.      * @return array
  61.      */
  62.     public static function getSubscribedEvents(): array
  63.     {
  64.         return [
  65.             ProductEvents::PRODUCT_WRITTEN_EVENT => 'onProductsWritten',
  66.             OrderEvents::ORDER_LINE_ITEM_WRITTEN_EVENT => 'onOrderLineItemWritten',
  67.             PreWriteValidationEvent::class => [['triggerChangeSet',100]],
  68.         ];
  69.     }
  70.     /**
  71.      * @param PreWriteValidationEvent $event
  72.      * @throws \Doctrine\DBAL\DBALException
  73.      * @throws \Doctrine\DBAL\Exception
  74.      */
  75.     public function triggerChangeSet(PreWriteValidationEvent $event): void
  76.     {
  77.         $writeCommands = $event->getCommands();
  78.         foreach ($writeCommands as $command) {
  79.             /** @var UpdateCommand $command */
  80.             if ($command instanceof UpdateCommand && $command->getDefinition()->getEntityName() === ProductDefinition::ENTITY_NAME) {
  81.                 /** @var string $productId */
  82.                 $productId = $command->getPrimaryKey()['id'];
  83.                 if ( $productId ) {
  84.                     $payload = $command->getPayload();
  85.                     # Continue if stock not changed
  86.                     if ( !isset($payload['stock']) )
  87.                         continue;
  88.                     $stockData = $this->connection->fetchAssociative(
  89.                         'SELECT stock, available_stock FROM `product` WHERE `id` = :id',
  90.                         ['id' => $productId]
  91.                     );
  92.                     $newAvailableStock = $stockData['available_stock']+($payload['stock']-$stockData['stock']);
  93.                     $context = Context::createDefaultContext();
  94.                     $criteria = new Criteria();
  95.                     $criteria->addAssociation('languages');
  96.                     $salesChannels = $this->salesChannelRepository->search($criteria, $context);
  97.                     # Walk through channels
  98.                     /** @var SalesChannelEntity $salesChannel */
  99.                     foreach ($salesChannels as $salesChannel) {
  100.                         /** @var LanguageCollection $languages */
  101.                         $languages = $salesChannel->getLanguages();
  102.                         if ( $languages instanceof LanguageCollection ) {
  103.                             foreach ($languages as $language) {
  104.                                 $langId = $language->getId();
  105.                                 $salesChannableId = $salesChannel->getId();
  106.                                 $webhookUrl = $this->resChannableConfigService->get('ResChannableConnector.settings.webhookUrl', $salesChannableId, $langId);
  107.                                 $webhookEnabled = $this->resChannableConfigService->get('ResChannableConnector.settings.webhookEnabled', $salesChannableId, $langId);
  108.                                 if (!$webhookUrl)
  109.                                     continue;
  110.                                 if (!$webhookEnabled)
  111.                                     continue;
  112.                                 $data = [
  113.                                     'id' => UUID::fromBytesToHex($productId),
  114.                                     'shop' => $salesChannableId,
  115.                                     'lang' => $langId,
  116.                                     'stock' => $newAvailableStock,
  117.                                 ];
  118.                                 $this->_postData($data, $webhookUrl);
  119.                             }
  120.                         }
  121.                     }
  122.                 }
  123.             }
  124.         }
  125.     }
  126.     /**
  127.      * @param EntityWrittenEvent $event
  128.      */
  129.     public function onProductsWritten(EntityWrittenEvent $event)
  130.     {
  131.         $context = Context::createDefaultContext();
  132.         $criteria = new Criteria();
  133.         $criteria->addAssociation('languages');
  134.         $salesChannels = $this->salesChannelRepository->search($criteria, $context);
  135.         if ( $salesChannels->count() === 0 )
  136.             return 0;
  137.         foreach ($event->getWriteResults() as $writeResult) {
  138.             if ($writeResult->getOperation() === EntityWriteResult::OPERATION_UPDATE) {
  139.                 $payLoad = $writeResult->getPayload();
  140.                 if ( empty($payLoad) )
  141.                     continue;
  142.                 # Walk through channels
  143.                 /** @var SalesChannelEntity $salesChannel */
  144.                 foreach ($salesChannels as $salesChannel) {
  145.                     /** @var LanguageCollection $languages */
  146.                     $languages = $salesChannel->getLanguages();
  147.                     foreach ($languages as $language) {
  148.                         $langId = $language->getId();
  149.                         $salesChannableId = $salesChannel->getId();
  150.                         $webhookUrl = $this->resChannableConfigService->get('ResChannableConnector.settings.webhookUrl', $salesChannableId, $langId);
  151.                         $webhookEnabled = $this->resChannableConfigService->get('ResChannableConnector.settings.webhookEnabled', $salesChannableId, $langId);
  152.                         if (!$webhookUrl)
  153.                             continue;
  154.                         if (!$webhookEnabled)
  155.                             continue;
  156.                         $data = $this->_convertProductWriteResult($payLoad, $salesChannel, $langId);
  157.                         if ( empty($data) || count($data) < 4 )
  158.                             continue;
  159.                         $this->_postData($data, $webhookUrl);
  160.                     }
  161.                 }
  162.             }
  163.         }
  164.     }
  165.     /**
  166.      * @param EntityWrittenEvent $event
  167.      */
  168.     public function onOrderLineItemWritten(EntityWrittenEvent $event)
  169.     {
  170.         $context = Context::createDefaultContext();
  171.         $criteria = new Criteria();
  172.         $criteria->addAssociation('languages');
  173.         $salesChannels = $this->salesChannelRepository->search($criteria, $context);
  174.         foreach ($event->getWriteResults() as $writeResult) {
  175.             if ($writeResult->getOperation() === EntityWriteResult::OPERATION_INSERT) {
  176.                 $payLoad = $writeResult->getPayload();
  177.                 if ( empty($payLoad) )
  178.                     continue;
  179.                 # Walk through channels
  180.                 /** @var SalesChannelEntity $salesChannel */
  181.                 foreach ($salesChannels as $salesChannel) {
  182.                     /** @var LanguageCollection $languages */
  183.                     $languages = $salesChannel->getLanguages();
  184.                     foreach ($languages as $language) {
  185.                         $langId = $language->getId();
  186.                         $salesChannableId = $salesChannel->getId();
  187.                         $webhookUrl = $this->resChannableConfigService->get('ResChannableConnector.settings.webhookUrl', $salesChannableId, $langId);
  188.                         $webhookEnabled = $this->resChannableConfigService->get('ResChannableConnector.settings.webhookEnabled', $salesChannableId, $langId);
  189.                         if (!$webhookUrl)
  190.                             continue;
  191.                         if (!$webhookEnabled)
  192.                             continue;
  193.                         $data = $this->_convertOrderLineItemWriteResult($payLoad, $salesChannel, $langId, $context);
  194.                         if (empty($data) || count($data) < 4)
  195.                             continue;
  196.                         $this->_postData($data, $webhookUrl);
  197.                     }
  198.                 }
  199.             }
  200.         }
  201.     }
  202.     /**
  203.      * @param $payLoad
  204.      * @param SalesChannelEntity $salesChannel
  205.      * @param $langId
  206.      * @param Context $context
  207.      * @return array
  208.      */
  209.     private function _convertOrderLineItemWriteResult($payLoad, $salesChannel, $langId, $context)
  210.     {
  211.         if ( !isset($payLoad['productId']) )
  212.             return;
  213.         $result = [
  214.             'id' => $payLoad['productId'],
  215.             'shop' => $salesChannel->getId(),
  216.             'lang' => $langId
  217.         ];
  218.         $product = $this->_getProduct($payLoad['productId'], $context);
  219.         if ( !$product )
  220.             return;
  221.         if ( isset($payLoad['quantity']) )
  222.             $result['stock'] = $product->getAvailableStock();
  223.         return $result;
  224.     }
  225.     /**
  226.      * @param array $payLoad
  227.      * @param SalesChannelEntity $salesChannel
  228.      * @param $langId
  229.      * @return array
  230.      */
  231.     private function _convertProductWriteResult($payLoad, $salesChannel, $langId)
  232.     {
  233.         if ( !isset($payLoad['id']) )
  234.             return;
  235.         $result = [
  236.             'id' => $payLoad['id'],
  237.             'shop' => $salesChannel->getId(),
  238.             'lang' => $langId
  239.         ];
  240.         if ( isset($payLoad['productNumber']) )
  241.             $result['number'] = $payLoad['productNumber'];
  242.         if ( isset($payLoad['ean']) )
  243.             $result['ean'] = $payLoad['ean'];
  244.         if ( isset($payLoad['price']) ) {
  245.             /** @var PriceCollection $prices */
  246.             $prices = $payLoad['price'];
  247.             if ( $prices instanceof PriceCollection ) {
  248.                 /** @var \Shopware\Core\Framework\DataAbstractionLayer\Pricing\Price $price */
  249.                 $price = $prices->getCurrencyPrice($salesChannel->getCurrencyId());
  250.                 $result['price'] = $price->getGross();
  251.             }
  252.         }
  253.         return $result;
  254.     }
  255.     /**
  256.      * @param $id
  257.      * @param $context
  258.      * @return ProductEntity|void
  259.      */
  260.     private function _getProduct($id, $context)
  261.     {
  262.         $productCriteria = new Criteria([$id]);
  263.         $products = $this->productRepository->search($productCriteria, $context);
  264.         if ( !$products->count() )
  265.             return;
  266.         return $products->first();
  267.     }
  268.     /**
  269.      * Post data to Channable webhook url
  270.      *
  271.      * @param array $data
  272.      * @param string $url
  273.      */
  274.     private function _postData($data, $url)
  275.     {
  276.         # Check webhook url
  277.         if ( !$url )
  278.             return;
  279.         # JSON encoding
  280.         $data = json_encode($data);
  281.         $ch = curl_init($url);
  282.         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
  283.         curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  284.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  285.         curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  286.                 'Content-Type: application/json',
  287.                 'Content-Length: ' . strlen($data))
  288.         );
  289.         curl_setopt($ch, CURLOPT_TIMEOUT, 5);
  290.         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
  291.         curl_exec($ch);
  292.     }
  293. }