Skip to content
8 changes: 6 additions & 2 deletions config/admin/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ services:
tags:
- { name: 'console.command' }

# Custom normalizers for commands/queries that are very specific and cannot rely on generic solutions from the core
PrestaShop\Module\APIResources\ApiPlatform\Normalizer\GenerateCombinationsSerializer:
autowire: true
autoconfigure: true
public: false

PrestaShop\Module\APIResources\ApiPlatform\Normalizer\StateIdInterfaceNormalizer:
tags:
- { name: 'serializer.normalizer', priority: 100 }
Expand All @@ -54,8 +60,6 @@ services:
public: false
arguments:
$decorated: '@serializer.normalizer.object'
tags:
- { name: 'serializer.normalizer', priority: 300 }

PrestaShopBundle\ApiPlatform\Serializer\CQRSApiSerializer:
class: PrestaShop\Module\APIResources\Serializer\QueryParameterTypeCastSerializer
Expand Down
2 changes: 1 addition & 1 deletion ps_apiresources.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function __construct()
$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');
$this->author = 'PrestaShop';
$this->version = '0.3.0';
$this->ps_versions_compliancy = ['min' => '9.0.2', 'max' => _PS_VERSION_];
$this->ps_versions_compliancy = ['min' => '9.0.3', 'max' => _PS_VERSION_];
$this->need_instance = 0;
$this->tab = 'administration';
parent::__construct();
Expand Down
64 changes: 64 additions & 0 deletions src/ApiPlatform/Normalizer/GenerateCombinationsSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/

namespace PrestaShop\Module\APIResources\ApiPlatform\Normalizer;

use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Command\GenerateProductCombinationsCommand;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShopBundle\ApiPlatform\Normalizer\ShopConstraintNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

class GenerateCombinationsSerializer implements DenormalizerInterface
{
public function __construct(
private readonly ShopConstraintNormalizer $shopConstraintNormalizer,
) {
}

public function denormalize(mixed $data, string $type, ?string $format = null, array $context = [])
{
$groupedAttributes = [];
foreach ($data['groupedAttributes'] as $attributeGroup) {
$groupedAttributes[$attributeGroup['attributeGroupId']] = array_map(static function ($attributeId): int {
return (int) $attributeId;
}, $attributeGroup['attributeIds']);
}

return new GenerateProductCombinationsCommand(
$data['productId'],
$groupedAttributes,
$this->shopConstraintNormalizer->denormalize($data['_context']['shopConstraint'], ShopConstraint::class),
);
}

public function supportsDenormalization(mixed $data, string $type, ?string $format = null)
{
return $type === GenerateProductCombinationsCommand::class;
}

public function getSupportedTypes(?string $format): array
{
return [
GenerateProductCombinationsCommand::class => true,
'object' => null,
'*' => null,
];
}
}
101 changes: 101 additions & 0 deletions src/ApiPlatform/Resources/Product/Combination.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/

declare(strict_types=1);

namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use PrestaShop\Decimal\DecimalNumber;
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Exception\CombinationNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Query\GetCombinationForEditing;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSGet;
use Symfony\Component\HttpFoundation\Response;

#[ApiResource(
operations: [
new CQRSGet(
uriTemplate: '/products/combinations/{combinationId}',
CQRSQuery: GetCombinationForEditing::class,
scopes: [
'product_read',
],
CQRSQueryMapping: self::QUERY_MAPPING,
),
],
exceptionToStatus: [
CombinationNotFoundException::class => Response::HTTP_NOT_FOUND,
],
)]
class Combination
{
public int $productId;

#[ApiProperty(identifier: true)]
public int $combinationId;
public string $name;
public bool $default;

public string $gtin;
public string $isbn;
public string $mpn;
public string $reference;
public string $upc;

public string $coverThumbnailUrl;
#[ApiProperty(openapiContext: ['type' => 'array', 'description' => 'List of image IDs', 'items' => ['type' => 'integer'], 'example' => [1, 3]])]
public array $imageIds;

public DecimalNumber $impactOnPriceTaxExcluded;
public DecimalNumber $impactOnPriceTaxIncluded;
public DecimalNumber $impactOnUnitPrice;
public DecimalNumber $impactOnUnitPriceTaxIncluded;
public DecimalNumber $ecotaxTaxExcluded;
public DecimalNumber $ecotaxTaxIncluded;
public DecimalNumber $impactOnWeight;
public DecimalNumber $wholesalePrice;
public DecimalNumber $productTaxRate;
public DecimalNumber $productPriceTaxExcluded;
public DecimalNumber $productEcotaxTaxExcluded;

public int $quantity;

public const QUERY_MAPPING = [
'[_context][shopConstraint]' => '[shopConstraint]',
'[details][gtin]' => '[gtin]',
'[details][isbn]' => '[isbn]',
'[details][mpn]' => '[mpn]',
'[details][reference]' => '[reference]',
'[details][upc]' => '[upc]',
'[details][impactOnWeight]' => '[impactOnWeight]',
'[prices][impactOnPrice]' => '[impactOnPriceTaxExcluded]',
'[prices][impactOnPriceTaxIncluded]' => '[impactOnPriceTaxIncluded]',
'[prices][impactOnUnitPrice]' => '[impactOnUnitPriceTaxExcluded]',
'[prices][impactOnUnitPriceTaxIncluded]' => '[impactOnUnitPriceTaxIncluded]',
'[prices][ecotax]' => '[ecotaxTaxExcluded]',
'[prices][ecotaxTaxIncluded]' => '[ecotaxTaxIncluded]',
'[prices][wholesalePrice]' => '[wholesalePrice]',
'[prices][productTaxRate]' => '[productTaxRate]',
'[prices][productPrice]' => '[productPriceTaxExcluded]',
'[prices][productEcotax]' => '[productEcotaxTaxExcluded]',
'[stock][quantity]' => '[quantity]',
];
}
55 changes: 55 additions & 0 deletions src/ApiPlatform/Resources/Product/CombinationIdList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/

declare(strict_types=1);

namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Query\GetCombinationIds;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSGet;
use Symfony\Component\HttpFoundation\Response;

#[ApiResource(
operations: [
new CQRSGet(
uriTemplate: '/products/{productId}/combination-ids',
CQRSQuery: GetCombinationIds::class,
scopes: [
'product_read',
],
CQRSQueryMapping: [
'[_context][shopConstraint]' => '[shopConstraint]',
'[@index][combinationId]' => '[combinationIds][@index]',
],
),
],
exceptionToStatus: [
ProductNotFoundException::class => Response::HTTP_NOT_FOUND,
],
)]
class CombinationIdList
{
public int $productId;
#[ApiProperty(openapiContext: ['type' => 'array', 'description' => 'List of combination IDs', 'items' => ['type' => 'integer'], 'example' => [1, 3]])]
public array $combinationIds;
}
88 changes: 88 additions & 0 deletions src/ApiPlatform/Resources/Product/CombinationList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/

namespace PrestaShop\Module\APIResources\ApiPlatform\Resources\Product;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use PrestaShop\Decimal\DecimalNumber;
use PrestaShop\PrestaShop\Core\Domain\Product\Combination\Query\GetEditableCombinationsList;
use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException;
use PrestaShop\PrestaShop\Core\Search\Filters\ProductCombinationFilters;
use PrestaShopBundle\ApiPlatform\Metadata\CQRSPaginate;
use Symfony\Component\HttpFoundation\Response;

#[ApiResource(
operations: [
new CQRSPaginate(
uriTemplate: '/products/{productId}/combinations',
CQRSQuery: GetEditableCombinationsList::class,
scopes: [
'product_read',
],
CQRSQueryMapping: [
'[_context][langId]' => '[languageId]',
'[_context][shopConstraint]' => '[shopConstraint]',
],
ApiResourceMapping: [
'[combinationName]' => '[name]',
'[attributesInformation]' => '[attributes]',
'[impactOnPrice]' => '[impactOnPriceTaxExcluded]',
],
filtersClass: ProductCombinationFilters::class,
filtersMapping: [
'[_context][shopId]' => '[shopId]',
],
itemsField: 'combinations',
countField: 'totalCombinationsCount',
),
],
exceptionToStatus: [
ProductNotFoundException::class => Response::HTTP_NOT_FOUND,
],
)]
class CombinationList
{
public int $productId;
public int $combinationId;
public string $name;
public bool $default;
public string $reference;
public DecimalNumber $impactOnPriceTaxExcluded;
public DecimalNumber $ecoTax;
public int $quantity;
public string $imageUrl;
#[ApiProperty(
openapiContext: [
'type' => 'array',
'description' => 'Combination attributes',
'items' => [
'type' => 'object',
'properties' => [
'attributeGroupId' => ['type' => 'integer'],
'attributeGroupName' => ['type' => 'string'],
'attributeId' => ['type' => 'integer'],
'attributeName' => ['type' => 'string'],
],
],
]
)]
public array $attributes;
}
Loading
Loading