<?php declare(strict_types=1);
namespace Acris\Filter;
use Acris\Filter\Storefront\Subscriber\PropertyGroupSubscriber;
use Doctrine\DBAL\Connection;
use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\ActivateContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
use Shopware\Core\Framework\Plugin\Context\UpdateContext;
use Shopware\Core\System\CustomField\CustomFieldTypes;
use Shopware\Core\System\Snippet\SnippetEntity;
class AcrisFilterCS extends Plugin
{
const DEFAULT_VALUE_OFFSET = 0;
const DEFAULT_CRITERIA_LIMIT = 50;
const CUSTOM_FIELD_SET_NAME_PRODUCT_FILTER = 'acris_filter';
const CUSTOM_FIELD_SET_NAME_PROPERTY_FILTER = 'acris_filter_property';
public function update(UpdateContext $updateContext): void
{
if ((version_compare($updateContext->getCurrentPluginVersion(), '1.1.0', '<') && version_compare($updateContext->getUpdatePluginVersion(), '1.1.0', '>='))) {
$this->removeCustomField('acris_filter_inactive', $updateContext->getContext());
}
if ((version_compare($updateContext->getCurrentPluginVersion(), '2.2.0', '<') && version_compare($updateContext->getUpdatePluginVersion(), '2.2.0', '>='))) {
if ($updateContext->getPlugin()->isActive()) {
$this->insertDefaultValues($updateContext->getContext());
}
}
}
public function postUpdate(UpdateContext $updateContext): void
{
if (version_compare($updateContext->getCurrentPluginVersion(), '3.3.0', '<') && version_compare($updateContext->getUpdatePluginVersion(), '1.4.1', '>=') && $updateContext->getPlugin()->isActive() === true) {
$this->updateProperties($updateContext->getContext());
}
}
public function activate(ActivateContext $activateContext): void
{
$this->insertDefaultValues($activateContext->getContext());
}
public function uninstall(UninstallContext $context): void
{
if ($context->keepUserData()) {
return;
}
$this->cleanupDatabase();
$this->removeCustomFields($context->getContext(), [self::CUSTOM_FIELD_SET_NAME_PRODUCT_FILTER]);
}
public function install(InstallContext $context): void
{
$this->addCustomFields($context->getContext());
}
private function addCustomFields(Context $context): void
{
/* Check for snippets if they exist for custom fields */
$this->checkForExistingCustomFieldSnippets(['acris_filter_type', 'acris_filter_unit', 'acris_filter_hide'], $context);
$customFieldSet = $this->container->get('custom_field_set.repository');
if ($customFieldSet->search((new Criteria())->addFilter(new EqualsFilter('name', self::CUSTOM_FIELD_SET_NAME_PRODUCT_FILTER)), $context)->count() == 0) {
$customFieldSet->create([[
'name' => self::CUSTOM_FIELD_SET_NAME_PRODUCT_FILTER,
'config' => [
'label' => [
'en-GB' => 'Product Filter',
'de-DE' => 'Produkt Filter'
]
],
'customFields' => [
['name' => 'acris_filter_type', 'type' => CustomFieldTypes::TEXT,
'config' => [
'componentName' => 'sw-field',
'type' => 'text',
'customFieldType' => 'text',
'customFieldPosition' => 2,
'label' => [
'en-GB' => 'Filter display type',
'de-DE' => 'Filter Darstellungsart'
]
]],
['name' => 'acris_filter_unit', 'type' => CustomFieldTypes::TEXT,
'config' => [
'componentName' => 'sw-field',
'type' => 'text',
'customFieldType' => 'text',
'customFieldPosition' => 3,
'label' => [
'en-GB' => 'Unit',
'de-DE' => 'Einheit',
]
]],
['name' => 'acris_filter_hide', 'type' => CustomFieldTypes::BOOL,
'config' => [
'componentName' => 'sw-field',
'type' => 'checkbox',
'customFieldType' => 'checkbox',
'customFieldPosition' => 4,
'label' => [
'en-GB' => 'Show filter only in dropdown',
'de-DE' => 'Filter nur in Dropdown anzeigen'
]
]],
],
]], $context);
}
if ($customFieldSet->search((new Criteria())->addFilter(new EqualsFilter('name', self::CUSTOM_FIELD_SET_NAME_PROPERTY_FILTER)), $context)->count() == 0) {
$customFieldSet->create([[
'name' => self::CUSTOM_FIELD_SET_NAME_PROPERTY_FILTER,
'config' => [
'label' => [
'en-GB' => 'Property Filter',
'de-DE' => 'Eigenschafts Filter'
]
],
'customFields' => [
['name' => 'acris_filter_numeric', 'type' => CustomFieldTypes::FLOAT,
'config' => [
'componentName' => 'sw-field',
'type' => 'number',
'numberType' => 'float',
'customFieldType' => 'number',
'customFieldPosition' => 1,
'label' => [
'en-GB' => 'Numerical value',
'de-DE' => 'Numerischer Wert'
]
]]
],
]], $context);
}
}
private function removeCustomFields(Context $context, array $setNames): void
{
/* Check for snippets if they exist for custom fields */
$this->checkForExistingCustomFieldSnippets(['acris_filter_type', 'acris_filter_unit', 'acris_filter_hide'], $context);
$customFieldSet = $this->container->get('custom_field_set.repository');
foreach ($setNames as $setName) {
$id = $customFieldSet->searchIds((new Criteria())->addFilter(new EqualsFilter('name', $setName)), $context)->firstId();
if ($id) $customFieldSet->delete([['id' => $id]], $context);
}
}
private function cleanupDatabase(): void
{
$connection = $this->container->get(Connection::class);
$connection->executeUpdate('DROP TABLE IF EXISTS acris_filter');
}
public function insertDefaultValues(Context $context)
{
$filterRpository = $this->container->get('acris_filter.repository');
$filters =
[[
'identifier' => 'manufacturer',
'active' => true,
'position' => 1,
'filter_type' => 'default',
'hide' => false
],
[
'identifier' => 'properties',
'active' => true,
'position' => 2,
'filter_type' => 'default',
'hide' => false
],
[
'identifier' => 'price',
'active' => true,
'position' => 3,
'filter_type' => 'range_min_max',
'hide' => false
],
[
'identifier' => 'rating',
'active' => true,
'position' => 4,
'filter_type' => 'default',
'hide' => false
],
[
'identifier' => 'shipping-free',
'active' => true,
'position' => 5,
'filter_type' => 'default',
'hide' => false
], [
'identifier' => 'availability',
'active' => true,
'position' => 6,
'filter_type' => 'default',
'hide' => false
], [
'identifier' => 'categories',
'active' => true,
'position' => 0,
'filter_type' => 'default',
'hide' => false
]];
foreach ($filters as $filter) {
$this->createIfNotExists($filterRpository, [['name' => 'identifier', 'value' => $filter['identifier']]], $filter, $context);
}
}
private function createIfNotExists(EntityRepositoryInterface $repository, array $equalFields, array $data, Context $context)
{
$filters = [];
foreach ($equalFields as $equalField) {
$filters[] = new EqualsFilter($equalField['name'], $equalField['value']);
}
if (sizeof($filters) > 1) {
$filter = new MultiFilter(MultiFilter::CONNECTION_OR, $filters);
} else {
$filter = array_shift($filters);
}
$searchResult = $repository->search((new Criteria())->addFilter($filter), $context);
if ($searchResult->count() == 0) {
$repository->create([$data], $context);
}
}
private function checkForExistingCustomFieldSnippets(array $customFields, Context $context)
{
/** @var EntityRepositoryInterface $snippetRepository */
$snippetRepository = $this->container->get('snippet.repository');
$criteria = new Criteria();
$filter = [];
foreach ($customFields as $customField) {
$filter[] = new EqualsFilter('translationKey', 'customFields.' . $customField);
}
$criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, $filter));
/** @var EntitySearchResult $searchResult */
$searchResult = $snippetRepository->search($criteria, $context);
if ($searchResult->count() > 0) {
$snippetIds = [];
/** @var SnippetEntity $snippet */
foreach ($searchResult->getEntities()->getElements() as $snippet) {
$snippetIds[] = [
'id' => $snippet->getId()
];
}
if (!empty($snippetIds)) {
$snippetRepository->delete($snippetIds, $context);
}
}
}
private function removeCustomField(string $customFieldName, Context $context)
{
$this->checkForExistingCustomFieldSnippets([$customFieldName], $context);
$customFieldRepository = $this->container->get('custom_field.repository');
$id = $customFieldRepository->searchIds((new Criteria())->addFilter(new EqualsFilter('name', $customFieldName)), $context)->firstId();
if ($id) {
$customFieldRepository->delete([['id' => $id]], $context);
}
}
private function updateProperties(Context $context): void
{
$propertyGroupSubscriber = $this->container->get(PropertyGroupSubscriber::class);
if (empty($propertyGroupSubscriber)) return;
/** @var EntityRepositoryInterface $propertyOptionRepository */
$propertyOptionRepository = $this->container->get('property_group_option.repository');
$criteria = (new Criteria())
->addFilter(
new MultiFilter(MultiFilter::CONNECTION_OR, [
new ContainsFilter('name', ','),
new ContainsFilter('name', '.')
])
)->addAssociation('group');
$offset = self::DEFAULT_VALUE_OFFSET;
$load = true;
$updateData = [];
while ($load) {
$criteria->setOffset($offset);
$searchResult = $propertyOptionRepository->search($criteria, $context);
if ($searchResult->getTotal() > 0) {
/** @var PropertyGroupOptionEntity $propertyOption */
foreach ($searchResult->getEntities()->getElements() as $propertyOption) {
$updateData = array_merge($updateData, $propertyGroupSubscriber->updatePropertyGroupAndOption($propertyOption->getGroup(), $propertyOption, $context));
}
}
$offset += self::DEFAULT_CRITERIA_LIMIT;
if ($searchResult->getTotal() < self::DEFAULT_CRITERIA_LIMIT) $load = false;
}
if (!empty($updateData)) {
$propertyOptionRepository->update($updateData, $context);
}
}
}