mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
feature #15068 View only available associations for product (TheMilek)
This PR was merged into the 1.13 branch.
Discussion
----------
| Q | A |
|-----------------|--------------------------------------------------------------|
| Branch? | 1.13 <!-- see the comment below --> |
| Bug fix? | yes |
| New feature? | no |
| BC breaks? | no |
| Deprecations? | no <!-- don't forget to update the UPGRADE-*.md file --> |
| License | MIT |
<!--
- Bug fixes must be submitted against the 1.12 branch
- Features and deprecations must be submitted against the 1.13 branch
- Make sure that the correct base branch is set
To be sure you are not breaking any Backward Compatibilities, check the documentation:
https://docs.sylius.com/en/latest/book/organization/backward-compatibility-promise.html
-->
Commits
-------
ebc7d88b08 View only available associations for product
This commit is contained in:
commit
79fba64c17
11 changed files with 541 additions and 8 deletions
|
|
@ -640,12 +640,9 @@ final class ProductContext implements Context
|
|||
|
||||
foreach ($associations as $association) {
|
||||
$associationResponse = $this->client->showByIri($association);
|
||||
$associationTypeIri = $this->responseChecker->getValue($associationResponse, 'type');
|
||||
|
||||
if ($associationResponse->getStatusCode() === 200) {
|
||||
$associationTypeIri = $this->responseChecker->getValue($associationResponse, 'type');
|
||||
|
||||
Assert::notSame($associationTypeIri, $productAssociationTypeIri);
|
||||
}
|
||||
Assert::notSame($associationTypeIri, $productAssociationTypeIri);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ final class ProductItemDataProvider implements RestrictedDataProviderInterface,
|
|||
/** @var ChannelInterface $channel */
|
||||
$channel = $context[ContextKeys::CHANNEL];
|
||||
|
||||
return $this->productRepository->findOneByChannelAndCode($channel, $id);
|
||||
return $this->productRepository->findOneByChannelAndCodeWithAvailableAssociations($channel, $id);
|
||||
}
|
||||
|
||||
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* 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\ApiBundle\Doctrine\QueryCollectionExtension;
|
||||
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\ContextAwareQueryCollectionExtensionInterface;
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
|
||||
/** @experimental */
|
||||
final class AvailableProductAssociationsInProductCollectionExtension implements ContextAwareQueryCollectionExtensionInterface
|
||||
{
|
||||
public function __construct(private UserContextInterface $userContext)
|
||||
{
|
||||
}
|
||||
|
||||
public function applyToCollection(
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
string $resourceClass,
|
||||
string $operationName = null,
|
||||
array $context = [],
|
||||
): void {
|
||||
if (!is_a($resourceClass, ProductInterface::class, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->userContext->getUser();
|
||||
if ($user !== null && in_array('ROLE_API_ACCESS', $user->getRoles(), true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootAlias = $queryBuilder->getRootAliases()[0];
|
||||
$enabledParameterName = $queryNameGenerator->generateParameterName('enabled');
|
||||
$associationAliasName = $queryNameGenerator->generateJoinAlias('association');
|
||||
$productAssociationAliasName = $queryNameGenerator->generateJoinAlias('associatedProduct');
|
||||
|
||||
$queryBuilder
|
||||
->addSelect($rootAlias)
|
||||
->addSelect($associationAliasName)
|
||||
->leftJoin(sprintf('%s.associations', $rootAlias), $associationAliasName)
|
||||
->leftJoin(
|
||||
sprintf('%s.associatedProducts', $associationAliasName),
|
||||
$productAssociationAliasName,
|
||||
'WITH',
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq(sprintf('%s.enabled', $productAssociationAliasName), 'true'),
|
||||
$queryBuilder->expr()->eq(sprintf('%s.owner', $associationAliasName), $rootAlias),
|
||||
),
|
||||
)
|
||||
->andWhere(sprintf('%s.associations IS EMPTY OR %s.id IS NOT NULL', $rootAlias, $productAssociationAliasName))
|
||||
->andWhere(sprintf('%s.enabled = :%s', $rootAlias, $enabledParameterName))
|
||||
->setParameter($enabledParameterName, true)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,11 @@
|
|||
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\Doctrine\QueryCollectionExtension\AvailableProductAssociationsInProductCollectionExtension">
|
||||
<argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />
|
||||
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ApiBundle\Doctrine\QueryCollectionExtension\OrdersByChannelExtension">
|
||||
<argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />
|
||||
<tag name="api_platform.doctrine.orm.query_extension.collection" />
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ final class ProductItemDataProviderSpec extends ObjectBehavior
|
|||
$userContext->getUser()->willReturn($user);
|
||||
$user->getRoles()->willReturn([]);
|
||||
|
||||
$productRepository->findOneByChannelAndCode($channel, 'FORD_FOCUS')->willReturn($product);
|
||||
$productRepository->findOneByChannelAndCodeWithAvailableAssociations($channel, 'FORD_FOCUS')->willReturn($product);
|
||||
|
||||
$this
|
||||
->getItem(
|
||||
|
|
@ -95,7 +95,7 @@ final class ProductItemDataProviderSpec extends ObjectBehavior
|
|||
): void {
|
||||
$userContext->getUser()->willReturn(null);
|
||||
|
||||
$productRepository->findOneByChannelAndCode($channel, 'FORD_FOCUS')->willReturn($product);
|
||||
$productRepository->findOneByChannelAndCodeWithAvailableAssociations($channel, 'FORD_FOCUS')->willReturn($product);
|
||||
|
||||
$this
|
||||
->getItem(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Paweł Jędrzejewski
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\ApiBundle\Doctrine\QueryCollectionExtension;
|
||||
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\ContextAwareQueryCollectionExtensionInterface;
|
||||
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\ProductInterface;
|
||||
use Sylius\Component\Core\Model\TaxonInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
final class AvailableProductAssociationsInProductCollectionExtensionSpec extends ObjectBehavior
|
||||
{
|
||||
function let(UserContextInterface $userContext): void
|
||||
{
|
||||
$this->beConstructedWith($userContext);
|
||||
}
|
||||
|
||||
public function it_is_a_constraint_validator()
|
||||
{
|
||||
$this->shouldHaveType(ContextAwareQueryCollectionExtensionInterface::class);
|
||||
}
|
||||
|
||||
function it_does_nothing_if_current_resource_is_not_a_product(
|
||||
UserContextInterface $userContext,
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
): void {
|
||||
$userContext->getUser()->shouldNotBeCalled();
|
||||
$queryBuilder->getRootAliases()->shouldNotBeCalled();
|
||||
|
||||
$this->applyToCollection($queryBuilder, $queryNameGenerator, TaxonInterface::class, 'get', []);
|
||||
}
|
||||
|
||||
function it_does_nothing_if_current_user_is_an_admin_user(
|
||||
UserContextInterface $userContext,
|
||||
UserInterface $user,
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
): void {
|
||||
$userContext->getUser()->willReturn($user);
|
||||
$user->getRoles()->willReturn(['ROLE_API_ACCESS']);
|
||||
|
||||
$queryBuilder->getRootAliases()->shouldNotBeCalled();
|
||||
|
||||
$this->applyToCollection($queryBuilder, $queryNameGenerator, ProductInterface::class, 'get', []);
|
||||
}
|
||||
|
||||
function it_filters_products_by_available_associations(
|
||||
UserContextInterface $userContext,
|
||||
UserInterface $user,
|
||||
QueryBuilder $queryBuilder,
|
||||
QueryNameGeneratorInterface $queryNameGenerator,
|
||||
ChannelInterface $channel,
|
||||
Expr $expr,
|
||||
Expr\Comparison $comparison,
|
||||
Andx $andx,
|
||||
): void {
|
||||
$userContext->getUser()->willReturn($user);
|
||||
$user->getRoles()->willReturn([]);
|
||||
|
||||
$queryNameGenerator->generateParameterName('enabled')->shouldBeCalled()->willReturn('enabled');
|
||||
$queryNameGenerator->generateJoinAlias('association')->shouldBeCalled()->willReturn('association');
|
||||
$queryNameGenerator->generateJoinAlias('associatedProduct')->shouldBeCalled()->willReturn('associatedProduct');
|
||||
|
||||
$queryBuilder->getRootAliases()->willReturn(['o']);
|
||||
$queryBuilder->addSelect('o')->shouldBeCalled()->willReturn($queryBuilder);
|
||||
$queryBuilder->addSelect('association')->shouldBeCalled()->willReturn($queryBuilder);
|
||||
$queryBuilder->leftJoin('o.associations', 'association')->shouldBeCalled()->willReturn($queryBuilder);
|
||||
$expr->andX(Argument::type(Expr\Comparison::class), Argument::type(Expr\Comparison::class))->willReturn($andx);
|
||||
$expr->eq('associatedProduct.enabled', 'true')->shouldBeCalled()->willReturn($comparison);
|
||||
$expr->eq('association.owner', 'o')->shouldBeCalled()->willReturn($comparison);
|
||||
$queryBuilder->expr()->willReturn($expr->getWrappedObject());
|
||||
$queryBuilder->leftJoin('association.associatedProducts', 'associatedProduct', 'WITH', Argument::type(Andx::class))->shouldBeCalled()->willReturn($queryBuilder);
|
||||
$queryBuilder->andWhere('o.associations IS EMPTY OR associatedProduct.id IS NOT NULL')->shouldBeCalled()->willReturn($queryBuilder);
|
||||
$queryBuilder->andWhere('o.enabled = :enabled')->shouldBeCalled()->willReturn($queryBuilder);
|
||||
$queryBuilder->setParameter('enabled', true)->shouldBeCalled()->willReturn($queryBuilder);
|
||||
|
||||
$this->applyToCollection($queryBuilder, $queryNameGenerator, ProductInterface::class, 'get', []);
|
||||
}
|
||||
}
|
||||
|
|
@ -232,4 +232,39 @@ class ProductRepository extends BaseProductRepository implements ProductReposito
|
|||
->getResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function findOneByChannelAndCodeWithAvailableAssociations(ChannelInterface $channel, string $code): ?ProductInterface
|
||||
{
|
||||
$product = $this->createQueryBuilder('o')
|
||||
->addSelect('association')
|
||||
->leftJoin('o.associations', 'association')
|
||||
->innerJoin('association.associatedProducts', 'associatedProduct', 'WITH', 'associatedProduct.enabled = :enabled')
|
||||
->where('o.code = :code')
|
||||
->andWhere(':channel MEMBER OF o.channels')
|
||||
->andWhere('o.enabled = :enabled')
|
||||
->setParameter('channel', $channel)
|
||||
->setParameter('code', $code)
|
||||
->setParameter('enabled', true)
|
||||
->getQuery()
|
||||
->getOneOrNullResult()
|
||||
;
|
||||
|
||||
if (null === $product) {
|
||||
$product = $this->findOneByChannelAndCode($channel, $code);
|
||||
|
||||
$product?->getAssociations()->clear();
|
||||
}
|
||||
|
||||
$this->associationHydrator->hydrateAssociations($product, [
|
||||
'images',
|
||||
'options',
|
||||
'options.translations',
|
||||
'variants',
|
||||
'variants.channelPricings',
|
||||
'variants.optionValues',
|
||||
'variants.optionValues.translations',
|
||||
]);
|
||||
|
||||
return $product;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@ interface ProductRepositoryInterface extends BaseProductRepositoryInterface
|
|||
public function findOneByCode(string $code): ?ProductInterface;
|
||||
|
||||
public function findByTaxon(TaxonInterface $taxon): array;
|
||||
|
||||
public function findOneByChannelAndCodeWithAvailableAssociations(ChannelInterface $channel, string $code): ?ProductInterface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
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'
|
||||
|
||||
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_mug:
|
||||
code: 'MUG'
|
||||
channels: ['@channel_web']
|
||||
currentLocale: 'en_US'
|
||||
translations:
|
||||
en_US: '@product_translation_mug_en_US'
|
||||
options: ['@product_option_color']
|
||||
product_cup:
|
||||
code: 'CUP'
|
||||
channels: [ '@channel_web' ]
|
||||
currentLocale: 'en_US'
|
||||
translations:
|
||||
en_US: '@product_translation_cup_en_US'
|
||||
product_hat:
|
||||
code: 'HAT'
|
||||
channels: [ '@channel_web' ]
|
||||
currentLocale: 'en_US'
|
||||
translations:
|
||||
en_US: '@product_translation_hat_en_US'
|
||||
enabled: false
|
||||
|
||||
Sylius\Component\Core\Model\ProductTranslation:
|
||||
product_translation_mug_en_US:
|
||||
slug: 'mug'
|
||||
locale: 'en_US'
|
||||
name: 'Mug'
|
||||
description: 'This is a mug'
|
||||
shortDescription: 'Short mug description'
|
||||
translatable: '@product_mug'
|
||||
product_translation_cup_en_US:
|
||||
slug: 'Cup'
|
||||
locale: 'en_US'
|
||||
name: 'Cup'
|
||||
description: 'Short cup description'
|
||||
shortDescription: 'Cup'
|
||||
translatable: '@product_cup'
|
||||
product_translation_hat_en_US:
|
||||
slug: 'Hat'
|
||||
locale: 'en_US'
|
||||
name: 'Hat'
|
||||
description: 'Short hat description'
|
||||
shortDescription: 'Hat'
|
||||
translatable: '@product_hat'
|
||||
|
||||
Sylius\Component\Core\Model\ProductVariant:
|
||||
product_variant_mug_blue:
|
||||
code: 'MUG_BLUE'
|
||||
product: '@product_mug'
|
||||
currentLocale: 'en_US'
|
||||
translations:
|
||||
en_US: '@product_variant_mug_blue_translation_en_EN'
|
||||
optionValues: ['@product_option_value_color_blue']
|
||||
channelPricings:
|
||||
WEB: '@channel_pricing_product_variant_mug_blue_web'
|
||||
|
||||
product_variant_mug_red:
|
||||
code: 'MUG_RED'
|
||||
product: '@product_mug'
|
||||
currentLocale: 'en_US'
|
||||
translations:
|
||||
en_US: '@product_variant_mug_red_translation_en_EN'
|
||||
optionValues: ['@product_option_value_color_red']
|
||||
channelPricings:
|
||||
WEB: '@channel_pricing_product_variant_mug_red_web'
|
||||
|
||||
Sylius\Component\Product\Model\ProductVariantTranslation:
|
||||
product_variant_mug_blue_translation_en_EN:
|
||||
locale: 'en_US'
|
||||
name: 'Blue Mug'
|
||||
translatable: '@product_variant_mug_blue'
|
||||
product_variant_mug_red_translation_en_EN:
|
||||
locale: 'en_US'
|
||||
name: 'Red Mug'
|
||||
translatable: '@product_variant_mug_red'
|
||||
|
||||
Sylius\Component\Core\Model\ChannelPricing:
|
||||
channel_pricing_product_variant_mug_blue_web:
|
||||
channelCode: 'WEB'
|
||||
price: 2000
|
||||
originalPrice: 3000
|
||||
channel_pricing_product_variant_mug_red_web:
|
||||
channelCode: 'WEB'
|
||||
price: 2000
|
||||
|
||||
Sylius\Component\Product\Model\ProductOption:
|
||||
product_option_color:
|
||||
code: 'COLOR'
|
||||
currentLocale: 'en_US'
|
||||
translations:
|
||||
- '@product_option_translation_en_EN'
|
||||
|
||||
Sylius\Component\Product\Model\ProductOptionTranslation:
|
||||
product_option_translation_en_EN:
|
||||
locale: 'en_US'
|
||||
name: 'Color'
|
||||
translatable: '@product_option_color'
|
||||
|
||||
Sylius\Component\Product\Model\ProductOptionValue:
|
||||
product_option_value_color_blue:
|
||||
code: 'COLOR_BLUE'
|
||||
currentLocale: 'en_US'
|
||||
fallbackLocale: 'en_US'
|
||||
option: '@product_option_color'
|
||||
translations:
|
||||
- '@product_option_value_translation_blue'
|
||||
product_option_value_color_red:
|
||||
code: 'COLOR_RED'
|
||||
currentLocale: 'en_US'
|
||||
fallbackLocale: 'en_US'
|
||||
option: '@product_option_color'
|
||||
translations:
|
||||
- '@product_option_value_translation_red'
|
||||
|
||||
Sylius\Component\Product\Model\ProductOptionValueTranslation:
|
||||
product_option_value_translation_blue:
|
||||
locale: 'en_US'
|
||||
value: 'Blue'
|
||||
translatable: '@product_option_value_color_blue'
|
||||
product_option_value_translation_red:
|
||||
locale: 'en_US'
|
||||
value: 'Red'
|
||||
translatable: '@product_option_value_color_red'
|
||||
|
||||
Sylius\Component\Product\Model\ProductAssociation:
|
||||
product_association:
|
||||
type: '@product_association_type'
|
||||
owner: '@product_mug'
|
||||
associatedProducts: ['@product_cup']
|
||||
another_product_association:
|
||||
type: '@another_product_association_type'
|
||||
owner: '@product_mug'
|
||||
associatedProducts: ['@product_hat']
|
||||
|
||||
Sylius\Component\Product\Model\ProductAssociationType:
|
||||
product_association_type:
|
||||
code: 'similar_products'
|
||||
translations:
|
||||
en_US: '@product_association_type_translation'
|
||||
another_product_association_type:
|
||||
code: 'another_similar_products'
|
||||
translations:
|
||||
en_US: '@another_product_association_type_translation'
|
||||
|
||||
Sylius\Component\Product\Model\ProductAssociationTypeTranslation:
|
||||
product_association_type_translation:
|
||||
name: 'Similar products'
|
||||
locale: 'en_US'
|
||||
translatable: '@product_association_type'
|
||||
another_product_association_type_translation:
|
||||
name: 'Another similar products'
|
||||
locale: 'en_US'
|
||||
translatable: '@another_product_association_type'
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
{
|
||||
"@context": "\/api\/v2\/contexts\/Product",
|
||||
"@id": "\/api\/v2\/shop\/products",
|
||||
"@type": "hydra:Collection",
|
||||
"hydra:member": [
|
||||
{
|
||||
"@id": "\/api\/v2\/shop\/products\/CUP",
|
||||
"@type": "Product",
|
||||
"productTaxons": [],
|
||||
"mainTaxon": null,
|
||||
"reviews": [],
|
||||
"averageRating": @integer@,
|
||||
"images": [],
|
||||
"id": @integer@,
|
||||
"code": "CUP",
|
||||
"variants": [],
|
||||
"options": [],
|
||||
"associations": [],
|
||||
"createdAt": @date@,
|
||||
"updatedAt": @date@,
|
||||
"shortDescription": "Cup",
|
||||
"name": "Cup",
|
||||
"description": @string@,
|
||||
"slug": "Cup",
|
||||
"defaultVariant": null
|
||||
},
|
||||
{
|
||||
"@id": "\/api\/v2\/shop\/products\/MUG",
|
||||
"@type": "Product",
|
||||
"productTaxons": [],
|
||||
"mainTaxon": null,
|
||||
"reviews": [],
|
||||
"averageRating": @integer@,
|
||||
"images": [],
|
||||
"id": @integer@,
|
||||
"code": "MUG",
|
||||
"variants": [
|
||||
"\/api\/v2\/shop\/product-variants\/MUG_BLUE",
|
||||
"\/api\/v2\/shop\/product-variants\/MUG_RED"
|
||||
],
|
||||
"options": [
|
||||
"\/api\/v2\/shop\/product-options\/COLOR"
|
||||
],
|
||||
"createdAt": @date@,
|
||||
"updatedAt": @date@,
|
||||
"shortDescription": "Short mug description",
|
||||
"name": "Mug",
|
||||
"description": @string@,
|
||||
"slug": "mug",
|
||||
"associations": [
|
||||
"\/api\/v2\/shop\/product-associations\/@integer@"
|
||||
],
|
||||
"defaultVariant": "\/api\/v2\/shop\/product-variants\/MUG_BLUE"
|
||||
}
|
||||
],
|
||||
"hydra:totalItems": 2,
|
||||
"hydra:search": {
|
||||
"@type": "hydra:IriTemplate",
|
||||
"hydra:template": "\/api\/v2\/shop\/products{?translations.name,order[code],order[createdAt],productTaxons.taxon.code,productTaxons.taxon.code[],order[price],order[translation.name],localeCode for order[translation.name],taxon}",
|
||||
"hydra:variableRepresentation": "BasicRepresentation",
|
||||
"hydra:mapping": [
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "translations.name",
|
||||
"property": "translations.name",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "order[code]",
|
||||
"property": "code",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "order[createdAt]",
|
||||
"property": "createdAt",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "productTaxons.taxon.code",
|
||||
"property": "productTaxons.taxon.code",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "productTaxons.taxon.code[]",
|
||||
"property": "productTaxons.taxon.code",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "order[price]",
|
||||
"property": "price",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "order[translation.name]",
|
||||
"property": "translation",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "localeCode for order[translation.name]",
|
||||
"property": "localeCode",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"@type": "IriTemplateMapping",
|
||||
"variable": "taxon",
|
||||
"property": null,
|
||||
"required": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -97,6 +97,42 @@ final class ProductsTest extends JsonApiTestCase
|
|||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_returns_products_collection_with_only_available_associations(): void
|
||||
{
|
||||
$this->loadFixturesFromFile('product/products_with_associations.yaml');
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: '/api/v2/shop/products',
|
||||
server: self::CONTENT_TYPE_HEADER,
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'shop/product/get_products_collection_with_associations_response',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_returns_product_item_with_only_available_associations(): void
|
||||
{
|
||||
$this->loadFixturesFromFile('product/products_with_associations.yaml');
|
||||
|
||||
$this->client->request(
|
||||
method: 'GET',
|
||||
uri: '/api/v2/shop/products/MUG',
|
||||
server: self::CONTENT_TYPE_HEADER,
|
||||
);
|
||||
|
||||
$this->assertResponse(
|
||||
$this->client->getResponse(),
|
||||
'shop/product/get_product_with_default_locale_translation',
|
||||
Response::HTTP_OK,
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_returns_product_attributes_collection_with_translations_in_locale_from_header(): void
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue