custom/plugins/ShopsyKlaviyo6/src/ShopsyKlaviyo6.php line 21

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopsy\ShopsyKlaviyo;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Framework\Api\Util\AccessKeyHelper;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  8. use Shopware\Core\Framework\Plugin;
  9. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  10. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  11. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  12. use Shopware\Core\Framework\Uuid\Uuid;
  13. use Shopware\Core\System\CustomField\CustomFieldTypes;
  14. if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
  15.     require_once dirname(__DIR__) . '/vendor/autoload.php';
  16. }
  17. class ShopsyKlaviyo6 extends Plugin
  18. {
  19.     public function install(InstallContext $installContext): void {
  20.         /** @var EntityRepositoryInterface $customFieldSetRepository */
  21.         $customFieldSetRepository $this->container->get('custom_field_set.repository');
  22.         $customFields = [
  23.             'name' => 'shopsy_klaviyo_order_custom_fields',
  24.             'config' => [
  25.                 'label' => [
  26.                     'de-DE' => 'SHOPSY Klaviyo',
  27.                     'en-GB' => 'SHOPSY Klaviyo'
  28.                 ]
  29.             ],
  30.             'customFields' => [
  31.                 [
  32.                     'name' => 'shopsy_klaviyo_tracked_fulfilled_order',
  33.                     'type' => CustomFieldTypes::BOOL,
  34.                     'config' => [
  35.                         'label' => [
  36.                             'de-DE' => '"Tracked fulfilled order" an Klaviyo übertragen',
  37.                             'en-GB' => '"Tracked fulfilled order" transferred to Klaviyo'
  38.                         ]
  39.                     ]
  40.                 ],
  41.                 [
  42.                     'name' => 'shopsy_klaviyo_tracked_purchased_items',
  43.                     'type' => CustomFieldTypes::BOOL,
  44.                     'config' => [
  45.                         'label' => [
  46.                             'de-DE' => '"Tracked purchased items" an Klaviyo übertragen',
  47.                             'en-GB' => '"Tracked purchased items" transferred to Klaviyo'
  48.                         ]
  49.                     ]
  50.                 ],
  51.                 [
  52.                     'name' => 'shopsy_klaviyo_tracked_cancelled_order',
  53.                     'type' => CustomFieldTypes::BOOL,
  54.                     'config' => [
  55.                         'label' => [
  56.                             'de-DE' => '"Tracked cancelled order" an Klaviyo übertragen',
  57.                             'en-GB' => '"Tracked cancelled order" transferred to Klaviyo'
  58.                         ]
  59.                     ]
  60.                 ],
  61.                 [
  62.                     'name' => 'shopsy_klaviyo_tracked_refunded_order',
  63.                     'type' => CustomFieldTypes::BOOL,
  64.                     'config' => [
  65.                         'label' => [
  66.                             'de-DE' => '"Tracked refunded order" an Klaviyo übertragen',
  67.                             'en-GB' => '"Tracked refunded order" transferred to Klaviyo'
  68.                         ]
  69.                     ]
  70.                 ],
  71.             ],
  72.             'relations' => [
  73.                 ['entityName' => 'order']
  74.             ]
  75.         ];
  76.         try {
  77.             $customFieldSetRepository->upsert([$customFields], $installContext->getContext());
  78.         }
  79.         catch (\Exception $ex) {
  80.         }
  81.         $productStreamId $this->addProductStream();
  82.         $salesChannelId $this->addProductExportSalesChannel();
  83.         $this->addProductExport($productStreamId$salesChannelId);
  84.         parent::install($installContext);
  85.     }
  86.     public function uninstall(UninstallContext $uninstallContext): void {
  87.         if (!$uninstallContext->keepUserData()) {
  88.             /** @var EntityRepositoryInterface $customFieldSetRepository */
  89.             $customFieldSetRepository $this->container->get('custom_field_set.repository');
  90.             $criteria = new Criteria();
  91.             $criteria->addFilter(new EqualsFilter('name''shopsy_klaviyo_order_custom_fields'));
  92.             $result $customFieldSetRepository->searchIds($criteria$uninstallContext->getContext());
  93.             if ($result->getTotal() > && !$uninstallContext->keepUserData()) {
  94.                 $data $result->getDataOfId($result->firstId());
  95.                 $customFieldSetRepository->delete([$data], $uninstallContext->getContext());
  96.             }
  97.             $this->removeProductStream();
  98.             $this->removeProductExportSalesChannel();
  99.             $this->removeProductExport();
  100.         }
  101.         parent::uninstall($uninstallContext);
  102.     }
  103.     public function update(UpdateContext $updateContext): void {
  104.         if (\version_compare($updateContext->getCurrentPluginVersion(), '1.0.1''<')) {
  105.             $productStreamId $this->addProductStream();
  106.             $salesChannelId $this->addProductExportSalesChannel();
  107.             $this->addProductExport($productStreamId$salesChannelId);
  108.         }
  109.     }
  110.     private function addProductStream() {
  111.         /** @var Connection $connection */
  112.         $connection $this->container->get(Connection::class);
  113.         $sql '
  114.             INSERT IGNORE INTO `product_stream`
  115.                 (`id`, `api_filter`, `invalid`, `created_at`)
  116.             VALUES
  117.                 (:id, :apiFilter, 0, NOW(3))';
  118.         $id Uuid::randomBytes();
  119.         $connection->executeStatement($sql, [
  120.             'id' => $id,
  121.             'apiFilter' => '[{"type": "multi", "queries": [{"type": "multi", "queries": [{"type": "equals", "field": "product.active", "value": "1"}], "operator": "AND"}], "operator": "OR"}]'
  122.         ]);
  123.         $sql2 "SELECT id from language where `name` = 'English'";
  124.         $languageId $connection->fetchOne($sql2);
  125.         $sql3 '
  126.             INSERT IGNORE INTO `product_stream_translation`
  127.                 (`product_stream_id`, `language_id`, `name`, `description`, `created_at`)
  128.             VALUES
  129.                 (:id, :languageId, :name, :description, NOW(3))';
  130.         $connection->executeStatement($sql3, [
  131.             'id' => $id,
  132.             'languageId' => $languageId,
  133.             'name' => 'Klaviyo',
  134.             'description' => 'Klaviyo Catalog Feed'
  135.         ]);
  136.         return $id;
  137.     }
  138.     private function addProductExportSalesChannel() {
  139.         /** @var Connection $connection */
  140.         $connection $this->container->get(Connection::class);
  141.         $id Uuid::randomBytes();
  142.         $sql "SELECT id from language where `name` = 'English'";
  143.         $languageId $connection->fetchOne($sql);
  144.         $sql "SELECT sales_channel_type_id from sales_channel_type_translation where `name` = 'Product comparison'";
  145.         $typeId $connection->fetchOne($sql);
  146.         $sql "SELECT id from currency where `iso_code` = 'EUR'";
  147.         $currencyId $connection->fetchOne($sql);
  148.         $sql "SELECT id from payment_method";
  149.         $paymentMethodId $connection->fetchOne($sql);
  150.         $sql "SELECT id from shipping_method";
  151.         $shippingMethodId $connection->fetchOne($sql);
  152.         $sql "SELECT id from country where `iso` = 'GB'";
  153.         $countryId $connection->fetchOne($sql);
  154.         $sql "SELECT id, version_id from category WHERE `parent_id` IS NULL && `path` IS NULL && `after_category_id` IS NULL";
  155.         $navigationCategoryId $connection->fetchOne($sql);
  156.         $sql "SELECT version_id from category WHERE `parent_id` IS NULL && `path` IS NULL && `after_category_id` IS NULL";
  157.         $navigationCategoryVersionId $connection->fetchOne($sql);
  158.         $sql "SELECT id from customer_group";
  159.         $customerGroupId $connection->fetchOne($sql);
  160.         $accessKey AccessKeyHelper::generateAccessKey('integration');
  161.         $sql '
  162.             INSERT IGNORE INTO `sales_channel`
  163.                 (`id`, `type_id`, `access_key`, `language_id`, `currency_id`, `payment_method_id`, `shipping_method_id`, `country_id`,
  164.                 `navigation_category_id`, `navigation_category_version_id`, `navigation_category_depth`, `customer_group_id`, `created_at`)
  165.             VALUES
  166.                 (:id, :typeId, :accessKey, :languageId, :currencyId, :paymentMethodId, :shippingMethodId, :countryId,
  167.                  :navigationCategoryId, :navigationCategoryVersionId, 2, :customerGroupId, NOW(3))';
  168.         $connection->executeStatement($sql, [
  169.             'id' => $id,
  170.             'typeId' => $typeId,
  171.             'accessKey' => $accessKey,
  172.             'languageId' => $languageId,
  173.             'currencyId' => $currencyId,
  174.             'paymentMethodId' => $paymentMethodId,
  175.             'shippingMethodId' => $shippingMethodId,
  176.             'countryId' => $countryId,
  177.             'navigationCategoryId' => $navigationCategoryId,
  178.             'navigationCategoryVersionId' => $navigationCategoryVersionId,
  179.             'customerGroupId' => $customerGroupId
  180.         ]);
  181.         $sql '
  182.             INSERT IGNORE INTO `sales_channel_translation`
  183.                 (`sales_channel_id`, `language_id`, `name`, `created_at`)
  184.             VALUES
  185.                 (:id, :languageId, :name, NOW(3))';
  186.         $connection->executeStatement($sql, [
  187.             'id' => $id,
  188.             'languageId' => $languageId,
  189.             'name' => 'Klaviyo Catalog Feed'
  190.         ]);
  191.         return $id;
  192.     }
  193.     private function addProductExport($productStreamId$salesChannelId) {
  194.         /** @var Connection $connection */
  195.         $connection $this->container->get(Connection::class);
  196.         $sql "SELECT id from currency where `iso_code` = 'EUR'";
  197.         $currencyId $connection->fetchOne($sql);
  198.         $sql '
  199.             INSERT IGNORE INTO `product_export`
  200.                 (`id`, `product_stream_id`, `sales_channel_id`, `file_name`, `access_key`, `encoding`, `file_format`, `generate_by_cronjob`,
  201.                  `interval`, `header_template`, `body_template`, `footer_template`, `created_at`, `currency_id`)
  202.             VALUES
  203.                 (:id, :productStreamId, :salesChannelId, :fileName, 0, :encoding, :fileFormat, 1, 86400, :headerTemplate,
  204.                  :bodyTemplate, :footerTemplate, NOW(3), :currencyId)';
  205.         $id Uuid::randomBytes();
  206.         $connection->executeStatement($sql, [
  207.             'id' => $id,
  208.             'productStreamId' => $productStreamId,
  209.             'salesChannelId' => $salesChannelId,
  210.             'fileName' => 'klaviyo.xml',
  211.             'encoding' => 'UTF-8',
  212.             'fileFormat' => 'xml',
  213.             'headerTemplate' => $this->getHeaderTemplate(),
  214.             'bodyTemplate' => $this->getBodyTemplate(),
  215.             'footerTemplate' => $this->getFooterTemplate(),
  216.             'currencyId' => $currencyId
  217.         ]);
  218.     }
  219.     private function getHeaderTemplate() {
  220.         return '<?xml version="1.0"?>
  221. <Products>';
  222.     }
  223.     private function getBodyTemplate() {
  224.         return "{% if product != null %}
  225.     <Product>
  226.         <id>{{ product.id }}</id>
  227.         {% if product != null %}
  228.         <sku>{{ product.productNumber }}</sku>
  229.         {% else %}
  230.         <sku>not set</sku>
  231.         {% endif %}
  232.         {% if product.translated.name != null %}
  233.         <title>{{ product.translated.name|escape }}</title>
  234.         {% endif %}
  235.         <link>{{ seoUrl('frontend.detail.page', {'productId': product.id}) }}</link>
  236.         {% if product.translated.description != null %}
  237.         <description>{{ product.translated.description|escape }}</description>
  238.         {% else %}
  239.         <description>-</description>
  240.         {% endif %}
  241.         <price>{{ product.calculatedPrice.unitPrice|number_format(context.currency.decimalPrecision, '.', '') }}</price>
  242.         <image_link>
  243.         {% if product.cover != null %}
  244.         {{ product.cover.media.url }}
  245.         {% endif %}
  246.         </image_link>
  247.         {% if product.categories.first != null %}
  248.         <categories>{{ product.categories.first.name }}</categories>
  249.         {% else %}
  250.         <categories>no-category</categories>
  251.         {% endif %}
  252.         <inventory_quantity>{{ product.availableStock }}</inventory_quantity>
  253.     </Product>
  254. {% endif %}";
  255.     }
  256.     private function getFooterTemplate() {
  257.         return '</Products>';
  258.     }
  259.     private function removeProductStream() {
  260.         /** @var Connection $connection */
  261.         $connection $this->container->get(Connection::class);
  262.         $sql "SELECT product_stream_id from product_stream_translation where `name` = 'Klaviyo'";
  263.         $productStreamId $connection->fetchOne($sql);
  264.         $connection->executeStatement("DELETE FROM product_stream_translation WHERE product_stream_id = ?", [$productStreamId]);
  265.         $connection->executeStatement("DELETE FROM product_stream WHERE id = ?", [$productStreamId]);
  266.     }
  267.     private function removeProductExportSalesChannel() {
  268.         /** @var Connection $connection */
  269.         $connection $this->container->get(Connection::class);
  270.         $sql "SELECT sales_channel_id from sales_channel_translation where `name` = 'Klaviyo Catalog Feed'";
  271.         $salesChannelId $connection->fetchOne($sql);
  272.         $connection->executeStatement("DELETE FROM sales_channel_translation WHERE sales_channel_id = ?", [$salesChannelId]);
  273.         $connection->executeStatement("DELETE FROM sales_channel WHERE id = ?", [$salesChannelId]);
  274.     }
  275.     private function removeProductExport() {
  276.         /** @var Connection $connection */
  277.         $connection $this->container->get(Connection::class);
  278.         $connection->executeStatement("DELETE FROM product_export WHERE file_name = 'klaviyo.xml'");
  279.     }
  280. }