Skip to content

Commit 4eb24ac

Browse files
authored
Merge pull request #121 from sebajps/add-combinations-endpoint
Add combinations endpoint
2 parents 440a2f0 + ea599e2 commit 4eb24ac

File tree

11 files changed

+773
-4
lines changed

11 files changed

+773
-4
lines changed

config/admin/services.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ services:
3333
tags:
3434
- { name: 'console.command' }
3535

36+
# Custom normalizers for commands/queries that are very specific and cannot rely on generic solutions from the core
37+
PrestaShop\Module\APIResources\ApiPlatform\Normalizer\GenerateCombinationsSerializer:
38+
autowire: true
39+
autoconfigure: true
40+
public: false
41+
3642
PrestaShop\Module\APIResources\ApiPlatform\Normalizer\StateIdInterfaceNormalizer:
3743
tags:
3844
- { name: 'serializer.normalizer', priority: 100 }
@@ -54,8 +60,6 @@ services:
5460
public: false
5561
arguments:
5662
$decorated: '@serializer.normalizer.object'
57-
tags:
58-
- { name: 'serializer.normalizer', priority: 300 }
5963

6064
PrestaShopBundle\ApiPlatform\Serializer\CQRSApiSerializer:
6165
class: PrestaShop\Module\APIResources\Serializer\QueryParameterTypeCastSerializer

ps_apiresources.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function __construct()
3737
$this->description = $this->trans('Includes the resources allowing using the API for the PrestaShop domain, all endpoints are based on CQRS commands/queries from the Core and we APIPlatform framework is used as a base.', [], 'Modules.Apiresources.Admin');
3838
$this->author = 'PrestaShop';
3939
$this->version = '0.3.0';
40-
$this->ps_versions_compliancy = ['min' => '9.0.2', 'max' => _PS_VERSION_];
40+
$this->ps_versions_compliancy = ['min' => '9.0.3', 'max' => _PS_VERSION_];
4141
$this->need_instance = 0;
4242
$this->tab = 'administration';
4343
parent::__construct();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright since 2007 PrestaShop SA and Contributors
4+
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5+
*
6+
* NOTICE OF LICENSE
7+
*
8+
* This source file is subject to the Academic Free License version 3.0
9+
* that is bundled with this package in the file LICENSE.md.
10+
* It is also available through the world-wide-web at this URL:
11+
* https://opensource.org/licenses/AFL-3.0
12+
* If you did not receive a copy of the license and are unable to
13+
* obtain it through the world-wide-web, please send an email
14+
* to [email protected] so we can send you a copy immediately.
15+
*
16+
* @author PrestaShop SA and Contributors <[email protected]>
17+
* @copyright Since 2007 PrestaShop SA and Contributors
18+
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
19+
*/
20+
21+
namespace PrestaShop\Module\APIResources\ApiPlatform\Normalizer;
22+
23+
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Command\GenerateProductCombinationsCommand;
24+
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
25+
use PrestaShopBundle\ApiPlatform\Normalizer\ShopConstraintNormalizer;
26+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
27+
28+
class GenerateCombinationsSerializer implements DenormalizerInterface
29+
{
30+
public function __construct(
31+
private readonly ShopConstraintNormalizer $shopConstraintNormalizer,
32+
) {
33+
}
34+
35+
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = [])
36+
{
37+
$groupedAttributes = [];
38+
foreach ($data['groupedAttributes'] as $attributeGroup) {
39+
$groupedAttributes[$attributeGroup['attributeGroupId']] = array_map(static function ($attributeId): int {
40+
return (int) $attributeId;
41+
}, $attributeGroup['attributeIds']);
42+
}
43+
44+
return new GenerateProductCombinationsCommand(
45+
$data['productId'],
46+
$groupedAttributes,
47+
$this->shopConstraintNormalizer->denormalize($data['_context']['shopConstraint'], ShopConstraint::class),
48+
);
49+
}
50+
51+
public function supportsDenormalization(mixed $data, string $type, ?string $format = null)
52+
{
53+
return $type === GenerateProductCombinationsCommand::class;
54+
}
55+
56+
public function getSupportedTypes(?string $format): array
57+
{
58+
return [
59+
GenerateProductCombinationsCommand::class => true,
60+
'object' => null,
61+
'*' => null,
62+
];
63+
}
64+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
/**
3+
* Copyright since 2007 PrestaShop SA and Contributors
4+
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5+
*
6+
* NOTICE OF LICENSE
7+
*
8+
* This source file is subject to the Academic Free License version 3.0
9+
* that is bundled with this package in the file LICENSE.md.
10+
* It is also available through the world-wide-web at this URL:
11+
* https://opensource.org/licenses/AFL-3.0
12+
* If you did not receive a copy of the license and are unable to
13+
* obtain it through the world-wide-web, please send an email
14+
* to [email protected] so we can send you a copy immediately.
15+
*
16+
* @author PrestaShop SA and Contributors <[email protected]>
17+
* @copyright Since 2007 PrestaShop SA and Contributors
18+
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product;
24+
25+
use ApiPlatform\Metadata\ApiProperty;
26+
use ApiPlatform\Metadata\ApiResource;
27+
use PrestaShop\Decimal\DecimalNumber;
28+
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Exception\CombinationNotFoundException;
29+
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Query\GetCombinationForEditing;
30+
use PrestaShopBundle\ApiPlatform\Metadata\CQRSGet;
31+
use Symfony\Component\HttpFoundation\Response;
32+
33+
#[ApiResource(
34+
operations: [
35+
new CQRSGet(
36+
uriTemplate: '/products/combinations/{combinationId}',
37+
CQRSQuery: GetCombinationForEditing::class,
38+
scopes: [
39+
'product_read',
40+
],
41+
CQRSQueryMapping: self::QUERY_MAPPING,
42+
),
43+
],
44+
exceptionToStatus: [
45+
CombinationNotFoundException::class => Response::HTTP_NOT_FOUND,
46+
],
47+
)]
48+
class Combination
49+
{
50+
public int $productId;
51+
52+
#[ApiProperty(identifier: true)]
53+
public int $combinationId;
54+
public string $name;
55+
public bool $default;
56+
57+
public string $gtin;
58+
public string $isbn;
59+
public string $mpn;
60+
public string $reference;
61+
public string $upc;
62+
63+
public string $coverThumbnailUrl;
64+
#[ApiProperty(openapiContext: ['type' => 'array', 'description' => 'List of image IDs', 'items' => ['type' => 'integer'], 'example' => [1, 3]])]
65+
public array $imageIds;
66+
67+
public DecimalNumber $impactOnPriceTaxExcluded;
68+
public DecimalNumber $impactOnPriceTaxIncluded;
69+
public DecimalNumber $impactOnUnitPrice;
70+
public DecimalNumber $impactOnUnitPriceTaxIncluded;
71+
public DecimalNumber $ecotaxTaxExcluded;
72+
public DecimalNumber $ecotaxTaxIncluded;
73+
public DecimalNumber $impactOnWeight;
74+
public DecimalNumber $wholesalePrice;
75+
public DecimalNumber $productTaxRate;
76+
public DecimalNumber $productPriceTaxExcluded;
77+
public DecimalNumber $productEcotaxTaxExcluded;
78+
79+
public int $quantity;
80+
81+
public const QUERY_MAPPING = [
82+
'[_context][shopConstraint]' => '[shopConstraint]',
83+
'[details][gtin]' => '[gtin]',
84+
'[details][isbn]' => '[isbn]',
85+
'[details][mpn]' => '[mpn]',
86+
'[details][reference]' => '[reference]',
87+
'[details][upc]' => '[upc]',
88+
'[details][impactOnWeight]' => '[impactOnWeight]',
89+
'[prices][impactOnPrice]' => '[impactOnPriceTaxExcluded]',
90+
'[prices][impactOnPriceTaxIncluded]' => '[impactOnPriceTaxIncluded]',
91+
'[prices][impactOnUnitPrice]' => '[impactOnUnitPriceTaxExcluded]',
92+
'[prices][impactOnUnitPriceTaxIncluded]' => '[impactOnUnitPriceTaxIncluded]',
93+
'[prices][ecotax]' => '[ecotaxTaxExcluded]',
94+
'[prices][ecotaxTaxIncluded]' => '[ecotaxTaxIncluded]',
95+
'[prices][wholesalePrice]' => '[wholesalePrice]',
96+
'[prices][productTaxRate]' => '[productTaxRate]',
97+
'[prices][productPrice]' => '[productPriceTaxExcluded]',
98+
'[prices][productEcotax]' => '[productEcotaxTaxExcluded]',
99+
'[stock][quantity]' => '[quantity]',
100+
];
101+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
/**
3+
* Copyright since 2007 PrestaShop SA and Contributors
4+
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5+
*
6+
* NOTICE OF LICENSE
7+
*
8+
* This source file is subject to the Academic Free License version 3.0
9+
* that is bundled with this package in the file LICENSE.md.
10+
* It is also available through the world-wide-web at this URL:
11+
* https://opensource.org/licenses/AFL-3.0
12+
* If you did not receive a copy of the license and are unable to
13+
* obtain it through the world-wide-web, please send an email
14+
* to [email protected] so we can send you a copy immediately.
15+
*
16+
* @author PrestaShop SA and Contributors <[email protected]>
17+
* @copyright Since 2007 PrestaShop SA and Contributors
18+
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product;
24+
25+
use ApiPlatform\Metadata\ApiProperty;
26+
use ApiPlatform\Metadata\ApiResource;
27+
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Query\GetCombinationIds;
28+
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException;
29+
use PrestaShopBundle\ApiPlatform\Metadata\CQRSGet;
30+
use Symfony\Component\HttpFoundation\Response;
31+
32+
#[ApiResource(
33+
operations: [
34+
new CQRSGet(
35+
uriTemplate: '/products/{productId}/combination-ids',
36+
CQRSQuery: GetCombinationIds::class,
37+
scopes: [
38+
'product_read',
39+
],
40+
CQRSQueryMapping: [
41+
'[_context][shopConstraint]' => '[shopConstraint]',
42+
'[@index][combinationId]' => '[combinationIds][@index]',
43+
],
44+
),
45+
],
46+
exceptionToStatus: [
47+
ProductNotFoundException::class => Response::HTTP_NOT_FOUND,
48+
],
49+
)]
50+
class CombinationIdList
51+
{
52+
public int $productId;
53+
#[ApiProperty(openapiContext: ['type' => 'array', 'description' => 'List of combination IDs', 'items' => ['type' => 'integer'], 'example' => [1, 3]])]
54+
public array $combinationIds;
55+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
/**
3+
* Copyright since 2007 PrestaShop SA and Contributors
4+
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
5+
*
6+
* NOTICE OF LICENSE
7+
*
8+
* This source file is subject to the Academic Free License version 3.0
9+
* that is bundled with this package in the file LICENSE.md.
10+
* It is also available through the world-wide-web at this URL:
11+
* https://opensource.org/licenses/AFL-3.0
12+
* If you did not receive a copy of the license and are unable to
13+
* obtain it through the world-wide-web, please send an email
14+
* to [email protected] so we can send you a copy immediately.
15+
*
16+
* @author PrestaShop SA and Contributors <[email protected]>
17+
* @copyright Since 2007 PrestaShop SA and Contributors
18+
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
19+
*/
20+
21+
namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product;
22+
23+
use ApiPlatform\Metadata\ApiProperty;
24+
use ApiPlatform\Metadata\ApiResource;
25+
use PrestaShop\Decimal\DecimalNumber;
26+
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Query\GetEditableCombinationsList;
27+
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException;
28+
use PrestaShop\PrestaShop\Core\Search\Filters\ProductCombinationFilters;
29+
use PrestaShopBundle\ApiPlatform\Metadata\CQRSPaginate;
30+
use Symfony\Component\HttpFoundation\Response;
31+
32+
#[ApiResource(
33+
operations: [
34+
new CQRSPaginate(
35+
uriTemplate: '/products/{productId}/combinations',
36+
CQRSQuery: GetEditableCombinationsList::class,
37+
scopes: [
38+
'product_read',
39+
],
40+
CQRSQueryMapping: [
41+
'[_context][langId]' => '[languageId]',
42+
'[_context][shopConstraint]' => '[shopConstraint]',
43+
],
44+
ApiResourceMapping: [
45+
'[combinationName]' => '[name]',
46+
'[attributesInformation]' => '[attributes]',
47+
'[impactOnPrice]' => '[impactOnPriceTaxExcluded]',
48+
],
49+
filtersClass: ProductCombinationFilters::class,
50+
filtersMapping: [
51+
'[_context][shopId]' => '[shopId]',
52+
],
53+
itemsField: 'combinations',
54+
countField: 'totalCombinationsCount',
55+
),
56+
],
57+
exceptionToStatus: [
58+
ProductNotFoundException::class => Response::HTTP_NOT_FOUND,
59+
],
60+
)]
61+
class CombinationList
62+
{
63+
public int $productId;
64+
public int $combinationId;
65+
public string $name;
66+
public bool $default;
67+
public string $reference;
68+
public DecimalNumber $impactOnPriceTaxExcluded;
69+
public DecimalNumber $ecoTax;
70+
public int $quantity;
71+
public string $imageUrl;
72+
#[ApiProperty(
73+
openapiContext: [
74+
'type' => 'array',
75+
'description' => 'Combination attributes',
76+
'items' => [
77+
'type' => 'object',
78+
'properties' => [
79+
'attributeGroupId' => ['type' => 'integer'],
80+
'attributeGroupName' => ['type' => 'string'],
81+
'attributeId' => ['type' => 'integer'],
82+
'attributeName' => ['type' => 'string'],
83+
],
84+
],
85+
]
86+
)]
87+
public array $attributes;
88+
}

0 commit comments

Comments
 (0)