mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
Merge branch '1.12' into 1.13
* 1.12: Refactor to provide a product translation instead of generate product shop url Provide post-CR fixes Allow passing a desired locale code to DefaultChannelFactory Remove unknown localeCode named paramter Add support for secure and unsecured URLs for generating product show page Refactor logic behind a button redirecting to the product's shop page Adding try logic to get the shop's slug Adding default if product has no slug [Bunnyshell] Adjust the workflows for Fork PRs [Maintenance] Improve ProportionalIntegerDistributor to keep original index
This commit is contained in:
commit
7035db1945
21 changed files with 500 additions and 39 deletions
|
|
@ -10,7 +10,7 @@ In order to check a product in shop in all channels it is available in
|
|||
And the store has a product "Bugatti" available in "United States" channel
|
||||
And I am logged in as an administrator
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Accessing product show page in shop from the product variant edit page where product is available in more than one channel
|
||||
Given this product is also available in the "Europe" channel
|
||||
And this product has "Red" variant priced at "$220000.00" in "Europe" channel
|
||||
|
|
@ -18,14 +18,14 @@ In order to check a product in shop in all channels it is available in
|
|||
And I choose to show this product in the "Europe" channel
|
||||
Then I should see this product in the "Europe" channel in the shop
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Accessing product show page in shop from the product variant edit page where product is available in one channel
|
||||
Given this product has "Red" variant priced at "$220000.00" in "United States" channel
|
||||
When I want to modify the "Bugatti" product variant
|
||||
And I choose to show this product in this channel
|
||||
Then I should see this product in the "United States" channel in the shop
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Being unable to access product show page in shop when the product is disabled
|
||||
Given this product has been disabled
|
||||
When I want to modify the "Bugatti" product variant
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
@managing_products
|
||||
Feature: Accessing a store's product page
|
||||
In order to access a store's product page from the admin panel
|
||||
As an Administrator
|
||||
I want to have a way to be redirected to the store's product page from the admin panel
|
||||
|
||||
Background:
|
||||
Given the store operates on a single channel in "German (Germany)" locale
|
||||
And the store has a product "Französischer Bulldoggen T-Shirt"
|
||||
And I am logged in as an administrator
|
||||
And I am using "Polish (Poland)" locale for my panel
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Opening a product's page from the admin panel when the product has a translation with a defined slug in the administrator's chosen language
|
||||
Given the locale "Polish (Poland)" is enabled
|
||||
And this product is named "Bulldog francuski T-Shirt" in the "Polish (Poland)" locale
|
||||
When I want to edit this product
|
||||
Then the show product's page button should be enabled
|
||||
And it should be leading to the product's page in the "Polish (Poland)" locale
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Opening a product's page from the admin panel when the product has a translation with a defined slug in the default channel's language
|
||||
Given the locale "French (France)" is enabled
|
||||
And this product is named "Tee-shirt bouledogue français" in the "French (France)" locale
|
||||
When I want to edit this product
|
||||
Then the show product's page button should be enabled
|
||||
And it should be leading to the product's page in the "German (Germany)" locale
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Opening a product's page from the admin panel with using first available locale with slug and enabled in the channel
|
||||
Given the locale "French (France)" is enabled
|
||||
And the store also operates in "French (France)" locale
|
||||
And this product has no slug in the "German (Germany)" locale
|
||||
And this product is named "T-shirt bouledogue français" in the "French (France)" locale
|
||||
When I want to edit this product
|
||||
Then it should be leading to the product's page in the "French (France)" locale
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Not being able to open a product's page from the admin panel when the product has no translations meeting the criteria
|
||||
Given this product has no translations with a defined slug
|
||||
When I want to edit this product
|
||||
Then the show product's page button should be disabled
|
||||
|
|
@ -11,20 +11,20 @@ Feature: Checking products in the shop while viewing them
|
|||
And I am logged in as an administrator
|
||||
And I am browsing products
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Accessing product show page in shop from the product show page where product is available in more than one channel
|
||||
Given this product is available in the "Europe" channel
|
||||
When I access "Bugatti" product page
|
||||
And I show this product in the "Europe" channel
|
||||
Then I should see this product in the "Europe" channel in the shop
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Accessing product show page in shop from the product show page where product is available in one channel
|
||||
When I access "Bugatti" product page
|
||||
And I show this product in this channel
|
||||
Then I should see this product in the "Europe" channel in the shop
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Being unable to access product show page in shop when the product is disabled
|
||||
Given this product has been disabled
|
||||
When I access "Bugatti" product page
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ use Sylius\Component\Core\Model\ChannelInterface;
|
|||
use Sylius\Component\Core\Model\ShopBillingData;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Sylius\Component\Core\Test\Services\DefaultChannelFactoryInterface;
|
||||
use Sylius\Component\Locale\Model\LocaleInterface;
|
||||
use Sylius\Component\Resource\Repository\RepositoryInterface;
|
||||
|
||||
final class ChannelContext implements Context
|
||||
{
|
||||
|
|
@ -35,6 +37,7 @@ final class ChannelContext implements Context
|
|||
private DefaultChannelFactoryInterface $defaultChannelFactory,
|
||||
private ChannelRepositoryInterface $channelRepository,
|
||||
private ObjectManager $channelManager,
|
||||
private RepositoryInterface $localeRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +101,17 @@ final class ChannelContext implements Context
|
|||
$this->sharedStorage->set('channel', $defaultData['channel']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given the store operates on a single channel in :localeCode locale
|
||||
*/
|
||||
public function storeOperatesOnASingleChannelInLocale(string $localeCode): void
|
||||
{
|
||||
$defaultData = $this->defaultChannelFactory->create(localeCode: $localeCode);
|
||||
|
||||
$this->sharedStorage->setClipboard($defaultData);
|
||||
$this->sharedStorage->set('channel', $defaultData['channel']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^the store(?:| also) operates on (?:a|another) channel named "([^"]+)"$/
|
||||
* @Given /^the store(?:| also) operates on (?:a|another) channel named "([^"]+)" in "([^"]+)" currency$/
|
||||
|
|
@ -320,6 +334,18 @@ final class ChannelContext implements Context
|
|||
$channel->getChannelPriceHistoryConfig()->setLowestPriceForDiscountedProductsVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given the store also operates in :locale locale
|
||||
*/
|
||||
public function theStoreAlsoOperatesInLocale(LocaleInterface $locale): void
|
||||
{
|
||||
/** @var ChannelInterface $channel */
|
||||
$channel = $this->sharedStorage->get('channel');
|
||||
$channel->addLocale($locale);
|
||||
|
||||
$this->channelManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $state
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,30 @@ final class ProductContext implements Context
|
|||
return $channelPricing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^(this product) has no slug in the ("[^"]+" locale)$/
|
||||
*/
|
||||
public function thisProductHasNoSlugInTheLocale(ProductInterface $product, string $localeCode): void
|
||||
{
|
||||
$productTranslation = $product->getTranslation($localeCode);
|
||||
$productTranslation->setSlug('');
|
||||
|
||||
$this->saveProduct($product);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^(this product) has no translations with a defined slug$/
|
||||
*/
|
||||
public function thisProductHasNoTranslationsWithADefinedSlug(ProductInterface $product): void
|
||||
{
|
||||
/** @var ProductTranslationInterface $productTranslation */
|
||||
foreach ($product->getTranslations() as $productTranslation) {
|
||||
$productTranslation->setSlug('');
|
||||
}
|
||||
|
||||
$this->saveProduct($product);
|
||||
}
|
||||
|
||||
private function getPriceFromString(string $price): int
|
||||
{
|
||||
return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
|
||||
|
|
|
|||
|
|
@ -370,6 +370,7 @@ final class ManagingProductsContext implements Context
|
|||
/**
|
||||
* @When I want to modify the :product product
|
||||
* @When /^I want to modify (this product)$/
|
||||
* @When /^I want to edit (this product)$/
|
||||
* @When I modify the :product product
|
||||
*/
|
||||
public function iWantToModifyAProduct(ProductInterface $product): void
|
||||
|
|
@ -1199,6 +1200,36 @@ final class ManagingProductsContext implements Context
|
|||
Assert::eq($this->indexPage->getPageNumber(), $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the show product's page button should be enabled
|
||||
*/
|
||||
public function theShowProductsPageButtonShouldBeEnabled(): void
|
||||
{
|
||||
Assert::false($this->updateSimpleProductPage->isShowInShopButtonDisabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the show product's page button should be disabled
|
||||
*/
|
||||
public function theShowProductsPageButtonShouldBeDisabled(): void
|
||||
{
|
||||
Assert::true($this->updateSimpleProductPage->isShowInShopButtonDisabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^it should be leading to (the product)'s page in the ("[^"]+" locale)$/
|
||||
*/
|
||||
public function itShouldBeLeadingToTheProductPageInTheLocale(ProductInterface $product, string $localeCode): void
|
||||
{
|
||||
$productTranslation = $product->getTranslation($localeCode);
|
||||
$showProductPageUrl = $this->updateSimpleProductPage->getShowProductInSingleChannelUrl();
|
||||
|
||||
Assert::contains(
|
||||
$showProductPageUrl,
|
||||
sprintf('/%s/products/%s', $localeCode, $productTranslation->getSlug()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $element
|
||||
* @param string $value
|
||||
|
|
|
|||
|
|
@ -413,6 +413,11 @@ class UpdateSimpleProductPage extends BaseUpdatePage implements UpdateSimpleProd
|
|||
return null !== $this->getDocument()->find('css', '.tab > h3:contains("Inventory")');
|
||||
}
|
||||
|
||||
public function getShowProductInSingleChannelUrl(): string
|
||||
{
|
||||
return $this->getElement('show_product_single_button')->getAttribute('href');
|
||||
}
|
||||
|
||||
public function isShowInShopButtonDisabled(): bool
|
||||
{
|
||||
return $this->getElement('show_product_single_button')->hasClass('disabled');
|
||||
|
|
@ -487,7 +492,7 @@ class UpdateSimpleProductPage extends BaseUpdatePage implements UpdateSimpleProd
|
|||
'product_taxons' => '#sylius_product_productTaxons',
|
||||
'shipping_required' => '#sylius_product_variant_shippingRequired',
|
||||
'show_product_dropdown' => '.scrolling.menu',
|
||||
'show_product_single_button' => 'a:contains("Show product in shop page")',
|
||||
'show_product_single_button' => '[data-test-show-product-in-shop-page]',
|
||||
'slug' => '#sylius_product_translations_%locale%_slug',
|
||||
'tab' => '.menu [data-tab="%name%"]',
|
||||
'taxonomy' => 'a[data-tab="taxonomy"]',
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ interface UpdateSimpleProductPageInterface extends BaseUpdatePageInterface
|
|||
|
||||
public function hasInventoryTab(): bool;
|
||||
|
||||
public function getShowProductInSingleChannelUrl(): string;
|
||||
|
||||
public function isShowInShopButtonDisabled(): bool;
|
||||
|
||||
public function showProductInChannel(string $channel): void;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
<argument type="service" id="sylius.behat.factory.default_channel" />
|
||||
<argument type="service" id="sylius.repository.channel" />
|
||||
<argument type="service" id="sylius.manager.channel" />
|
||||
<argument type="service" id="sylius.repository.locale" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.behat.context.setup.currency" class="Sylius\Behat\Context\Setup\CurrencyContext">
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
{% if sylius_bundle_loaded_checker('SyliusShopBundle') %}
|
||||
{% if not product.enabled or enabledChannels|length < 1 %}
|
||||
<a class="ui labeled icon button disabled" href="#">
|
||||
<a class="ui labeled icon button disabled" href="#" {{ sylius_test_html_attribute('show-product-in-shop-page') }}>
|
||||
<i class="angle right icon"></i>
|
||||
{{ 'sylius.ui.show_product_in_shop_page'|trans }}
|
||||
</a>
|
||||
{% elseif enabledChannels|length > 1 %}
|
||||
<div class="ui floating dropdown labeled icon button">
|
||||
<div class="ui floating dropdown labeled icon button" {{ sylius_test_html_attribute('show-product-in-shop-page') }}>
|
||||
<i class="share alternate icon"></i>
|
||||
<span class="text">
|
||||
{{ 'sylius.ui.show_product_in_shop_page'|trans }}
|
||||
|
|
@ -15,8 +15,16 @@
|
|||
<div class="menu">
|
||||
<div class="scrolling menu">
|
||||
{% for channel in enabledChannels %}
|
||||
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product.slug, '_locale': channel.defaultLocale.code}), channel) %}
|
||||
<a href="{{ url|raw }}" class="item" target="_blank">
|
||||
{% set product_translation = sylius_product_translation(product, channel) %}
|
||||
{% if product_translation is not null %}
|
||||
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product_translation.slug, '_locale': product_translation.locale}), channel) %}
|
||||
{% endif %}
|
||||
|
||||
<a
|
||||
href="{{ (url is not defined) ? '#' : url|raw }}"
|
||||
class="item {% if url is not defined %}disabled{% endif %}"
|
||||
target="_blank"
|
||||
>
|
||||
<i class="angle right icon"></i>
|
||||
{{ 'sylius.ui.show_in'|trans }}
|
||||
{{ channel.name }} ({{ channel.code }})
|
||||
|
|
@ -27,8 +35,17 @@
|
|||
</div>
|
||||
{% else %}
|
||||
{% for channel in enabledChannels %}
|
||||
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product.slug, '_locale': channel.defaultLocale.code}), channel) %}
|
||||
<a class="ui labeled icon button" href="{{ url|raw }}" target="_blank">
|
||||
{% set product_translation = sylius_product_translation(product, channel) %}
|
||||
{% if product_translation is not null %}
|
||||
{% set url = sylius_channel_url(path('sylius_shop_product_show', {'slug': product_translation.slug, '_locale': product_translation.locale}), channel) %}
|
||||
{% endif %}
|
||||
|
||||
<a
|
||||
class="ui labeled icon button {% if url is not defined %}disabled{% endif %}"
|
||||
href="{{ (url is not defined) ? '#' : url|raw }}"
|
||||
target="_blank"
|
||||
{{ sylius_test_html_attribute('show-product-in-shop-page') }}
|
||||
>
|
||||
<i class="angle right icon"></i>
|
||||
{{ 'sylius.ui.show_product_in_shop_page'|trans }}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Provider;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductTranslationInterface;
|
||||
use Sylius\Component\Locale\Context\LocaleContextInterface;
|
||||
use Sylius\Component\Locale\Model\LocaleInterface;
|
||||
|
||||
final class ChannelBasedProductTranslationProvider implements ChannelBasedProductTranslationProviderInterface
|
||||
{
|
||||
public function __construct(private LocaleContextInterface $localeContext)
|
||||
{
|
||||
}
|
||||
|
||||
public function provide(ProductInterface $product, ChannelInterface $channel): ?ProductTranslationInterface
|
||||
{
|
||||
/** @var Collection<array-key, ProductTranslationInterface> $productTranslations */
|
||||
$productTranslations = $product->getTranslations();
|
||||
|
||||
$contextLocaleCode = $this->localeContext->getLocaleCode();
|
||||
$productTranslation = $this->findTranslationWithSlugForLocales($productTranslations, [$contextLocaleCode]);
|
||||
|
||||
if (null !== $productTranslation) {
|
||||
return $productTranslation;
|
||||
}
|
||||
|
||||
/** @var string $channelDefaultLocaleCode */
|
||||
$channelDefaultLocaleCode = $channel->getDefaultLocale()->getCode();
|
||||
$productTranslation = $this->findTranslationWithSlugForLocales($productTranslations, [$channelDefaultLocaleCode]);
|
||||
|
||||
if (null !== $productTranslation) {
|
||||
return $productTranslation;
|
||||
}
|
||||
|
||||
$localesEnabledInChannel = $this->getLocalesCodesEnabledInChannel($channel);
|
||||
$productTranslation = $this->findTranslationWithSlugForLocales($productTranslations, $localesEnabledInChannel);
|
||||
|
||||
return $productTranslation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<array-key, ProductTranslationInterface> $productTranslations
|
||||
* @param array<string> $localeCodes
|
||||
*/
|
||||
private function findTranslationWithSlugForLocales(Collection $productTranslations, array $localeCodes): ?ProductTranslationInterface
|
||||
{
|
||||
foreach ($productTranslations as $productTranslation) {
|
||||
$isLocaleCodeMatching = in_array($productTranslation->getLocale(), $localeCodes);
|
||||
$isSlugPresent = '' !== $productTranslation->getSlug() && null !== $productTranslation->getSlug();
|
||||
|
||||
if ($isLocaleCodeMatching && $isSlugPresent) {
|
||||
return $productTranslation;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return array<array-key, string> */
|
||||
private function getLocalesCodesEnabledInChannel(ChannelInterface $channel): array
|
||||
{
|
||||
return $channel->getLocales()->map(function (LocaleInterface $locale): string {
|
||||
return $locale->getCode();
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Provider;
|
||||
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductTranslationInterface;
|
||||
|
||||
interface ChannelBasedProductTranslationProviderInterface
|
||||
{
|
||||
public function provide(ProductInterface $product, ChannelInterface $channel): ?ProductTranslationInterface;
|
||||
}
|
||||
|
|
@ -12,12 +12,12 @@ parameters:
|
|||
framework:
|
||||
messenger:
|
||||
transports:
|
||||
main:
|
||||
main:
|
||||
dsn: '%sylius_messenger_transport_main_dsn%'
|
||||
failure_transport: 'main_failed'
|
||||
main_failed:
|
||||
main_failed:
|
||||
dsn: '%sylius_messenger_transport_main_failed_dsn%'
|
||||
catalog_promotion_removal:
|
||||
catalog_promotion_removal:
|
||||
dsn: '%sylius_messenger_transport_catalog_promotion_removal_dsn%'
|
||||
failure_transport: 'catalog_promotion_removal_failed'
|
||||
catalog_promotion_removal_failed:
|
||||
|
|
@ -42,4 +42,4 @@ framework:
|
|||
default_middleware: allow_no_handlers
|
||||
sylius_default.bus: *command_bus
|
||||
sylius_event.bus: *event_bus
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
<import resource="services/price_history.xml" />
|
||||
<import resource="services/product_variant_map.xml" />
|
||||
<import resource="services/promotion.xml" />
|
||||
<import resource="services/providers.xml" />
|
||||
<import resource="services/shipping.xml" />
|
||||
<import resource="services/state_resolvers.xml" />
|
||||
<import resource="services/taxation.xml" />
|
||||
|
|
@ -52,12 +53,6 @@
|
|||
<services>
|
||||
<defaults public="true" />
|
||||
|
||||
<service id="sylius.provider.channel_based_default_zone_provider" class="Sylius\Bundle\CoreBundle\Provider\ChannelBasedDefaultTaxZoneProvider" />
|
||||
|
||||
<service id="Sylius\Bundle\CoreBundle\Provider\CustomerProviderInterface" class="Sylius\Bundle\CoreBundle\Provider\CustomerProvider" >
|
||||
<argument type="service" id="sylius.repository.customer" />
|
||||
<argument type="service" id="sylius.canonicalizer" />
|
||||
</service>
|
||||
<service id="sylius.integer_distributor" class="Sylius\Component\Core\Distributor\IntegerDistributor" />
|
||||
<service id="Sylius\Component\Core\Distributor\IntegerDistributorInterface" alias="sylius.integer_distributor" />
|
||||
|
||||
|
|
@ -168,11 +163,6 @@
|
|||
</service>
|
||||
<service id="Sylius\Component\Core\Factory\CustomerAfterCheckoutFactoryInterface" alias="sylius.factory.customer_after_checkout" />
|
||||
|
||||
<service id="sylius.provider.product_variants_prices" class="Sylius\Component\Core\Provider\ProductVariantsPricesProvider">
|
||||
<argument type="service" id="sylius.calculator.product_variant_price" />
|
||||
</service>
|
||||
<service id="Sylius\Component\Core\Provider\ProductVariantsPricesProviderInterface" alias="sylius.provider.product_variants_prices" />
|
||||
|
||||
<service id="sylius.twig.extension.product_variants_prices" class="Sylius\Bundle\CoreBundle\Twig\ProductVariantsPricesExtension" public="false">
|
||||
<argument type="service" id="sylius.templating.helper.product_variants_prices" />
|
||||
<tag name="twig.extension" />
|
||||
|
|
@ -219,11 +209,6 @@
|
|||
</service>
|
||||
<service id="Sylius\Component\Core\Calculator\ProductVariantPriceCalculatorInterface" alias="sylius.calculator.product_variant_price" />
|
||||
|
||||
<service id="sylius.translation_locale_provider.admin" class="Sylius\Component\Core\Provider\TranslationLocaleProvider">
|
||||
<argument type="service" id="sylius.repository.locale" />
|
||||
<argument type="string">%locale%</argument>
|
||||
</service>
|
||||
|
||||
<service id="sylius.section_resolver.uri_based_section_resolver" class="Sylius\Bundle\CoreBundle\SectionResolver\UriBasedSectionProvider">
|
||||
<argument type="service" id="request_stack" />
|
||||
<argument type="collection" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
This file is part of the Sylius package.
|
||||
|
||||
(c) Sylius Sp. z o.o.
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
|
||||
-->
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<defaults public="true" />
|
||||
|
||||
<service id="sylius.provider.channel_based_default_zone_provider" class="Sylius\Bundle\CoreBundle\Provider\ChannelBasedDefaultTaxZoneProvider" />
|
||||
|
||||
<service id="Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProviderInterface" class="Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProvider">
|
||||
<argument type="service" id="sylius.context.locale" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\CoreBundle\Provider\CustomerProviderInterface" class="Sylius\Bundle\CoreBundle\Provider\CustomerProvider" >
|
||||
<argument type="service" id="sylius.repository.customer" />
|
||||
<argument type="service" id="sylius.canonicalizer" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.provider.product_variants_prices" class="Sylius\Component\Core\Provider\ProductVariantsPricesProvider">
|
||||
<argument type="service" id="sylius.calculator.product_variant_price" />
|
||||
</service>
|
||||
<service id="Sylius\Component\Core\Provider\ProductVariantsPricesProviderInterface" alias="sylius.provider.product_variants_prices" />
|
||||
|
||||
<service id="sylius.translation_locale_provider.admin" class="Sylius\Component\Core\Provider\TranslationLocaleProvider">
|
||||
<argument type="service" id="sylius.repository.locale" />
|
||||
<argument type="string">%locale%</argument>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
@ -57,5 +57,10 @@
|
|||
<argument>%sylius.unsecured_urls%</argument>
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.twig.extension.product_translation" class="Sylius\Bundle\CoreBundle\Twig\ProductTranslationExtension" public="false">
|
||||
<argument type="service" id="Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProviderInterface" />
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Tests\Provider;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProvider;
|
||||
use Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProviderInterface;
|
||||
use Sylius\Component\Core\Model\Channel;
|
||||
use Sylius\Component\Core\Model\Product;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\ProductTranslation;
|
||||
use Sylius\Component\Core\Model\ProductTranslationInterface;
|
||||
use Sylius\Component\Locale\Context\LocaleContextInterface;
|
||||
use Sylius\Component\Locale\Model\Locale;
|
||||
|
||||
final class ChannelBasedProductTranslationProviderTest extends TestCase
|
||||
{
|
||||
private LocaleContextInterface|MockObject $localeContext;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->localeContext = $this->createMock(LocaleContextInterface::class);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_provides_product_translation_using_administrator_locale(): void
|
||||
{
|
||||
$product = new Product();
|
||||
$product->addTranslation($this->createProductTranslation($product, 'pl_PL', 'polish-product-slug'));
|
||||
$product->addTranslation($this->createProductTranslation($product, 'en_US', 'english-product-slug'));
|
||||
|
||||
$channelLocale = new Locale();
|
||||
$channelLocale->setCode('en_US');
|
||||
|
||||
$channel = new Channel();
|
||||
$channel->setDefaultLocale($channelLocale);
|
||||
|
||||
$this->localeContext->expects($this->once())->method('getLocaleCode')->willReturn('pl_PL');
|
||||
|
||||
$provider = $this->createProvider();
|
||||
$productTranslation = $provider->provide($product, $channel);
|
||||
|
||||
$this->assertSame('polish-product-slug', $productTranslation->getSlug());
|
||||
$this->assertSame('pl_PL', $productTranslation->getLocale());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_generates_url_using_channel_default_locale_translations(): void
|
||||
{
|
||||
$product = new Product();
|
||||
$product->addTranslation($this->createProductTranslation($product, 'pl_PL', ''));
|
||||
$product->addTranslation($this->createProductTranslation($product, 'en_US', 'english-product-slug'));
|
||||
|
||||
$channelLocale = new Locale();
|
||||
$channelLocale->setCode('en_US');
|
||||
|
||||
$channel = new Channel();
|
||||
$channel->setDefaultLocale($channelLocale);
|
||||
|
||||
$this->localeContext->expects($this->once())->method('getLocaleCode')->willReturn('de_DE');
|
||||
|
||||
$provider = $this->createProvider();
|
||||
$productTranslation = $provider->provide($product, $channel);
|
||||
|
||||
$this->assertSame('english-product-slug', $productTranslation->getSlug());
|
||||
$this->assertSame('en_US', $productTranslation->getLocale());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_generates_url_using_any_translation_enabled_in_channel_if_administrator_and_channel_default_translations_does_not_exist(): void
|
||||
{
|
||||
$product = new Product();
|
||||
$product->addTranslation($this->createProductTranslation($product, 'pl_PL', ''));
|
||||
$product->addTranslation($this->createProductTranslation($product, 'en_US', ''));
|
||||
$product->addTranslation($this->createProductTranslation($product, 'de_DE', 'german-product-slug'));
|
||||
$product->addTranslation($this->createProductTranslation($product, 'fr_FR', 'french-product-slug'));
|
||||
|
||||
$enUsLocale = new Locale();
|
||||
$enUsLocale->setCode('en_US');
|
||||
|
||||
$deDeLocale = new Locale();
|
||||
$deDeLocale->setCode('de_DE');
|
||||
|
||||
$channel = new Channel();
|
||||
$channel->setDefaultLocale($enUsLocale);
|
||||
$channel->addLocale($enUsLocale);
|
||||
$channel->addLocale($deDeLocale);
|
||||
|
||||
$this->localeContext->expects($this->once())->method('getLocaleCode')->willReturn('es_ES');
|
||||
|
||||
$provider = $this->createProvider();
|
||||
$productTranslation = $provider->provide($product, $channel);
|
||||
|
||||
$this->assertSame('german-product-slug', $productTranslation->getSlug());
|
||||
$this->assertSame('de_DE', $productTranslation->getLocale());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_generates_null_when_no_translations_available(): void
|
||||
{
|
||||
$product = new Product();
|
||||
|
||||
$channelLocale = new Locale();
|
||||
$channelLocale->setCode('en_US');
|
||||
|
||||
$channel = new Channel();
|
||||
$channel->setDefaultLocale($channelLocale);
|
||||
|
||||
$this->localeContext->expects($this->once())->method('getLocaleCode')->willReturn('fr_FR');
|
||||
|
||||
$provider = $this->createProvider();
|
||||
$productTranslation = $provider->provide($product, $channel);
|
||||
|
||||
$this->assertNull($productTranslation);
|
||||
}
|
||||
|
||||
private function createProvider(): ChannelBasedProductTranslationProviderInterface
|
||||
{
|
||||
return new ChannelBasedProductTranslationProvider($this->localeContext);
|
||||
}
|
||||
|
||||
private function createProductTranslation(ProductInterface $product, string $localeCode, string $slug = ''): ProductTranslationInterface
|
||||
{
|
||||
$productTranslation = new ProductTranslation();
|
||||
$productTranslation->setTranslatable($product);
|
||||
$productTranslation->setLocale($localeCode);
|
||||
$productTranslation->setSlug($slug);
|
||||
|
||||
return $productTranslation;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Twig;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Provider\ChannelBasedProductTranslationProviderInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class ProductTranslationExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(
|
||||
private ChannelBasedProductTranslationProviderInterface $channelBasedProductTranslationProvider,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('sylius_product_translation', [$this->channelBasedProductTranslationProvider, 'provide']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -39,10 +39,10 @@ final class DefaultChannelFactory implements DefaultChannelFactoryInterface
|
|||
) {
|
||||
}
|
||||
|
||||
public function create(?string $code = null, ?string $name = null, ?string $currencyCode = null): array
|
||||
public function create(?string $code = null, ?string $name = null, ?string $currencyCode = null, ?string $localeCode = null): array
|
||||
{
|
||||
$currency = $this->provideCurrency($currencyCode);
|
||||
$locale = $this->provideLocale();
|
||||
$locale = $this->provideLocale($localeCode);
|
||||
|
||||
/** @var ChannelInterface $channel */
|
||||
$channel = $this->channelFactory->createNamed($name ?: self::DEFAULT_CHANNEL_NAME);
|
||||
|
|
@ -82,7 +82,7 @@ final class DefaultChannelFactory implements DefaultChannelFactoryInterface
|
|||
return $currency;
|
||||
}
|
||||
|
||||
private function provideLocale(): LocaleInterface
|
||||
private function provideLocale(?string $localeCode = null): LocaleInterface
|
||||
{
|
||||
/** @var LocaleInterface|null $locale */
|
||||
$locale = $this->localeRepository->findOneBy(['code' => $this->defaultLocaleCode]);
|
||||
|
|
@ -90,7 +90,7 @@ final class DefaultChannelFactory implements DefaultChannelFactoryInterface
|
|||
if (null === $locale) {
|
||||
/** @var LocaleInterface $locale */
|
||||
$locale = $this->localeFactory->createNew();
|
||||
$locale->setCode($this->defaultLocaleCode);
|
||||
$locale->setCode($localeCode ?? $this->defaultLocaleCode);
|
||||
|
||||
$this->localeRepository->add($locale);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,10 @@ namespace Sylius\Component\Core\Test\Services;
|
|||
|
||||
interface DefaultChannelFactoryInterface
|
||||
{
|
||||
public function create(?string $code = null, ?string $name = null, ?string $currencyCode = null): array;
|
||||
public function create(
|
||||
?string $code = null,
|
||||
?string $name = null,
|
||||
?string $currencyCode = null,
|
||||
?string $localeCode = null,
|
||||
): array;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ final class DefaultUnitedStatesChannelFactory implements DefaultChannelFactoryIn
|
|||
) {
|
||||
}
|
||||
|
||||
public function create(?string $code = null, ?string $name = null, ?string $currencyCode = null): array
|
||||
public function create(?string $code = null, ?string $name = null, ?string $currencyCode = null, ?string $localeCode = null): array
|
||||
{
|
||||
$currency = $this->provideCurrency($currencyCode);
|
||||
$locale = $this->provideLocale();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue