custom/plugins/ResChannableConnector/src/Subscriber/Subscriber.php line 216

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