<?php declare(strict_types=1);
namespace Shopsy\ShopsyKlaviyo;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Api\Util\AccessKeyHelper;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
use Shopware\Core\Framework\Plugin\Context\UpdateContext;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\CustomField\CustomFieldTypes;
if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
require_once dirname(__DIR__) . '/vendor/autoload.php';
}
class ShopsyKlaviyo6 extends Plugin
{
public function install(InstallContext $installContext): void {
/** @var EntityRepositoryInterface $customFieldSetRepository */
$customFieldSetRepository = $this->container->get('custom_field_set.repository');
$customFields = [
'name' => 'shopsy_klaviyo_order_custom_fields',
'config' => [
'label' => [
'de-DE' => 'SHOPSY Klaviyo',
'en-GB' => 'SHOPSY Klaviyo'
]
],
'customFields' => [
[
'name' => 'shopsy_klaviyo_tracked_fulfilled_order',
'type' => CustomFieldTypes::BOOL,
'config' => [
'label' => [
'de-DE' => '"Tracked fulfilled order" an Klaviyo übertragen',
'en-GB' => '"Tracked fulfilled order" transferred to Klaviyo'
]
]
],
[
'name' => 'shopsy_klaviyo_tracked_purchased_items',
'type' => CustomFieldTypes::BOOL,
'config' => [
'label' => [
'de-DE' => '"Tracked purchased items" an Klaviyo übertragen',
'en-GB' => '"Tracked purchased items" transferred to Klaviyo'
]
]
],
[
'name' => 'shopsy_klaviyo_tracked_cancelled_order',
'type' => CustomFieldTypes::BOOL,
'config' => [
'label' => [
'de-DE' => '"Tracked cancelled order" an Klaviyo übertragen',
'en-GB' => '"Tracked cancelled order" transferred to Klaviyo'
]
]
],
[
'name' => 'shopsy_klaviyo_tracked_refunded_order',
'type' => CustomFieldTypes::BOOL,
'config' => [
'label' => [
'de-DE' => '"Tracked refunded order" an Klaviyo übertragen',
'en-GB' => '"Tracked refunded order" transferred to Klaviyo'
]
]
],
],
'relations' => [
['entityName' => 'order']
]
];
try {
$customFieldSetRepository->upsert([$customFields], $installContext->getContext());
}
catch (\Exception $ex) {
}
$productStreamId = $this->addProductStream();
$salesChannelId = $this->addProductExportSalesChannel();
$this->addProductExport($productStreamId, $salesChannelId);
parent::install($installContext);
}
public function uninstall(UninstallContext $uninstallContext): void {
if (!$uninstallContext->keepUserData()) {
/** @var EntityRepositoryInterface $customFieldSetRepository */
$customFieldSetRepository = $this->container->get('custom_field_set.repository');
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', 'shopsy_klaviyo_order_custom_fields'));
$result = $customFieldSetRepository->searchIds($criteria, $uninstallContext->getContext());
if ($result->getTotal() > 0 && !$uninstallContext->keepUserData()) {
$data = $result->getDataOfId($result->firstId());
$customFieldSetRepository->delete([$data], $uninstallContext->getContext());
}
$this->removeProductStream();
$this->removeProductExportSalesChannel();
$this->removeProductExport();
}
parent::uninstall($uninstallContext);
}
public function update(UpdateContext $updateContext): void {
if (\version_compare($updateContext->getCurrentPluginVersion(), '1.0.1', '<')) {
$productStreamId = $this->addProductStream();
$salesChannelId = $this->addProductExportSalesChannel();
$this->addProductExport($productStreamId, $salesChannelId);
}
}
private function addProductStream() {
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$sql = '
INSERT IGNORE INTO `product_stream`
(`id`, `api_filter`, `invalid`, `created_at`)
VALUES
(:id, :apiFilter, 0, NOW(3))';
$id = Uuid::randomBytes();
$connection->executeStatement($sql, [
'id' => $id,
'apiFilter' => '[{"type": "multi", "queries": [{"type": "multi", "queries": [{"type": "equals", "field": "product.active", "value": "1"}], "operator": "AND"}], "operator": "OR"}]'
]);
$sql2 = "SELECT id from language where `name` = 'English'";
$languageId = $connection->fetchOne($sql2);
$sql3 = '
INSERT IGNORE INTO `product_stream_translation`
(`product_stream_id`, `language_id`, `name`, `description`, `created_at`)
VALUES
(:id, :languageId, :name, :description, NOW(3))';
$connection->executeStatement($sql3, [
'id' => $id,
'languageId' => $languageId,
'name' => 'Klaviyo',
'description' => 'Klaviyo Catalog Feed'
]);
return $id;
}
private function addProductExportSalesChannel() {
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$id = Uuid::randomBytes();
$sql = "SELECT id from language where `name` = 'English'";
$languageId = $connection->fetchOne($sql);
$sql = "SELECT sales_channel_type_id from sales_channel_type_translation where `name` = 'Product comparison'";
$typeId = $connection->fetchOne($sql);
$sql = "SELECT id from currency where `iso_code` = 'EUR'";
$currencyId = $connection->fetchOne($sql);
$sql = "SELECT id from payment_method";
$paymentMethodId = $connection->fetchOne($sql);
$sql = "SELECT id from shipping_method";
$shippingMethodId = $connection->fetchOne($sql);
$sql = "SELECT id from country where `iso` = 'GB'";
$countryId = $connection->fetchOne($sql);
$sql = "SELECT id, version_id from category WHERE `parent_id` IS NULL && `path` IS NULL && `after_category_id` IS NULL";
$navigationCategoryId = $connection->fetchOne($sql);
$sql = "SELECT version_id from category WHERE `parent_id` IS NULL && `path` IS NULL && `after_category_id` IS NULL";
$navigationCategoryVersionId = $connection->fetchOne($sql);
$sql = "SELECT id from customer_group";
$customerGroupId = $connection->fetchOne($sql);
$accessKey = AccessKeyHelper::generateAccessKey('integration');
$sql = '
INSERT IGNORE INTO `sales_channel`
(`id`, `type_id`, `access_key`, `language_id`, `currency_id`, `payment_method_id`, `shipping_method_id`, `country_id`,
`navigation_category_id`, `navigation_category_version_id`, `navigation_category_depth`, `customer_group_id`, `created_at`)
VALUES
(:id, :typeId, :accessKey, :languageId, :currencyId, :paymentMethodId, :shippingMethodId, :countryId,
:navigationCategoryId, :navigationCategoryVersionId, 2, :customerGroupId, NOW(3))';
$connection->executeStatement($sql, [
'id' => $id,
'typeId' => $typeId,
'accessKey' => $accessKey,
'languageId' => $languageId,
'currencyId' => $currencyId,
'paymentMethodId' => $paymentMethodId,
'shippingMethodId' => $shippingMethodId,
'countryId' => $countryId,
'navigationCategoryId' => $navigationCategoryId,
'navigationCategoryVersionId' => $navigationCategoryVersionId,
'customerGroupId' => $customerGroupId
]);
$sql = '
INSERT IGNORE INTO `sales_channel_translation`
(`sales_channel_id`, `language_id`, `name`, `created_at`)
VALUES
(:id, :languageId, :name, NOW(3))';
$connection->executeStatement($sql, [
'id' => $id,
'languageId' => $languageId,
'name' => 'Klaviyo Catalog Feed'
]);
return $id;
}
private function addProductExport($productStreamId, $salesChannelId) {
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$sql = "SELECT id from currency where `iso_code` = 'EUR'";
$currencyId = $connection->fetchOne($sql);
$sql = '
INSERT IGNORE INTO `product_export`
(`id`, `product_stream_id`, `sales_channel_id`, `file_name`, `access_key`, `encoding`, `file_format`, `generate_by_cronjob`,
`interval`, `header_template`, `body_template`, `footer_template`, `created_at`, `currency_id`)
VALUES
(:id, :productStreamId, :salesChannelId, :fileName, 0, :encoding, :fileFormat, 1, 86400, :headerTemplate,
:bodyTemplate, :footerTemplate, NOW(3), :currencyId)';
$id = Uuid::randomBytes();
$connection->executeStatement($sql, [
'id' => $id,
'productStreamId' => $productStreamId,
'salesChannelId' => $salesChannelId,
'fileName' => 'klaviyo.xml',
'encoding' => 'UTF-8',
'fileFormat' => 'xml',
'headerTemplate' => $this->getHeaderTemplate(),
'bodyTemplate' => $this->getBodyTemplate(),
'footerTemplate' => $this->getFooterTemplate(),
'currencyId' => $currencyId
]);
}
private function getHeaderTemplate() {
return '<?xml version="1.0"?>
<Products>';
}
private function getBodyTemplate() {
return "{% if product != null %}
<Product>
<id>{{ product.id }}</id>
{% if product != null %}
<sku>{{ product.productNumber }}</sku>
{% else %}
<sku>not set</sku>
{% endif %}
{% if product.translated.name != null %}
<title>{{ product.translated.name|escape }}</title>
{% endif %}
<link>{{ seoUrl('frontend.detail.page', {'productId': product.id}) }}</link>
{% if product.translated.description != null %}
<description>{{ product.translated.description|escape }}</description>
{% else %}
<description>-</description>
{% endif %}
<price>{{ product.calculatedPrice.unitPrice|number_format(context.currency.decimalPrecision, '.', '') }}</price>
<image_link>
{% if product.cover != null %}
{{ product.cover.media.url }}
{% endif %}
</image_link>
{% if product.categories.first != null %}
<categories>{{ product.categories.first.name }}</categories>
{% else %}
<categories>no-category</categories>
{% endif %}
<inventory_quantity>{{ product.availableStock }}</inventory_quantity>
</Product>
{% endif %}";
}
private function getFooterTemplate() {
return '</Products>';
}
private function removeProductStream() {
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$sql = "SELECT product_stream_id from product_stream_translation where `name` = 'Klaviyo'";
$productStreamId = $connection->fetchOne($sql);
$connection->executeStatement("DELETE FROM product_stream_translation WHERE product_stream_id = ?", [$productStreamId]);
$connection->executeStatement("DELETE FROM product_stream WHERE id = ?", [$productStreamId]);
}
private function removeProductExportSalesChannel() {
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$sql = "SELECT sales_channel_id from sales_channel_translation where `name` = 'Klaviyo Catalog Feed'";
$salesChannelId = $connection->fetchOne($sql);
$connection->executeStatement("DELETE FROM sales_channel_translation WHERE sales_channel_id = ?", [$salesChannelId]);
$connection->executeStatement("DELETE FROM sales_channel WHERE id = ?", [$salesChannelId]);
}
private function removeProductExport() {
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$connection->executeStatement("DELETE FROM product_export WHERE file_name = 'klaviyo.xml'");
}
}