Unverified Commit 8eace600 authored by Pablo Borowicz's avatar Pablo Borowicz Committed by GitHub
Browse files

Merge pull request #25875 from matthieu-rolland/apply-fix-config-values-multistore

parents 5e53f4f1 1bcdeccc
......@@ -884,7 +884,7 @@ class ShopCore extends ObjectModel
* @param int $shop_id Shop ID
* @param bool $as_id
*
* @return int|array Group ID
* @return int|array|bool Group ID
*/
public static function getGroupFromShop($shop_id, $as_id = true)
{
......
......@@ -31,6 +31,7 @@ use Configuration as ConfigurationLegacy;
use Feature;
use Language;
use PrestaShop\PrestaShop\Core\Domain\Configuration\ShopConfigurationInterface;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopException;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShopBundle\Exception\NotImplementedException;
use Shop;
......@@ -94,19 +95,23 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
*
* @param string $key
* @param mixed $default The default value if the parameter key does not exist
* @param ShopConstraint|null $shopConstraint
* @param ShopConstraint|null $shopConstraint This parameter should always be given, if not, a ShopConstraint will
* be built based on the current shop context
*
* @return mixed
*/
public function get($key, $default = null, ShopConstraint $shopConstraint = null)
{
if (null === $shopConstraint) {
$shopConstraint = $this->buildShopConstraintFromContext();
}
if (defined($key)) {
return constant($key);
}
$shopId = $this->getShopId($shopConstraint);
$shopGroupId = $this->getShopGroupId($shopConstraint);
$isStrict = $this->isStrict($shopConstraint);
//If configuration has never been accessed it is still empty and hasKey/isLangKey will always return false
if (!ConfigurationLegacy::configurationIsLoaded()) {
......@@ -118,19 +123,20 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
return $this->getLocalized($key, $shopId, $shopGroupId);
}
// Since hasKey doesn't check manage the fallback shop > shop group > global, we handle it manually
$hasKey = ConfigurationLegacy::hasKey($key, null, null, $shopId);
if ($hasKey || $isStrict) {
return $hasKey ? ConfigurationLegacy::get($key, null, null, $shopId) : null;
if ($shopConstraint->isStrict()) {
return $this->getStrictValue($key, $shopConstraint);
}
$hasKey = ConfigurationLegacy::hasKey($key, null, $shopGroupId);
if ($hasKey) {
// Since hasKey doesn't manage the fallback shop > shop group > global, we handle it manually
if (null !== $shopId && ConfigurationLegacy::hasKey($key, null, null, $shopId)) {
return ConfigurationLegacy::get($key, null, null, $shopId);
}
if (null !== $shopGroupId && ConfigurationLegacy::hasKey($key, null, $shopGroupId)) {
return ConfigurationLegacy::get($key, null, $shopGroupId);
}
$hasKey = ConfigurationLegacy::hasKey($key);
if ($hasKey) {
if (ConfigurationLegacy::hasKey($key)) {
return ConfigurationLegacy::get($key);
}
......@@ -142,7 +148,8 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
*
* @param string $key
* @param mixed $value
* @param ShopConstraint|null $shopConstraint
* @param ShopConstraint|null $shopConstraint If this parameter is not given, a ShopConstraint will
* be built based on the current shop context, except if $this->shop is set
* @param array $options Options @deprecated Will be removed in next major
*
* @return $this
......@@ -151,14 +158,11 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
*/
public function set($key, $value, ShopConstraint $shopConstraint = null, array $options = [])
{
// By default, set a piece of configuration for all available shops and shop groups
$shopGroupId = null;
$shopId = null;
if ($this->shop instanceof Shop && null === $shopConstraint) {
$shopGroupId = $this->shop->id_shop_group;
$shopId = $this->shop->id;
} else {
$shopConstraint = $shopConstraint ?: $this->buildShopConstraintFromContext();
$shopId = $this->getShopId($shopConstraint);
$shopGroupId = $this->getShopGroupId($shopConstraint);
}
......@@ -169,8 +173,8 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
$key,
$value,
$html,
$shopGroupId,
$shopId
$shopGroupId ?: 0,
$shopId ?: 0
);
if (!$success) {
......@@ -182,27 +186,31 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
/**
* @param string $key
* @param ShopConstraint|null $shopConstraint
* @param ShopConstraint|null $shopConstraint This parameter should always be given, if not, a ShopConstraint will
* be built based on the current shop context
*
* @return bool
*/
public function has($key, ShopConstraint $shopConstraint = null)
{
if (null === $shopConstraint) {
$shopConstraint = $this->buildShopConstraintFromContext();
}
$shopId = $this->getShopId($shopConstraint);
$shopGroupId = $this->getShopGroupId($shopConstraint);
$isStrict = $this->isStrict($shopConstraint);
if (ConfigurationLegacy::isLangKey($key)) {
return $this->hasMultilang($key, $shopId, $shopGroupId, $isStrict);
return $this->hasMultilang($key, $shopId, $shopGroupId, $shopConstraint->isStrict());
}
$hasKey = ConfigurationLegacy::hasKey($key, null, $shopGroupId, $shopId);
if ($hasKey || $isStrict) {
if (null !== $shopId && ($hasKey || $shopConstraint->isStrict())) {
return $hasKey;
}
$hasKey = ConfigurationLegacy::hasKey($key, null, $shopGroupId);
if ($hasKey) {
if (null !== $shopGroupId && ($shopConstraint->isStrict() || $hasKey)) {
return $hasKey;
}
......@@ -345,39 +353,43 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
}
/**
* @param ShopConstraint|null $shopConstraint
* @param ShopConstraint $shopConstraint
*
* @return int|null
*/
private function getShopId(?ShopConstraint $shopConstraint): ?int
private function getShopId(ShopConstraint $shopConstraint): ?int
{
return null !== $shopConstraint && null !== $shopConstraint->getShopId()
return null !== $shopConstraint->getShopId()
? $shopConstraint->getShopId()->getValue()
: null
;
}
/**
* @param ShopConstraint|null $shopConstraint
* @param ShopConstraint $shopConstraint
*
* @return int|null
*/
private function getShopGroupId(?ShopConstraint $shopConstraint): ?int
private function getShopGroupId(ShopConstraint $shopConstraint): ?int
{
return null !== $shopConstraint && null !== $shopConstraint->getShopGroupId()
? $shopConstraint->getShopGroupId()->getValue()
: null
;
}
if (null !== $shopConstraint->getShopGroupId()) {
return $shopConstraint->getShopGroupId()->getValue();
} elseif (null !== $shopConstraint->getShopId()) {
$shopGroupId = Shop::getGroupFromShop((int) $shopConstraint->getShopId()->getValue(), true);
// $shopGroupId can not be false, it would mean that the shop group was not found for the given shop
if ($shopGroupId === false) {
throw new ShopException(
sprintf(
'Shop group was not found for the shop with id %d.',
$shopConstraint->getShopId()->getValue()
)
);
}
/**
* @param ShopConstraint|null $shopConstraint
*
* @return bool
*/
private function isStrict(?ShopConstraint $shopConstraint): bool
{
return null !== $shopConstraint ? $shopConstraint->isStrict() : false;
return (int) $shopGroupId;
}
return null;
}
/**
......@@ -395,4 +407,50 @@ class Configuration extends ParameterBag implements ShopConfigurationInterface
!empty($shopId) ? $shopId->getValue() : null
);
}
/**
* @return ShopConstraint
*/
private function buildShopConstraintFromContext(): ShopConstraint
{
@trigger_error(
'Not specifying the optional ShopConstraint parameter is deprecated since version 1.7.8.0',
E_USER_DEPRECATED
);
if (Shop::getContext() === Shop::CONTEXT_SHOP) {
return ShopConstraint::shop(Shop::getContextShopID());
} elseif (Shop::getContext() === Shop::CONTEXT_GROUP) {
return ShopConstraint::shopGroup(Shop::getContextShopGroupID());
}
return ShopConstraint::allShops();
}
/**
* @param string $key
* @param ShopConstraint $shopConstraint
*
* @return mixed
*/
private function getStrictValue(string $key, ShopConstraint $shopConstraint)
{
if (null !== $shopConstraint->getShopId()) {
$hasKey = ConfigurationLegacy::hasKey($key, null, null, $shopConstraint->getShopId()->getValue());
return $hasKey ? ConfigurationLegacy::get($key, null, null, $shopConstraint->getShopId()->getValue()) : null;
}
if (null !== $shopConstraint->getShopGroupId()) {
$hasKey = ConfigurationLegacy::hasKey($key, null, $shopConstraint->getShopGroupId()->getValue());
return $hasKey ? ConfigurationLegacy::get($key, null, $shopConstraint->getShopGroupId()->getValue()) : null;
}
if (ConfigurationLegacy::hasKey($key)) {
return ConfigurationLegacy::get($key);
}
return null;
}
}
......@@ -631,10 +631,7 @@ class OrderAmountUpdater
{
$constraintKey = $order->id_shop . '-' . $order->id_shop_group;
if (!isset($this->orderConstraints[$constraintKey])) {
$this->orderConstraints[$constraintKey] = new ShopConstraint(
(int) $order->id_shop,
(int) $order->id_shop_group
);
$this->orderConstraints[$constraintKey] = ShopConstraint::shop((int) $order->id_shop);
}
return $this->orderConstraints[$constraintKey];
......
......@@ -224,10 +224,7 @@ class OrderDetailUpdater
*/
private function prepareOrderContext(Order $order): array
{
$shopConstraint = new ShopConstraint(
(int) $order->id_shop,
(int) $order->id_shop_group
);
$shopConstraint = ShopConstraint::shop((int) $order->id_shop);
$roundType = (int) $this->shopConfiguration->get('PS_ROUND_TYPE', null, $shopConstraint);
$taxAddressType = $this->shopConfiguration->get('PS_TAX_ADDRESS_TYPE', null, $shopConstraint);
$taxAddress = new Address($order->{$taxAddressType});
......
......@@ -184,7 +184,7 @@ final class GetOrderForViewingHandler extends AbstractOrderHandler implements Ge
$invoiceManagementIsEnabled = (bool) $this->configuration->get(
'PS_INVOICE',
null,
new ShopConstraint((int) $order->id_shop, (int) $order->id_shop_group)
ShopConstraint::shop((int) $order->id_shop)
);
$orderInvoiceAddress = $this->getOrderInvoiceAddress($order);
......@@ -434,7 +434,7 @@ final class GetOrderForViewingHandler extends AbstractOrderHandler implements Ge
$conf = $this->configuration->get(
'PS_DELIVERY_PREFIX',
null,
new ShopConstraint($order->id_shop, $order->id_shop_group)
ShopConstraint::shop((int) $order->id_shop)
);
$number = sprintf(
'%s%06d',
......
......@@ -27,6 +27,7 @@
namespace PrestaShop\PrestaShop\Adapter\Shop;
use Context as LegacyContext;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Multistore\MultistoreContextCheckerInterface;
use PrestaShop\PrestaShop\Core\Shop\ShopContextInterface;
use Shop;
......@@ -228,4 +229,20 @@ class Context implements MultistoreContextCheckerInterface, ShopContextInterface
{
return LegacyContext::getContext()->shop->name;
}
/**
* @param bool $strict
*
* @return ShopConstraint
*/
public function getShopConstraint(bool $strict = false): ShopConstraint
{
if ($this->isShopContext()) {
return ShopConstraint::shop((int) $this->getContextShopID(), $strict);
} elseif ($this->isGroupShopContext()) {
return ShopConstraint::shopGroup((int) $this->getContextShopGroup()->id, $strict);
}
return ShopConstraint::allShops();
}
}
......@@ -74,14 +74,7 @@ abstract class AbstractMultistoreConfiguration implements DataConfigurationInter
return null;
}
$contextShopGroup = $this->shopContext->getContextShopGroup();
$contextShopId = $this->shopContext->getContextShopID();
$contextShopId = (int) $contextShopId > 0 ? $contextShopId : null;
return new ShopConstraint(
$contextShopId,
$contextShopGroup->id
);
return $this->shopContext->getShopConstraint();
}
/**
......
......@@ -35,56 +35,60 @@ class ShopConstraint
/**
* @var ShopId|null
*/
private $shopId;
protected $shopId;
/**
* @var ShopGroupId|null
*/
private $shopGroupId;
protected $shopGroupId;
/**
* Indicate if the value returned matches the constraints strictly, else it fallbacks to Shop > Group > Global value
*
* @var bool
*/
private $strict;
protected $strict;
/**
* Constraint to get configuration for a specific shop
*
* @param int $shopId
* @param bool $isStrict
*
* @return static
*
* @throws ShopException
*/
public static function shop(int $shopId): self
public static function shop(int $shopId, bool $isStrict = false): self
{
return new static($shopId, null, false);
return new static($shopId, null, $isStrict);
}
/**
* Constraint to get configuration for a specific shop group
*
* @param int $shopGroupId
* @param bool $isStrict
*
* @return static
*
* @throws ShopException
*/
public static function shopGroup(int $shopGroupId): self
public static function shopGroup(int $shopGroupId, bool $isStrict = false): self
{
return new static(null, $shopGroupId, false);
return new static(null, $shopGroupId, $isStrict);
}
/**
* Constraint to get configuration for all shops (the global value)
*
* @param bool $isStrict
*
* @return static
*/
public static function allShops(): self
public static function allShops(bool $isStrict = false): self
{
return new static(null, null, false);
return new static(null, null, $isStrict);
}
/**
......@@ -94,7 +98,7 @@ class ShopConstraint
*
* @throws ShopException
*/
public function __construct(?int $shopId, ?int $shopGroupId, bool $strict = false)
protected function __construct(?int $shopId, ?int $shopGroupId, bool $strict = false)
{
$this->shopId = null !== $shopId ? new ShopId($shopId) : null;
$this->shopGroupId = null !== $shopGroupId ? new ShopGroupId($shopGroupId) : null;
......
......@@ -67,7 +67,7 @@ class ShopGroupId
if (0 >= $shopGroupId) {
throw new ShopException(
sprintf(
'Shop id %s is invalid. Shop id must be number that is greater than zero.',
'Shop group id %s is invalid. Shop group id must be a number that is greater than zero.',
var_export($shopGroupId, true)
)
);
......
......@@ -30,7 +30,6 @@ namespace PrestaShopBundle\Service\Form;
use PrestaShop\PrestaShop\Adapter\Shop\Context;
use PrestaShop\PrestaShop\Core\Domain\Configuration\ShopConfigurationInterface;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Feature\FeatureInterface;
use PrestaShopBundle\Controller\Admin\MultistoreController;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
......@@ -143,13 +142,8 @@ class MultistoreCheckboxEnabler
private function isOverriddenInCurrentContext(string $configurationKey): bool
{
// Check if current configuration is overridden by current shop / group shop context
$shopConstraint = new ShopConstraint(
$this->multiStoreContext->getContextShopId(),
$this->multiStoreContext->getContextShopGroup()->id,
true // important: will return a value only if it's present, skipping the hierarchical fallback system
);
return $this->configuration->has($configurationKey, $shopConstraint);
// The $isStrict parameter is important: it will return a value only if it's present, skipping the hierarchical fallback system
return $this->configuration->has($configurationKey, $this->multiStoreContext->getShopConstraint(true));
}
/**
......
......@@ -65,11 +65,8 @@ class CustomizedConfigurationChecker
// we don't check group shop customization if we are already in group shop context
if (!$isGroupShopContext) {
// check if given configuration is overridden for the parent group shop
$shopGroupConstraint = new ShopConstraint(
null,
$shop->getShopGroup()->getId(),
true // it must be strict, otherwise the method will also check for configuration settings in "all shop" context
);
// isStrict must be true, otherwise the method will also check for configuration settings in "all shop" context
$shopGroupConstraint = ShopConstraint::shopGroup($shop->getShopGroup()->getId(), true);
if ($this->configuration->has($configurationKey, $shopGroupConstraint)) {
return true;
......@@ -77,11 +74,7 @@ class CustomizedConfigurationChecker
}
// check if given configuration is overridden for the shop
$shopConstraint = new ShopConstraint(
$shop->getId(),
$shop->getShopGroup()->getId(),
true
);
$shopConstraint = ShopConstraint::shop($shop->getId(), true);
return $this->configuration->has($configurationKey, $shopConstraint);
}
......
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
declare(strict_types=1);
namespace Tests\Integration\Adapter;
use Doctrine\ORM\EntityManager;
use PrestaShop\PrestaShop\Adapter\Configuration;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShopBundle\Entity\Shop;
use PrestaShopBundle\Entity\ShopGroup;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class ConfigurationTest extends KernelTestCase
{
/**
* @var Configuration|null
*/
private $configuration;
/**
* @var EntityManager
*/
private $entityManager;
protected function setUp(): void
{
self::bootKernel();
$container = self::$kernel->getContainer();
$this->entityManager = $container->get('doctrine.orm.entity_manager');
$this->configuration = $container->get('prestashop.adapter.legacy.configuration');
$this->initMultistore();
}
/**
* @param array $setParams
* @param array $getParams
* @param string $expectedResult
*
* @dataProvider getProvider
*/
public function testGet(array $setParams, array $getParams, string $expectedResult): void
{
$this->setAndGetValuesForTesting($setParams, $getParams, $expectedResult);
}
/**
* @param array $setParams
* @param array $getParams
* @param string|null $expectedResult
*
* @dataProvider getWithStrictParameterProvider
*/
public function testGetWithSrictParameter(array $setParams, array $getParams, ?string $expectedResult): void
{
$this->setAndGetValuesForTesting($setParams, $getParams, $expectedResult);
}
/**
* @param array $setParams
* @param array $getParams
* @param bool $expectedResult
* @dataProvider hasProvider
*/
public function testHas(array $setParams, array $getParams, bool $expectedResult): void
{
if (!empty($setParams)) {
$this->configuration->set($setParams['key'], $setParams['value'], $setParams['shopConstraint']);
}
$result = $this->configuration->has($getParams['key'], $getParams['shopConstraint']);
$this->assertEquals($expectedResult, $result);
}
/**
* @return iterable
*/
public function hasProvider(): iterable
{
// simple test when value doesn't exist and we ask for it in all shop context
yield [
[],
[
'key' => 'does_not_exist',
'shopConstraint' => ShopConstraint::allShops(),
],
false,
];
// simple test when value doesn't exist and we ask for it in group context
yield [
[],
[