diff --git a/spec/Provider/ProductUrlProviderSpec.php b/spec/Provider/ProductUrlProviderSpec.php index b011654a..b51db9c5 100644 --- a/spec/Provider/ProductUrlProviderSpec.php +++ b/spec/Provider/ProductUrlProviderSpec.php @@ -75,6 +75,8 @@ function it_generates_urls_for_the_unique_channel_locale( $locale->getCode()->willReturn('en_US'); + $channel->getDefaultLocale()->shouldBeCalledOnce()->willReturn($locale); + $channel->getLocales()->shouldBeCalled()->willReturn(new ArrayCollection([ $locale->getWrappedObject(), ])); @@ -138,7 +140,7 @@ function it_generates_urls_for_the_unique_channel_locale( $this->generate($channel); } - function it_generates_urls_for_all_channel_locales( + function it_generates_urls_using_the_default_channel_locale_as_main_and_others_as_alternatives( ProductRepository $repository, RouterInterface $router, UrlFactoryInterface $urlFactory, @@ -161,7 +163,9 @@ function it_generates_urls_for_all_channel_locales( ): void { $now = new \DateTime(); - $localeContext->getLocaleCode()->willReturn('en_US'); + $localeContext->getLocaleCode()->shouldNotBeCalled()->willReturn('en_US'); + + $channel->getDefaultLocale()->shouldBeCalledOnce()->willReturn($enUSLocale); $enUSLocale->getCode()->willReturn('en_US'); $nlNLLocale->getCode()->willReturn('nl_NL'); @@ -213,7 +217,7 @@ function it_generates_urls_for_all_channel_locales( $router->generate('sylius_shop_product_show', [ 'slug' => 't-shirt', '_locale' => 'en_US', - ])->willReturn('http://sylius.org/en_US/products/t-shirt'); + ])->shouldBeCalled()->willReturn('http://sylius.org/en_US/products/t-shirt'); $router->generate('sylius_shop_product_show', [ 'slug' => 't-shirt', @@ -234,4 +238,103 @@ function it_generates_urls_for_all_channel_locales( $this->generate($channel); } + + function it_generates_urls_using_the_storage_context_locale_as_main_and_others_as_alternatives( + ProductRepository $repository, + RouterInterface $router, + UrlFactoryInterface $urlFactory, + AlternativeUrlFactoryInterface $alternativeUrlFactory, + LocaleContextInterface $localeContext, + LocaleInterface $enUSLocale, + LocaleInterface $nlNLLocale, + Collection $products, + \Iterator $iterator, + ProductInterface $product, + ProductImageInterface $productImage, + ProductTranslation $productEnUSTranslation, + ProductTranslation $productNlNLTranslation, + UrlInterface $url, + AlternativeUrlInterface $alternativeUrl, + QueryBuilder $queryBuilder, + AbstractQuery $query, + ChannelInterface $channel, + ProductImagesToSitemapImagesCollectionGeneratorInterface $productToImageSitemapArrayGenerator + ): void { + $now = new \DateTime(); + + $localeContext->getLocaleCode()->shouldBeCalledOnce()->willReturn('nl_NL'); + + $channel->getDefaultLocale()->shouldBeCalledOnce()->willReturn(null); + + $enUSLocale->getCode()->willReturn('en_US'); + $nlNLLocale->getCode()->willReturn('nl_NL'); + + $channel->getLocales()->shouldBeCalled()->willReturn(new ArrayCollection([ + $enUSLocale->getWrappedObject(), + $nlNLLocale->getWrappedObject(), + ])); + + $repository->createQueryBuilder('o')->willReturn($queryBuilder); + $queryBuilder->addSelect('translation')->willReturn($queryBuilder); + $queryBuilder->innerJoin('o.translations', 'translation')->willReturn($queryBuilder); + $queryBuilder->andWhere(':channel MEMBER OF o.channels')->willReturn($queryBuilder); + $queryBuilder->andWhere('o.enabled = :enabled')->willReturn($queryBuilder); + $queryBuilder->setParameter('channel', $channel)->willReturn($queryBuilder); + $queryBuilder->setParameter('enabled', true)->willReturn($queryBuilder); + $queryBuilder->getQuery()->willReturn($query); + $query->getResult()->willReturn($products); + + $products->getIterator()->willReturn($iterator); + $iterator->valid()->willReturn(true, false); + $iterator->next()->shouldBeCalled(); + $iterator->rewind()->shouldBeCalled(); + + $iterator->current()->willReturn($product); + + $productImage->getPath()->willReturn(null); + + $product->getUpdatedAt()->willReturn($now); + $product->getImages()->willReturn(new ArrayCollection([ + $productImage->getWrappedObject(), + ])); + + $sitemapImageCollection = new ArrayCollection([]); + + $productToImageSitemapArrayGenerator->generate($product)->willReturn($sitemapImageCollection); + + $productEnUSTranslation->getLocale()->willReturn('en_US'); + $productEnUSTranslation->getSlug()->willReturn('t-shirt'); + + $productNlNLTranslation->getLocale()->willReturn('nl_NL'); + $productNlNLTranslation->getSlug()->willReturn('t-shirt'); + + $product->getTranslations()->shouldBeCalled()->willReturn(new ArrayCollection([ + $productEnUSTranslation->getWrappedObject(), + $productNlNLTranslation->getWrappedObject(), + ])); + + $router->generate('sylius_shop_product_show', [ + 'slug' => 't-shirt', + '_locale' => 'en_US', + ])->shouldBeCalled()->willReturn('http://sylius.org/en_US/products/t-shirt'); + + $router->generate('sylius_shop_product_show', [ + 'slug' => 't-shirt', + '_locale' => 'nl_NL', + ])->shouldBeCalled()->willReturn('http://sylius.org/nl_NL/products/t-shirt'); + + $urlFactory->createNew('')->willReturn($url); + $alternativeUrlFactory->createNew('http://sylius.org/en_US/products/t-shirt', 'en_US')->willReturn($alternativeUrl); + + $url->setImages($sitemapImageCollection)->shouldBeCalled(); + $url->setLocation('http://sylius.org/en_US/products/t-shirt')->shouldNotBeCalled(); + $url->setLocation('http://sylius.org/nl_NL/products/t-shirt')->shouldBeCalled(); + $url->setLastModification($now)->shouldBeCalled(); + $url->setChangeFrequency(ChangeFrequency::always())->shouldBeCalled(); + $url->setPriority(0.5)->shouldBeCalled(); + + $url->addAlternative($alternativeUrl)->shouldBeCalled(); + + $this->generate($channel); + } } diff --git a/src/Provider/ProductUrlProvider.php b/src/Provider/ProductUrlProvider.php index 5ad11e24..e5c1c38a 100644 --- a/src/Provider/ProductUrlProvider.php +++ b/src/Provider/ProductUrlProvider.php @@ -33,6 +33,8 @@ final class ProductUrlProvider implements UrlProviderInterface private ChannelInterface $channel; + private string $defaultLocaleCode; + /** @var array */ private array $channelLocaleCodes; @@ -67,6 +69,8 @@ public function generate(ChannelInterface $channel): iterable $this->channel = $channel; $urls = []; $this->channelLocaleCodes = []; + $channelDefaultLocaleCode = $this->getChannelDefaultLocaleCode($channel); + $this->defaultLocaleCode = $channelDefaultLocaleCode ?? $this->localeContext->getLocaleCode(); foreach ($this->getProducts() as $product) { $urls[] = $this->createProductUrl($product); @@ -127,9 +131,9 @@ private function createProductUrl(ProductInterface $product): UrlInterface /** @var ProductTranslationInterface $translation */ foreach ($this->getTranslations($product) as $translation) { - $locale = $translation->getLocale(); + $localeCode = $translation->getLocale(); - if ($locale === null) { + if ($localeCode === null) { continue; } @@ -142,15 +146,25 @@ private function createProductUrl(ProductInterface $product): UrlInterface '_locale' => $translation->getLocale(), ]); - if ($locale === $this->localeContext->getLocaleCode()) { + if ($localeCode === $this->defaultLocaleCode) { $productUrl->setLocation($location); continue; } - $productUrl->addAlternative($this->urlAlternativeFactory->createNew($location, $locale)); + $productUrl->addAlternative($this->urlAlternativeFactory->createNew($location, $localeCode)); } return $productUrl; } + + private function getChannelDefaultLocaleCode(ChannelInterface $channel): ?string + { + $channelDefaultLocale = $channel->getDefaultLocale(); + if ($channelDefaultLocale === null) { + return null; + } + + return $channelDefaultLocale->getCode(); + } } diff --git a/tests/Controller/SitemapProductControllerApiChannelDefaultLocaleTest.php b/tests/Controller/SitemapProductControllerApiChannelDefaultLocaleTest.php new file mode 100644 index 00000000..ebafc201 --- /dev/null +++ b/tests/Controller/SitemapProductControllerApiChannelDefaultLocaleTest.php @@ -0,0 +1,18 @@ +loadFixturesFromFiles(['channel_with_locale_different_from_default.yaml', 'product_with_locales.yaml']); + $this->generateSitemaps(); + + $response = $this->getBufferedResponse('/sitemap/products.xml'); + $this->assertResponse($response, 'show_sitemap_products_with_channel_default_locale'); + $this->deleteSitemaps(); + } +} diff --git a/tests/DataFixtures/ORM/channel_with_locale_different_from_default.yaml b/tests/DataFixtures/ORM/channel_with_locale_different_from_default.yaml new file mode 100644 index 00000000..20cf0b08 --- /dev/null +++ b/tests/DataFixtures/ORM/channel_with_locale_different_from_default.yaml @@ -0,0 +1,25 @@ +Sylius\Component\Core\Model\Channel: + web_channel: + code: "WEB" + name: "Web Store" + hostname: "localhost" + description: "Lorem ipsum" + baseCurrency: "@usd" + defaultLocale: "@locale_nl_nl" + locales: ["@locale_nl_nl"] + color: "black" + enabled: true + taxCalculationStrategy: "order_items_based" + accountVerificationRequired: true + +Sylius\Component\Currency\Model\Currency: + usd: + code: USD + euro: + code: EUR + +Sylius\Component\Locale\Model\Locale: + locale_nl_nl: + code: nl_NL + locale_en_us: + code: en_US diff --git a/tests/DataFixtures/ORM/product_with_locales.yaml b/tests/DataFixtures/ORM/product_with_locales.yaml new file mode 100644 index 00000000..e62cc3d4 --- /dev/null +++ b/tests/DataFixtures/ORM/product_with_locales.yaml @@ -0,0 +1,20 @@ +Sylius\Component\Core\Model\Product: + test_product: + code: "test-code" + channels: ["@web_channel"] + currentLocale: "nl_NL" + translations: + - "@nl_nl_test_product_translation" + - "@en_us_test_product_translation" + +Sylius\Component\Core\Model\ProductTranslation: + nl_nl_test_product_translation: + slug: "test-code" + locale: "nl_NL" + name: "Test" + translatable: "@test_product" + en_us_test_product_translation: + slug: "test-code" + locale: "en_US" + name: "Test" + translatable: "@test_product" diff --git a/tests/Responses/show_sitemap_products_with_channel_default_locale.xml b/tests/Responses/show_sitemap_products_with_channel_default_locale.xml new file mode 100644 index 00000000..d1ef212a --- /dev/null +++ b/tests/Responses/show_sitemap_products_with_channel_default_locale.xml @@ -0,0 +1,9 @@ + + + + http://localhost/nl_NL/products/test-code + @string@.isDateTime() + always + 0.5 + +