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
|
## 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`
|
# UPGRADE FROM `2.1` TO `2.2`
|
||||||
|
|
||||||
## Telemetry
|
## 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`
|
# UPGRADE FROM `2.1` TO `2.2`
|
||||||
|
|
||||||
## Modified routes
|
## Modified routes
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ interface ResponseCheckerInterface
|
||||||
|
|
||||||
public function hasViolationWithMessage(Response $response, string $message, ?string $property = null): bool;
|
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;
|
public function cleanErrors(): void;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,17 @@ sylius_twig_hooks:
|
||||||
template: '@SyliusAdmin/order/show/content/sections/shipments/item/actions.html.twig'
|
template: '@SyliusAdmin/order/show/content/sections/shipments/item/actions.html.twig'
|
||||||
priority: 0
|
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':
|
'sylius_admin.order.show.content.sections.shipments.item.actions':
|
||||||
ship:
|
ship:
|
||||||
component: 'sylius_admin:shipment:ship_form'
|
component: 'sylius_admin:shipment:ship_form'
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,3 @@
|
||||||
{% set shipment = hookable_metadata.context.shipment %}
|
|
||||||
|
|
||||||
<td class="px-3 py-2 text-nowrap">
|
<td class="px-3 py-2 text-nowrap">
|
||||||
<div class="py-2">
|
{% hook 'general' %}
|
||||||
<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 %}
|
|
||||||
</td>
|
</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;
|
namespace Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
|
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
|
||||||
|
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
|
||||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
|
@ -21,9 +22,8 @@ use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiSection;
|
||||||
use Sylius\Bundle\ApiBundle\Serializer\ContextKeys;
|
use Sylius\Bundle\ApiBundle\Serializer\ContextKeys;
|
||||||
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
|
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
|
||||||
use Sylius\Component\Core\Model\ProductInterface;
|
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)
|
public function __construct(private SectionProviderInterface $sectionProvider)
|
||||||
{
|
{
|
||||||
|
|
@ -38,6 +38,33 @@ final readonly class ChannelAndLocaleBasedExtension implements QueryCollectionEx
|
||||||
string $resourceClass,
|
string $resourceClass,
|
||||||
?Operation $operation = null,
|
?Operation $operation = null,
|
||||||
array $context = [],
|
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 {
|
): void {
|
||||||
if (!is_a($resourceClass, ProductInterface::class, true)) {
|
if (!is_a($resourceClass, ProductInterface::class, true)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -47,8 +74,9 @@ final readonly class ChannelAndLocaleBasedExtension implements QueryCollectionEx
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert::keyExists($context, ContextKeys::CHANNEL);
|
if (!isset($context[ContextKeys::CHANNEL]) || !isset($context[ContextKeys::LOCALE_CODE])) {
|
||||||
Assert::keyExists($context, ContextKeys::LOCALE_CODE);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$channel = $context[ContextKeys::CHANNEL];
|
$channel = $context[ContextKeys::CHANNEL];
|
||||||
$localeCode = $context[ContextKeys::LOCALE_CODE];
|
$localeCode = $context[ContextKeys::LOCALE_CODE];
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
>
|
>
|
||||||
<argument type="service" id="sylius.section_resolver.uri_based" />
|
<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.collection" />
|
||||||
|
<tag name="api_platform.doctrine.orm.query_extension.item" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,8 @@ final readonly class ItemProvider implements ProviderInterface
|
||||||
|
|
||||||
$customer = $order->getCustomer();
|
$customer = $order->getCustomer();
|
||||||
|
|
||||||
return null === $customer
|
return null === $customer ||
|
||||||
|| null === $customer->getUser()
|
null === $customer->getUser() ||
|
||||||
|| $order->isCreatedByGuest();
|
$order->isCreatedByGuest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ namespace Tests\Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product
|
||||||
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use InvalidArgumentException;
|
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Sylius\Bundle\ApiBundle\Doctrine\ORM\QueryExtension\Shop\Product\ChannelAndLocaleBasedExtension;
|
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->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->extension->applyToCollection(
|
||||||
$this->queryBuilder,
|
$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());
|
$this->sectionProvider->method('getSection')->willReturn(new ShopApiSection());
|
||||||
|
|
||||||
$channel = $this->createMock(ChannelInterface::class);
|
$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->extension->applyToCollection(
|
||||||
$this->queryBuilder,
|
$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'
|
template: '@SyliusShop/shared/layout/base/footer/content/copy.html.twig'
|
||||||
priority: 0
|
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':
|
'sylius_shop.base.offcanvas':
|
||||||
cart:
|
cart:
|
||||||
component: 'sylius_shop:cart:widget:offcanvas'
|
component: 'sylius_shop:cart:widget:offcanvas'
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
id="sylius_shop.twig.component.product.summary"
|
id="sylius_shop.twig.component.product.summary"
|
||||||
class="Sylius\Bundle\ShopBundle\Twig\Component\Product\SummaryComponent"
|
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" />
|
||||||
<argument type="service" id="sylius.repository.product_variant" />
|
<argument type="service" id="sylius.repository.product_variant" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ class FormComponent
|
||||||
/** @var OrderInterface|null $order */
|
/** @var OrderInterface|null $order */
|
||||||
$order = $this->repository->find($value);
|
$order = $this->repository->find($value);
|
||||||
|
|
||||||
if (!$order instanceof OrderInterface
|
if (!$order instanceof OrderInterface ||
|
||||||
|| $order->getCheckoutState() === OrderCheckoutStates::STATE_COMPLETED
|
$order->getCheckoutState() === OrderCheckoutStates::STATE_COMPLETED
|
||||||
) {
|
) {
|
||||||
return $this->createResource();
|
return $this->createResource();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,5 @@
|
||||||
<div class="col-12 col-lg-6 order-lg-2">
|
<div class="col-12 col-lg-6 order-lg-2">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-sm-6">
|
{% hook 'menu' %}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</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="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="mb-3 fw-medium">Payment methods:</div>
|
||||||
|
{% hook 'payment_methods' %}
|
||||||
<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>
|
|
||||||
</div>
|
</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
|
public function build(ContainerBuilder $container): void
|
||||||
{
|
{
|
||||||
$container->addCompilerPass(new LiveComponentTagPass(), priority: 500);
|
$container->addCompilerPass(new LiveComponentTagPass(), priority: 50);
|
||||||
$container->addCompilerPass(new TwigComponentTagPass(), priority: 500);
|
$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);
|
$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]
|
#[Test]
|
||||||
public function it_returns_product_with_translations_in_default_locale(): void
|
public function it_returns_product_with_translations_in_default_locale(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue