<?php
namespace Maxia\MaxiaListingVariants6\Service;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Struct\Struct;
use Maxia\MaxiaListingVariants6\Config\BaseConfig;
use Maxia\MaxiaListingVariants6\Config\ProductConfig;
use Maxia\MaxiaListingVariants6\Config\PropertyGroupConfig;
use Maxia\MaxiaListingVariants6\MaxiaListingVariants6;
use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Property\PropertyGroupEntity;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\HttpFoundation\RequestStack;
class ConfigService
{
/** @var string */
private $namespace;
/** @var SystemConfigService */
private $systemConfig;
/** @var ProductConfig */
private $baseConfig;
/** @var RequestStack */
private $requestStack;
/** @var EntityRepository */
private $categoryRepository;
public function __construct(
string $namespace,
SystemConfigService $systemConfig,
RequestStack $requestStack,
/*EntityRepository*/ $categoryRepository
) {
$this->namespace = $namespace;
$this->systemConfig = $systemConfig;
$this->requestStack = $requestStack;
$this->categoryRepository = $categoryRepository;
}
/**
* @return SystemConfigService
*/
public function getSystemConfig(): SystemConfigService
{
return $this->systemConfig;
}
/**
* Returns the plugin configuration as array, with defaults.
*
* @param SalesChannelContext|int $context
* @return BaseConfig
*/
public function getBaseConfig($context): BaseConfig
{
if ($this->baseConfig === null) {
$config = $this->systemConfig->get(
$this->namespace,
$context instanceof SalesChannelContext
? $context->getSalesChannel()->getId()
: $context
);
$struct = new BaseConfig();
$struct->assign($config);
$struct->setDisplayParentSupported(
(new \ReflectionClass(ProductEntity::class))->hasMethod('getVariantListingConfig')
);
$this->baseConfig = $struct;
}
return $this->baseConfig;
}
/**
* Returns the extended property group config.
*
* @param PropertyGroupEntity $groupEntity
* @param SalesChannelContext $context
* @return PropertyGroupConfig
*/
public function getPropertyGroupConfig(PropertyGroupEntity $groupEntity, SalesChannelContext $context) : PropertyGroupConfig
{
$groupConfig = PropertyGroupConfig::createFromCustomFields(
$groupEntity->getTranslation('customFields'),
MaxiaListingVariants6::CUSTOM_FIELD_PREFIX.'_property'
);
if (!$groupConfig->getMaxEntries()) {
$groupConfig->setMaxEntries($this->getBaseConfig($context)->getMaxEntries() ?? 4);
}
$groupConfig->setTotalEntries($groupEntity->getOptions() ? $groupEntity->getOptions()->count() : null);
return $groupConfig;
}
/**
* Returns the product variant config for a cms slot.
*
* @param CmsSlotEntity $slot
* @param SalesChannelContext $context
* @return ProductConfig
*/
protected function getCmsSlotConfig(CmsSlotEntity $slot, SalesChannelContext $context) : ProductConfig
{
$productConfig = ProductConfig::createFromCustomFields(
$slot->getCustomFields() ?: [],
MaxiaListingVariants6::CUSTOM_FIELD_PREFIX.'_cms_block'
);
$category = $this->getCurrentCategory($context);
if ($category && $category->getSlotConfig() && isset($category->getSlotConfig()[$slot->getId()])) {
$slotConfig = $category->getSlotConfig()[$slot->getId()];
if (isset($slotConfig['maxiaListingVariants'])) {
$value = isset($slotConfig['maxiaListingVariants']['value'])
? $slotConfig['maxiaListingVariants']['value']
: $slotConfig['maxiaListingVariants'];
$categoryConfig = ProductConfig::createFromCustomFields(
$value,
MaxiaListingVariants6::CUSTOM_FIELD_PREFIX.'_cms_block'
);
$productConfig = $this->mergeStruct(ProductConfig::class, $productConfig, $categoryConfig);
}
}
return $this->mergeStruct(ProductConfig::class, $this->getBaseConfig($context), $productConfig);
}
/**
* Returns the product variant config for a product.
*
* @param ProductEntity $product
* @param CmsSlotEntity|null $slot
* @param SalesChannelContext $context
* @return ProductConfig
*/
public function getProductConfig(ProductEntity $product, CmsSlotEntity $slot = null, SalesChannelContext $context) : ProductConfig
{
$baseConfig = $slot !== null
? $this->getCmsSlotConfig($slot, $context)
: $this->getBaseConfig($context);
$productConfig = ProductConfig::createFromCustomFields(
$product->getCustomFields() ?: [],
MaxiaListingVariants6::CUSTOM_FIELD_PREFIX.'_product'
);
$newConfig = $this->mergeStruct(ProductConfig::class, $baseConfig, $productConfig);
if ($baseConfig->getDisplayMode() === 'none') {
$newConfig->setDisplayMode('none');
}
return $newConfig;
}
/**
* @param $type
* @param Struct $a
* @param Struct $b
* @return mixed
*/
public function mergeStruct($type, Struct $a, Struct $b)
{
/** @var Struct $result */
$result = new $type;
if (!$result instanceof Struct) {
throw new \InvalidArgumentException("a struct is required");
}
$fillKeys = array_keys($result->getVars());
$result->assign(array_filter($a->getVars(), function ($key) use ($fillKeys) {
return in_array($key, $fillKeys, true);
}, ARRAY_FILTER_USE_KEY));
foreach ($b->getVars() as $key => $value) {
if ($value !== null && $value !== 'inherited' && in_array($key, $fillKeys, true)) {
$result->assign([$key => $value]);
}
}
return $result;
}
/**
* Check if the given property group should be displayed by config.
*
* @param ProductConfig $config
* @param PropertyGroupEntity $group
* @param int $index
* @return bool
*/
public function checkDisplayMode(ProductConfig $config, PropertyGroupEntity $group, int $index) : bool
{
if ($config->getDisplayMode() === 'all') {
return true;
}
if ($config->getDisplayMode() === 'none') {
return false;
}
if ($config->getDisplayMode() === 'selected') {
if (!$config->getRestrictedProperties()) {
return true;
}
return in_array($group->getId(), $config->getRestrictedProperties(), false);
}
// property_1, property_2, property_1_2, ...
if (!preg_match('/^index_(\d)_?(\d)?$/', $config->getDisplayMode(), $matches)) {
return false;
}
$allowedIndexes = [(int)$matches[1]];
if (isset($matches[2])) {
$allowedIndexes[] = (int)$matches[2];
}
if (in_array($index + 1, $allowedIndexes)) {
return true;
}
return false;
}
/**
* @param SalesChannelContext $context
* @return CategoryEntity|null
*/
protected function getCurrentCategory(SalesChannelContext $context)
{
static $category = false;
if ($category === false) {
$request = $this->requestStack->getMainRequest();
if ($request->query->get('navigationId')) {
$navigationId = $request->query->get('navigationId', $context->getSalesChannel()->getNavigationCategoryId());
} else {
$navigationId = $request->attributes->get('navigationId', $context->getSalesChannel()->getNavigationCategoryId());
}
if ($navigationId) {
$result = $this->categoryRepository->search(new Criteria([$navigationId]), $context->getContext());
if ($result->first()) {
$category = $result->first();
} else {
$category = null;
}
}
}
return $category;
}
}