mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-05 20:57:12 +00:00
Merge branch '2.2' into fix/discriminator-subclass-iri-generation
This commit is contained in:
commit
cafc7ff9e9
37 changed files with 421 additions and 77 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# UPGRADE FROM `2.6.0` TO `2.7.0`
|
||||
# UPGRADE FROM `2.2.6` TO `2.2.7`
|
||||
|
||||
## Constructor Signature Changes
|
||||
|
||||
|
|
@ -14,6 +14,14 @@
|
|||
)
|
||||
```
|
||||
|
||||
## Bahavior changes
|
||||
|
||||
1. The `LiveComponentTagPass` and `TwigComponentTagPass` in `SyliusUiBundle` were registered with a priority of `500`,
|
||||
which caused them to run before Symfony's autoconfiguration passes (priority `100`).
|
||||
As a result, services tagged via `#[AutoconfigureTag]` or `registerForAutoconfiguration()` with the `sylius.twig_component`
|
||||
or `sylius.live_component.*` tag did not receive the `twig.component` tag.
|
||||
The priority has been lowered to `50` to ensure Symfony's autoconfiguration runs first.
|
||||
|
||||
# UPGRADE FROM `2.1` TO `2.2`
|
||||
|
||||
## Telemetry
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
# UPGRADE FROM `2.2.6` TO `2.2.7`
|
||||
|
||||
## Shop API
|
||||
|
||||
1. The `Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product\ChannelAndLocaleBasedExtension` service (`sylius_api.doctrine.orm.query_extension.shop.product.channel_and_locale_based`)
|
||||
now implements `QueryItemExtensionInterface` in addition to `QueryCollectionExtensionInterface`.
|
||||
The service is tagged with `api_platform.doctrine.orm.query_extension.item`.
|
||||
|
||||
If you decorate this service, make sure your decorator also implements
|
||||
`ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface` and proxies the `applyToItem` method.
|
||||
|
||||
# UPGRADE FROM `2.1` TO `2.2`
|
||||
|
||||
## Modified routes
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ interface ResponseCheckerInterface
|
|||
|
||||
public function hasViolationWithMessage(Response $response, string $message, ?string $property = null): bool;
|
||||
|
||||
public function appendError(Response $response): ResponseCheckerInterface;
|
||||
public function appendError(Response $response): self;
|
||||
|
||||
public function cleanErrors(): void;
|
||||
|
||||
|
|
|
|||
|
|
@ -228,6 +228,17 @@ sylius_twig_hooks:
|
|||
template: '@SyliusAdmin/order/show/content/sections/shipments/item/actions.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_admin.order.show.content.sections.shipments.item.general':
|
||||
method:
|
||||
template: '@SyliusAdmin/order/show/content/sections/shipments/item/general/method.html.twig'
|
||||
priority: 200
|
||||
shipped_at:
|
||||
template: '@SyliusAdmin/order/show/content/sections/shipments/item/general/shipped_at.html.twig'
|
||||
priority: 100
|
||||
tracking:
|
||||
template: '@SyliusAdmin/order/show/content/sections/shipments/item/general/tracking.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_admin.order.show.content.sections.shipments.item.actions':
|
||||
ship:
|
||||
component: 'sylius_admin:shipment:ship_form'
|
||||
|
|
|
|||
|
|
@ -1,20 +1,3 @@
|
|||
{% set shipment = hookable_metadata.context.shipment %}
|
||||
|
||||
<td class="px-3 py-2 text-nowrap">
|
||||
<div class="py-2">
|
||||
<strong {{ sylius_test_html_attribute('shipment-method') }}>{{ shipment.method.name }}</strong>
|
||||
<div class="text-secondary">{{ ux_icon('tabler:world') }} {{ shipment.method.zone }}</div>
|
||||
</div>
|
||||
{% if shipment.shippedAt is not empty %}
|
||||
<div class="text-secondary-emphasis">
|
||||
<strong>{{ 'sylius.ui.shipped_at'|trans }}:</strong>
|
||||
<span {{ sylius_test_html_attribute('shipped-at-date') }}>{{ shipment.shippedAt|date('d-m-Y H:i:s') }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if shipment.state == 'shipped' and shipment.tracking is not empty %}
|
||||
<div class="text-secondary-emphasis">
|
||||
<strong>{{ 'sylius.ui.tracking_code'|trans }}:</strong>
|
||||
<span>{{ shipment.tracking }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% hook 'general' %}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{% set shipment = hookable_metadata.context.shipment %}
|
||||
|
||||
<div class="py-2">
|
||||
<strong {{ sylius_test_html_attribute('shipment-method') }}>{{ shipment.method.name }}</strong>
|
||||
<div class="text-secondary">{{ ux_icon('tabler:world') }} {{ shipment.method.zone }}</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{% set shipment = hookable_metadata.context.shipment %}
|
||||
|
||||
{% if shipment.shippedAt is not empty %}
|
||||
<div class="text-secondary-emphasis">
|
||||
<strong>{{ 'sylius.ui.shipped_at'|trans }}:</strong>
|
||||
<span {{ sylius_test_html_attribute('shipped-at-date') }}>{{ shipment.shippedAt|date('d-m-Y H:i:s') }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{% set shipment = hookable_metadata.context.shipment %}
|
||||
|
||||
{% if shipment.state == 'shipped' and shipment.tracking is not empty %}
|
||||
<div class="text-secondary-emphasis">
|
||||
<strong>{{ 'sylius.ui.tracking_code'|trans }}:</strong>
|
||||
<span>{{ shipment.tracking }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
|
||||
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
|
||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
|
@ -21,9 +22,8 @@ use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiSection;
|
|||
use Sylius\Bundle\ApiBundle\Serializer\ContextKeys;
|
||||
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final readonly class ChannelAndLocaleBasedExtension implements QueryCollectionExtensionInterface
|
||||
final readonly class ChannelAndLocaleBasedExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
|
||||
{
|
||||
public function __construct(private SectionProviderInterface $sectionProvider)
|
||||
{
|
||||
|
|
@ -38,6 +38,33 @@ final readonly class ChannelAndLocaleBasedExtension implements QueryCollectionEx
|
|||
string $resourceClass,
|
||||
?Operation $operation = null,
|
||||
array $context = [],
|
||||
): void {
|
||||
$this->apply($queryBuilder, $queryNameGenerator, $resourceClass, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $identifiers
|
||||
* @param array<array-key, mixed> $context
|
||||
*/
|
||||
public function applyToItem(
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
string $resourceClass,
|
||||
array $identifiers,
|
||||
?Operation $operation = null,
|
||||
array $context = [],
|
||||
): void {
|
||||
$this->apply($queryBuilder, $queryNameGenerator, $resourceClass, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $context
|
||||
*/
|
||||
private function apply(
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
string $resourceClass,
|
||||
array $context,
|
||||
): void {
|
||||
if (!is_a($resourceClass, ProductInterface::class, true)) {
|
||||
return;
|
||||
|
|
@ -47,8 +74,9 @@ final readonly class ChannelAndLocaleBasedExtension implements QueryCollectionEx
|
|||
return;
|
||||
}
|
||||
|
||||
Assert::keyExists($context, ContextKeys::CHANNEL);
|
||||
Assert::keyExists($context, ContextKeys::LOCALE_CODE);
|
||||
if (!isset($context[ContextKeys::CHANNEL]) || !isset($context[ContextKeys::LOCALE_CODE])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channel = $context[ContextKeys::CHANNEL];
|
||||
$localeCode = $context[ContextKeys::LOCALE_CODE];
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
>
|
||||
<argument type="service" id="sylius.section_resolver.uri_based" />
|
||||
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||
<tag name="api_platform.doctrine.orm.query_extension.item" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ final readonly class ItemProvider implements ProviderInterface
|
|||
|
||||
$customer = $order->getCustomer();
|
||||
|
||||
return null === $customer
|
||||
|| null === $customer->getUser()
|
||||
|| $order->isCreatedByGuest();
|
||||
return null === $customer ||
|
||||
null === $customer->getUser() ||
|
||||
$order->isCreatedByGuest();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ namespace Tests\Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product
|
|||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product\ChannelAndLocaleBasedExtension;
|
||||
|
|
@ -73,11 +72,12 @@ final class ChannelAndLocaleBasedExtensionTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function test_it_throws_exception_if_context_has_no_channel(): void
|
||||
public function test_it_does_not_apply_conditions_to_collection_when_context_has_no_channel(): void
|
||||
{
|
||||
$this->sectionProvider->method('getSection')->willReturn(new ShopApiSection());
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->queryBuilder->expects(self::never())->method('getRootAliases');
|
||||
$this->queryBuilder->expects(self::never())->method('andWhere');
|
||||
|
||||
$this->extension->applyToCollection(
|
||||
$this->queryBuilder,
|
||||
|
|
@ -87,13 +87,14 @@ final class ChannelAndLocaleBasedExtensionTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function test_it_throws_exception_if_context_has_no_locale(): void
|
||||
public function test_it_does_not_apply_conditions_to_collection_when_context_has_no_locale(): void
|
||||
{
|
||||
$this->sectionProvider->method('getSection')->willReturn(new ShopApiSection());
|
||||
|
||||
$channel = $this->createMock(ChannelInterface::class);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->queryBuilder->expects(self::never())->method('getRootAliases');
|
||||
$this->queryBuilder->expects(self::never())->method('andWhere');
|
||||
|
||||
$this->extension->applyToCollection(
|
||||
$this->queryBuilder,
|
||||
|
|
@ -167,4 +168,132 @@ final class ChannelAndLocaleBasedExtensionTest extends TestCase
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_does_not_apply_conditions_to_item_for_unsupported_resource(): void
|
||||
{
|
||||
$this->queryBuilder->expects(self::never())->method('getRootAliases');
|
||||
$this->queryBuilder->expects(self::never())->method('andWhere');
|
||||
|
||||
$this->extension->applyToItem(
|
||||
$this->queryBuilder,
|
||||
$this->queryNameGenerator,
|
||||
\stdClass::class,
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_does_not_apply_conditions_to_item_for_admin_api_section(): void
|
||||
{
|
||||
$this->sectionProvider->method('getSection')->willReturn(new AdminApiSection());
|
||||
|
||||
$this->queryBuilder->expects(self::never())->method('getRootAliases');
|
||||
$this->queryBuilder->expects(self::never())->method('andWhere');
|
||||
|
||||
$this->extension->applyToItem(
|
||||
$this->queryBuilder,
|
||||
$this->queryNameGenerator,
|
||||
AddressInterface::class,
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_does_not_apply_conditions_to_item_when_context_has_no_channel(): void
|
||||
{
|
||||
$this->sectionProvider->method('getSection')->willReturn(new ShopApiSection());
|
||||
|
||||
$this->queryBuilder->expects(self::never())->method('getRootAliases');
|
||||
$this->queryBuilder->expects(self::never())->method('andWhere');
|
||||
|
||||
$this->extension->applyToItem(
|
||||
$this->queryBuilder,
|
||||
$this->queryNameGenerator,
|
||||
ProductInterface::class,
|
||||
['code' => 'MUG'],
|
||||
new Get(),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_does_not_apply_conditions_to_item_when_context_has_no_locale(): void
|
||||
{
|
||||
$this->sectionProvider->method('getSection')->willReturn(new ShopApiSection());
|
||||
|
||||
$channel = $this->createMock(ChannelInterface::class);
|
||||
|
||||
$this->queryBuilder->expects(self::never())->method('getRootAliases');
|
||||
$this->queryBuilder->expects(self::never())->method('andWhere');
|
||||
|
||||
$this->extension->applyToItem(
|
||||
$this->queryBuilder,
|
||||
$this->queryNameGenerator,
|
||||
ProductInterface::class,
|
||||
['code' => 'MUG'],
|
||||
new Get(),
|
||||
[ContextKeys::CHANNEL => $channel],
|
||||
);
|
||||
}
|
||||
|
||||
public function test_it_filters_item_by_channel_and_locale(): void
|
||||
{
|
||||
$this->sectionProvider->method('getSection')->willReturn(new ShopApiSection());
|
||||
|
||||
$channel = $this->createMock(ChannelInterface::class);
|
||||
|
||||
$this->queryNameGenerator->expects(self::exactly(2))
|
||||
->method('generateParameterName')
|
||||
->with($this->callback(function ($param) {
|
||||
return $param === 'channel' || $param === 'localeCode';
|
||||
}))
|
||||
->willReturnCallback(function ($param) {
|
||||
return $param;
|
||||
});
|
||||
|
||||
$this->queryBuilder->method('getRootAliases')->willReturn(['o']);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('addSelect')
|
||||
->with('translation')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('innerJoin')
|
||||
->with('o.translations', 'translation', 'WITH', 'translation.locale = :localeCode')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('andWhere')
|
||||
->with(':channel MEMBER OF o.channels')
|
||||
->willReturnSelf();
|
||||
|
||||
$expectedParams = [
|
||||
['channel', $channel],
|
||||
['localeCode', 'en_US'],
|
||||
];
|
||||
$callIndex = 0;
|
||||
$this->queryBuilder->expects(self::exactly(2))
|
||||
->method('setParameter')
|
||||
->with(
|
||||
$this->callback(function ($name) use (&$expectedParams, &$callIndex) {
|
||||
return $name === $expectedParams[$callIndex][0];
|
||||
}),
|
||||
$this->callback(function ($value) use (&$expectedParams, &$callIndex) {
|
||||
$result = $value === $expectedParams[$callIndex][1];
|
||||
++$callIndex;
|
||||
|
||||
return $result;
|
||||
}),
|
||||
)
|
||||
->willReturnSelf();
|
||||
|
||||
$this->extension->applyToItem(
|
||||
$this->queryBuilder,
|
||||
$this->queryNameGenerator,
|
||||
ProductInterface::class,
|
||||
['code' => 'MUG'],
|
||||
new Get(),
|
||||
[
|
||||
ContextKeys::CHANNEL => $channel,
|
||||
ContextKeys::LOCALE_CODE => 'en_US',
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,6 +175,69 @@ sylius_twig_hooks:
|
|||
template: '@SyliusShop/shared/layout/base/footer/content/copy.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.footer.content.menu':
|
||||
informational_links:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/informational_links.html.twig'
|
||||
priority: 100
|
||||
help_links:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/help_links.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.footer.content.menu.informational_links':
|
||||
about:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/informational_links/about.html.twig'
|
||||
priority: 300
|
||||
terms_and_conditions:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/informational_links/terms_and_conditions.html.twig'
|
||||
priority: 200
|
||||
privacy_policy:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/informational_links/privacy_policy.html.twig'
|
||||
priority: 100
|
||||
contact_us:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/informational_links/contact_us.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.footer.content.menu.help_links':
|
||||
faq:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/help_links/faq.html.twig'
|
||||
priority: 200
|
||||
delivery_and_shipping:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/help_links/delivery_and_shipping.html.twig'
|
||||
priority: 100
|
||||
returns_policy:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/menu/help_links/returns_policy.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.footer.content.payment_methods':
|
||||
providers:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/providers.html.twig'
|
||||
priority: 100
|
||||
social_media:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/social_media.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.footer.content.payment_methods.providers':
|
||||
paypal:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/providers/paypal.html.twig'
|
||||
priority: 200
|
||||
adyen:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/providers/adyen.html.twig'
|
||||
priority: 100
|
||||
mollie:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/providers/mollie.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.footer.content.payment_methods.social_media':
|
||||
instagram:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/social_media/instagram.html.twig'
|
||||
priority: 200
|
||||
facebook:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/social_media/facebook.html.twig'
|
||||
priority: 100
|
||||
x:
|
||||
template: '@SyliusShop/shared/layout/base/footer/content/payment_methods/social_media/x.html.twig'
|
||||
priority: 0
|
||||
|
||||
'sylius_shop.base.offcanvas':
|
||||
cart:
|
||||
component: 'sylius_shop:cart:widget:offcanvas'
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
id="sylius_shop.twig.component.product.summary"
|
||||
class="Sylius\Bundle\ShopBundle\Twig\Component\Product\SummaryComponent"
|
||||
>
|
||||
<argument type="service" id="sylius.resolver.product_variant.default" />
|
||||
<argument type="service" id="sylius.resolver.product_variant" />
|
||||
<argument type="service" id="sylius.repository.product" />
|
||||
<argument type="service" id="sylius.repository.product_variant" />
|
||||
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ class FormComponent
|
|||
/** @var OrderInterface|null $order */
|
||||
$order = $this->repository->find($value);
|
||||
|
||||
if (!$order instanceof OrderInterface
|
||||
|| $order->getCheckoutState() === OrderCheckoutStates::STATE_COMPLETED
|
||||
if (!$order instanceof OrderInterface ||
|
||||
$order->getCheckoutState() === OrderCheckoutStates::STATE_COMPLETED
|
||||
) {
|
||||
return $this->createResource();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,5 @@
|
|||
<div class="col-12 col-lg-6 order-lg-2">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
<nav aria-label="{{ 'sylius.ui.footer_informational_links'|trans }}" class="nav flex-column">
|
||||
<a class="nav-link" href="#">About</a>
|
||||
<a class="nav-link" href="#">Terms and Conditions</a>
|
||||
<a class="nav-link" href="#">Privacy Policy</a>
|
||||
<a class="nav-link" href="{{ path('sylius_shop_contact_request') }}">Contact us</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6">
|
||||
<nav aria-label="{{ 'sylius.ui.footer_help_links'|trans }}" class="nav flex-column mb-5">
|
||||
<a class="nav-link" href="#">FAQ</a>
|
||||
<a class="nav-link" href="#">Delivery & Shipping</a>
|
||||
<a class="nav-link" href="#">Returns Policy</a>
|
||||
</nav>
|
||||
</div>
|
||||
{% hook 'menu' %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="col-12 col-sm-6">
|
||||
<nav aria-label="{{ 'sylius.ui.footer_help_links'|trans }}" class="nav flex-column mb-5">
|
||||
{% hook 'help_links' %}
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="#">Delivery & Shipping</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="#">FAQ</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="#">Returns Policy</a>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="col-12 col-sm-6">
|
||||
<nav aria-label="{{ 'sylius.ui.footer_informational_links'|trans }}" class="nav flex-column">
|
||||
{% hook 'informational_links' %}
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="#">About</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="{{ path('sylius_shop_contact_request') }}">Contact us</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="#">Privacy Policy</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="nav-link" href="#">Terms and Conditions</a>
|
||||
|
|
@ -1,27 +1,4 @@
|
|||
<div class="col-12 text-center col-md-6 order-md-4 text-md-start col-lg-3 order-lg-4 text-lg-center">
|
||||
<div class="mb-3 fw-medium">Payment methods:</div>
|
||||
|
||||
<div class="row g-2 mb-4">
|
||||
<div class="col-4">
|
||||
<a href="https://sylius.com/partner/paypal/" target="_blank" class="d-block border rounded p-2">
|
||||
<img class="img-fluid" src="{{ asset('build/shop/images/paypal-logo.svg', 'shop') }}" width="100%" height="100%" alt="PayPal" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a href="https://sylius.com/partner/adyen/" target="_blank" class="d-block border rounded p-2">
|
||||
<img class="img-fluid" src="{{ asset('build/shop/images/adyen-logo.svg', 'shop') }}" width="100%" height="100%" alt="Adyen" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a href="https://sylius.com/partner/mollie/" target="_blank" class="d-block border rounded p-2">
|
||||
<img class="img-fluid" src="{{ asset('build/shop/images/mollie-logo.svg', 'shop') }}" width="100%" height="100%" alt="Mollie" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center justify-content-md-start justify-content-lg-center gap-3 mb-5">
|
||||
<a class="link-reset" href="https://www.instagram.com/sylius.team/" target="_blank" aria-label="instagram logo">{{ ux_icon('tabler:brand-instagram', { class: 'icon icon-md' }) }}</a>
|
||||
<a class="link-reset" href="https://www.facebook.com/SyliusEcommerce/" target="_blank" aria-label="facebook logo">{{ ux_icon('tabler:brand-facebook', { class: 'icon icon-md' }) }}</a>
|
||||
<a class="link-reset" href="https://x.com/Sylius" target="_blank" aria-label="x logo">{{ ux_icon('tabler:brand-x', { class: 'icon icon-md' }) }}</a>
|
||||
</div>
|
||||
{% hook 'payment_methods' %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<div class="row g-2 mb-4">
|
||||
{% hook 'providers' %}
|
||||
</div>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="col-4">
|
||||
<a href="https://sylius.com/partner/adyen/" target="_blank" class="d-block border rounded p-2">
|
||||
<img class="img-fluid" src="{{ asset('build/shop/images/adyen-logo.svg', 'shop') }}" width="100%" height="100%" alt="Adyen" />
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="col-4">
|
||||
<a href="https://sylius.com/partner/mollie/" target="_blank" class="d-block border rounded p-2">
|
||||
<img class="img-fluid" src="{{ asset('build/shop/images/mollie-logo.svg', 'shop') }}" width="100%" height="100%" alt="Mollie" />
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="col-4">
|
||||
<a href="https://sylius.com/partner/paypal/" target="_blank" class="d-block border rounded p-2">
|
||||
<img class="img-fluid" src="{{ asset('build/shop/images/paypal-logo.svg', 'shop') }}" width="100%" height="100%" alt="PayPal" />
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<div class="d-flex justify-content-center justify-content-md-start justify-content-lg-center gap-3 mb-5">
|
||||
{% hook 'social_media' %}
|
||||
</div>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="link-reset" href="https://www.facebook.com/SyliusEcommerce/" target="_blank" aria-label="facebook logo">{{ ux_icon('tabler:brand-facebook', { class: 'icon icon-md' }) }}</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="link-reset" href="https://www.instagram.com/sylius.team/" target="_blank" aria-label="instagram logo">{{ ux_icon('tabler:brand-instagram', { class: 'icon icon-md' }) }}</a>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a class="link-reset" href="https://x.com/Sylius" target="_blank" aria-label="x logo">{{ ux_icon('tabler:brand-x', { class: 'icon icon-md' }) }}</a>
|
||||
|
|
@ -25,7 +25,7 @@ final class SyliusUiBundle extends Bundle
|
|||
{
|
||||
public function build(ContainerBuilder $container): void
|
||||
{
|
||||
$container->addCompilerPass(new LiveComponentTagPass(), priority: 500);
|
||||
$container->addCompilerPass(new TwigComponentTagPass(), priority: 500);
|
||||
$container->addCompilerPass(new LiveComponentTagPass(), priority: 50);
|
||||
$container->addCompilerPass(new TwigComponentTagPass(), priority: 50);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
Sylius\Component\Core\Model\Channel:
|
||||
channel_web:
|
||||
code: 'WEB'
|
||||
name: 'Web Channel'
|
||||
hostname: 'localhost'
|
||||
description: 'Lorem ipsum'
|
||||
baseCurrency: '@currency_usd'
|
||||
defaultLocale: '@locale_en'
|
||||
locales: ['@locale_en']
|
||||
color: 'black'
|
||||
enabled: true
|
||||
taxCalculationStrategy: 'order_items_based'
|
||||
channel_mobile:
|
||||
code: 'MOBILE'
|
||||
name: 'Mobile Channel'
|
||||
hostname: 'mobile.localhost'
|
||||
description: 'Lorem ipsum'
|
||||
baseCurrency: '@currency_usd'
|
||||
defaultLocale: '@locale_en'
|
||||
locales: ['@locale_en']
|
||||
color: 'blue'
|
||||
enabled: true
|
||||
taxCalculationStrategy: 'order_items_based'
|
||||
|
||||
Sylius\Component\Currency\Model\Currency:
|
||||
currency_usd:
|
||||
code: 'USD'
|
||||
|
||||
Sylius\Component\Locale\Model\Locale:
|
||||
locale_en:
|
||||
code: 'en_US'
|
||||
|
||||
Sylius\Component\Core\Model\Product:
|
||||
product_in_web_channel:
|
||||
code: 'MUG'
|
||||
channels: ['@channel_web']
|
||||
currentLocale: 'en_US'
|
||||
product_in_mobile_channel_only:
|
||||
code: 'T_SHIRT'
|
||||
channels: ['@channel_mobile']
|
||||
currentLocale: 'en_US'
|
||||
|
||||
Sylius\Component\Core\Model\ProductTranslation:
|
||||
product_translation_mug_en:
|
||||
slug: 'mug'
|
||||
locale: 'en_US'
|
||||
name: 'Mug'
|
||||
description: 'A mug'
|
||||
shortDescription: 'Mug'
|
||||
translatable: '@product_in_web_channel'
|
||||
product_translation_tshirt_en:
|
||||
slug: 't-shirt'
|
||||
locale: 'en_US'
|
||||
name: 'T-Shirt'
|
||||
description: 'A t-shirt'
|
||||
shortDescription: 'T-Shirt'
|
||||
translatable: '@product_in_mobile_channel_only'
|
||||
|
|
@ -37,6 +37,34 @@ final class ProductsTest extends JsonApiTestCase
|
|||
$this->assertResponseCode($response, Response::HTTP_MOVED_PERMANENTLY);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_returns_not_found_response_when_getting_product_not_in_current_channel(): void
|
||||
{
|
||||
$this->loadFixturesFromFile('product/product_not_in_channel.yaml');
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: '/api/v2/shop/products/T_SHIRT',
|
||||
server: self::CONTENT_TYPE_HEADER,
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_returns_product_from_current_channel(): void
|
||||
{
|
||||
$this->loadFixturesFromFile('product/product_not_in_channel.yaml');
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: '/api/v2/shop/products/MUG',
|
||||
server: self::CONTENT_TYPE_HEADER,
|
||||
);
|
||||
|
||||
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_OK);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_returns_product_with_translations_in_default_locale(): void
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue