diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 6e5393c3..15e131ad 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -29,6 +29,7 @@ ->in(__DIR__ . '/src/Services/SonetGroup/') ->in(__DIR__ . '/src/Services/IMOpenLines/') ->in(__DIR__ . '/src/Services/Landing/') + ->in(__DIR__ . '/src/Services/Catalog/') ->name('*.php') ->exclude(['vendor', 'storage', 'docker', 'docs']) // Exclude directories ->ignoreDotFiles(true) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4bc3b52..6cfc732e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ ### Added +- Added service `Services\Catalog\ProductPropertyFeature` with support for `catalog.productPropertyFeature.*` + methods, + see [catalog.productPropertyFeature.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/index.html) ([#553](https://github.com/bitrix24/b24phpsdk/issues/553)): + - `add` adds a parameter (feature) for a product or variation property + - `update` updates a parameter of a product or variation property by id + - `get` returns a product or variation property parameter by id + - `list` returns the list of product/variation property parameters matching the filter + - `getAvailableFeaturesByProperty` returns the list of available parameters for a given property + - `getFields` returns the description of product/variation property parameter fields - Added service `Services\Landing\Site\Service\Site` with support methods, see [landing.site.* methods](https://github.com/bitrix24/b24phpsdk/issues/267): - `add` adds a site diff --git a/Makefile b/Makefile index c2fc4a9a..f0b19198 100644 --- a/Makefile +++ b/Makefile @@ -492,6 +492,10 @@ test-integration-landing-role: test-integration-landing-repowidget: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_landing_repowidget +.PHONY: test-integration-catalog-product-property-feature +test-integration-catalog-product-property-feature: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_product_property_feature + # work dev environment .PHONY: php-dev-server-up php-dev-server-up: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0187ae5..52888893 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -262,6 +262,9 @@ ./tests/Integration/Services/Landing/RepoWidget/ + + ./tests/Integration/Services/Catalog/ProductPropertyFeature/ + diff --git a/src/Services/Catalog/Catalog/Result/CatalogItemResult.php b/src/Services/Catalog/Catalog/Result/CatalogItemResult.php index e62fc7cb..189852bb 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogItemResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogItemResult.php @@ -29,4 +29,4 @@ */ class CatalogItemResult extends AbstractCatalogItem { -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Result/CatalogResult.php b/src/Services/Catalog/Catalog/Result/CatalogResult.php index aa468aa8..c5222f11 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogResult.php @@ -21,4 +21,4 @@ public function catalog(): CatalogItemResult { return new CatalogItemResult($this->getCoreResponse()->getResponseData()->getResult()['catalog']); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Result/CatalogsResult.php b/src/Services/Catalog/Catalog/Result/CatalogsResult.php index add5f259..f721c3bb 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogsResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogsResult.php @@ -32,4 +32,4 @@ public function getCatalogs(): array return $res; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Service/Catalog.php b/src/Services/Catalog/Catalog/Service/Catalog.php index d9cb55fe..0da76ea3 100644 --- a/src/Services/Catalog/Catalog/Service/Catalog.php +++ b/src/Services/Catalog/Catalog/Service/Catalog.php @@ -82,4 +82,4 @@ public function fields(): FieldsResult { return new FieldsResult($this->core->call('catalog.catalog.getFields')); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/CatalogServiceBuilder.php b/src/Services/Catalog/CatalogServiceBuilder.php index 57c91b88..b28d9366 100644 --- a/src/Services/Catalog/CatalogServiceBuilder.php +++ b/src/Services/Catalog/CatalogServiceBuilder.php @@ -17,6 +17,7 @@ use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Services\AbstractServiceBuilder; use Bitrix24\SDK\Services\Catalog; + #[ApiServiceBuilderMetadata(new Scope(['catalog']))] class CatalogServiceBuilder extends AbstractServiceBuilder { @@ -44,4 +45,23 @@ public function catalog(): Catalog\Catalog\Service\Catalog return $this->serviceCache[__METHOD__]; } -} \ No newline at end of file + + public function productPropertyFeature(): Catalog\ProductPropertyFeature\Service\ProductPropertyFeature + { + if (!isset($this->serviceCache[__METHOD__])) { + // Use specialized Batch for ProductPropertyFeature to ensure correct REST parameter mapping + // (lowercase 'id' key, unlike the base Batch default of uppercase 'ID') + $productPropertyFeatureBatch = new Catalog\ProductPropertyFeature\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Catalog\ProductPropertyFeature\Service\ProductPropertyFeature( + new Catalog\ProductPropertyFeature\Service\Batch($productPropertyFeatureBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } +} diff --git a/src/Services/Catalog/Common/ProductType.php b/src/Services/Catalog/Common/ProductType.php index 0bf8ef09..ad568a47 100644 --- a/src/Services/Catalog/Common/ProductType.php +++ b/src/Services/Catalog/Common/ProductType.php @@ -20,4 +20,4 @@ enum ProductType: int case SKU = 3; case productOffer = 4; case genericOffer = 5; -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php index e2929d73..9761daed 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -109,4 +109,4 @@ protected function getKeyWithUserfieldByFieldName(string $fieldName) return $this->$fieldName; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductItemResult.php b/src/Services/Catalog/Product/Result/ProductItemResult.php index c576d9b7..58c80851 100644 --- a/src/Services/Catalog/Product/Result/ProductItemResult.php +++ b/src/Services/Catalog/Product/Result/ProductItemResult.php @@ -52,4 +52,4 @@ */ class ProductItemResult extends AbstractCatalogItem { -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductResult.php b/src/Services/Catalog/Product/Result/ProductResult.php index 2751fda8..fc83fa8c 100644 --- a/src/Services/Catalog/Product/Result/ProductResult.php +++ b/src/Services/Catalog/Product/Result/ProductResult.php @@ -26,4 +26,4 @@ public function product(): ProductItemResult return new ProductItemResult($this->getCoreResponse()->getResponseData()->getResult()['product']); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductsResult.php b/src/Services/Catalog/Product/Result/ProductsResult.php index aa644fa3..268d6d8f 100644 --- a/src/Services/Catalog/Product/Result/ProductsResult.php +++ b/src/Services/Catalog/Product/Result/ProductsResult.php @@ -31,4 +31,4 @@ public function getProducts(): array return $res; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Service/Batch.php b/src/Services/Catalog/Product/Service/Batch.php index 45dddcb7..42970546 100644 --- a/src/Services/Catalog/Product/Service/Batch.php +++ b/src/Services/Catalog/Product/Service/Batch.php @@ -25,7 +25,7 @@ { public function __construct( protected BatchOperationsInterface $batch, - protected LoggerInterface $log) - { + protected LoggerInterface $log + ) { } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Service/Product.php b/src/Services/Catalog/Product/Service/Product.php index 730fd7c9..d37d34f6 100644 --- a/src/Services/Catalog/Product/Service/Product.php +++ b/src/Services/Catalog/Product/Service/Product.php @@ -25,7 +25,6 @@ use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductResult; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductsResult; - use Psr\Log\LoggerInterface; #[ApiServiceMetadata(new Scope(['catalog']))] @@ -35,8 +34,7 @@ public function __construct( public Batch $batch, CoreInterface $core, LoggerInterface $logger - ) - { + ) { parent::__construct($core, $logger); } @@ -71,7 +69,9 @@ public function get(int $productId): ProductResult )] public function add(array $productFields): ProductResult { - return new ProductResult($this->core->call('catalog.product.add', [ + return new ProductResult($this->core->call( + 'catalog.product.add', + [ 'fields' => $productFields ] )); @@ -140,4 +140,4 @@ public function fieldsByFilter(int $iblockId, ProductType $productType, ?array $ return new FieldsResult($this->core->call('catalog.product.getFieldsByFilter', ['filter' => $filter])); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Batch.php b/src/Services/Catalog/ProductPropertyFeature/Batch.php new file mode 100644 index 00000000..c4bccb19 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Batch.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature; + +use Bitrix24\SDK\Core\Response\DTO\ResponseData; + +/** + * Class Batch + * + * Overrides base Batch to handle differences in catalog.productPropertyFeature.* REST methods: + * - the id key is lowercase 'id', not 'ID' as assumed by the base class default + * - catalog.productPropertyFeature.list wraps list items under the 'productPropertyFeatures' key + * instead of returning a flat array in 'result', as the base class assumes for non-CRM methods + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for catalog.productPropertyFeature.* + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts list items from the 'productPropertyFeatures' key of the batch/list result + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + return $responseData->getResult()['productPropertyFeatures'] ?? []; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php new file mode 100644 index 00000000..a8aa02d2 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\AbstractItem; + +/** + * @property-read string $featureId + * @property-read string $featureName + * @property-read string $moduleId + */ +class AvailableFeatureItemResult extends AbstractItem +{ +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php new file mode 100644 index 00000000..48c941ee --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class AvailableFeaturesResult extends AbstractResult +{ + /** + * @return AvailableFeatureItemResult[] + * @throws BaseException + */ + public function features(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + foreach (($result['features'] ?? []) as $item) { + $items[] = new AvailableFeatureItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php new file mode 100644 index 00000000..48a5ccf6 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +class ProductPropertyFeatureAddedBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)($this->getResponseData()->getResult()['productPropertyFeature']['id'] ?? 0); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php new file mode 100644 index 00000000..36da9e5f --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; + +class ProductPropertyFeatureAddedResult extends ProductPropertyFeatureResult +{ + /** + * @throws BaseException + */ + public function getId(): int + { + return (int)($this->productPropertyFeature()->id ?? 0); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php new file mode 100644 index 00000000..722dd97d --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\FieldsResult; + +class ProductPropertyFeatureFieldsResult extends FieldsResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getFieldsDescription(): array + { + return $this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature'] ?? []; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php new file mode 100644 index 00000000..9ced4ecb --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\AbstractItem; + +/** + * @property-read int $id + * @property-read int $propertyId + * @property-read string $moduleId + * @property-read string $featureId + * @property-read bool $isEnabled + */ +class ProductPropertyFeatureItemResult extends AbstractItem +{ + /** + * @param int|string $offset + * + * @return bool|int|mixed|null + */ + public function __get($offset) + { + switch ($offset) { + case 'id': + case 'propertyId': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { + return (int)$this->data[$offset]; + } + + return null; + case 'isEnabled': + return $this->data[$offset] === 'Y'; + default: + return $this->data[$offset] ?? null; + } + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php new file mode 100644 index 00000000..7cd312a9 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertyFeatureResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function productPropertyFeature(): ProductPropertyFeatureItemResult + { + return new ProductPropertyFeatureItemResult( + $this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature'] ?? [] + ); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php new file mode 100644 index 00000000..99909db7 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +class ProductPropertyFeatureUpdatedBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return isset($this->getResponseData()->getResult()['productPropertyFeature']); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php new file mode 100644 index 00000000..93314758 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertyFeatureUpdatedResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function productPropertyFeature(): ProductPropertyFeatureItemResult + { + return new ProductPropertyFeatureItemResult( + $this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature'] ?? [] + ); + } + + /** + * @throws BaseException + */ + public function isSuccess(): bool + { + return isset($this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature']); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php new file mode 100644 index 00000000..319a083b --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertyFeaturesResult extends AbstractResult +{ + /** + * @return ProductPropertyFeatureItemResult[] + * @throws BaseException + */ + public function productPropertyFeatures(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + foreach (($result['productPropertyFeatures'] ?? []) as $item) { + $items[] = new ProductPropertyFeatureItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Service/Batch.php b/src/Services/Catalog/ProductPropertyFeature/Service/Batch.php new file mode 100644 index 00000000..5001f8ab --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Service/Batch.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedBatchResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['catalog']))] +class Batch +{ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list product/variation property parameters. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.productPropertyFeature.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html', + 'Batch list product/variation property parameters' + )] + public function list(array $order = [], array $filter = [], array $select = [], ?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + + foreach ( + $this->batch->getTraversableListWithCount( + 'catalog.productPropertyFeature.list', + $order, + $filter, + $select, + $limit + ) as $key => $value + ) { + yield $key => new ProductPropertyFeatureItemResult($value); + } + } + + /** + * Batch add product/variation property parameters. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html + * + * @param array $productPropertyFeatures + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.productPropertyFeature.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html', + 'Batch add product/variation property parameters' + )] + public function add(array $productPropertyFeatures): Generator + { + $items = []; + foreach ($productPropertyFeatures as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('catalog.productPropertyFeature.add', $items) as $key => $item) { + yield $key => new ProductPropertyFeatureAddedBatchResult($item); + } + } + + /** + * Batch update product/variation property parameters. + * + * Update elements in array with structure: + * id => [ + * 'fields' => [] // fields to update + * ] + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.productPropertyFeature.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html', + 'Batch update product/variation property parameters' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'catalog.productPropertyFeature.update', + $entityItems + ) as $key => $item + ) { + yield $key => new ProductPropertyFeatureUpdatedBatchResult($item); + } + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php b/src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php new file mode 100644 index 00000000..35770c53 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\AvailableFeaturesResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureFieldsResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeaturesResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class ProductPropertyFeature extends AbstractService +{ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Adds a parameter (feature) for a product or variation property. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html + * + * @param array{ + * propertyId: int, + * moduleId: string, + * featureId: string, + * isEnabled: string, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html', + 'Adds a parameter of a product or variation property' + )] + public function add(array $fields): ProductPropertyFeatureAddedResult + { + return new ProductPropertyFeatureAddedResult( + $this->core->call('catalog.productPropertyFeature.add', [ + 'fields' => $fields, + ]) + ); + } + + /** + * Updates a parameter of a product or variation property by id. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html + * + * @param array{ + * propertyId: int, + * moduleId: string, + * featureId: string, + * isEnabled: string, + * } $fields + * + * @throws BaseException + * @throws InvalidArgumentException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html', + 'Updates a parameter of a product or variation property' + )] + public function update(int $id, array $fields): ProductPropertyFeatureUpdatedResult + { + $this->guardPositiveId($id); + + return new ProductPropertyFeatureUpdatedResult( + $this->core->call('catalog.productPropertyFeature.update', [ + 'id' => $id, + 'fields' => $fields, + ]) + ); + } + + /** + * Returns a product or variation property parameter by id. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get.html + * + * @throws BaseException + * @throws InvalidArgumentException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.get', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get.html', + 'Returns a product or variation property parameter by id' + )] + public function get(int $id): ProductPropertyFeatureResult + { + $this->guardPositiveId($id); + + return new ProductPropertyFeatureResult( + $this->core->call('catalog.productPropertyFeature.get', [ + 'id' => $id, + ]) + ); + } + + /** + * Returns the list of product/variation property parameters matching the filter. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html + * + * @param array $select Fields to select + * @param array $filter Filter map + * @param array $order Sort order map + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html', + 'Returns the list of product/variation property parameters' + )] + public function list(array $select = [], array $filter = [], array $order = []): ProductPropertyFeaturesResult + { + return new ProductPropertyFeaturesResult( + $this->core->call('catalog.productPropertyFeature.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + ]) + ); + } + + /** + * Returns the list of available parameters (features) for the given product or variation property. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-available-features-by-property.html + * + * @throws BaseException + * @throws InvalidArgumentException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.getAvailableFeaturesByProperty', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-available-features-by-property.html', + 'Returns the list of available parameters for the given product or variation property' + )] + public function getAvailableFeaturesByProperty(int $propertyId): AvailableFeaturesResult + { + $this->guardPositiveId($propertyId); + + return new AvailableFeaturesResult( + $this->core->call('catalog.productPropertyFeature.getAvailableFeaturesByProperty', [ + 'propertyId' => $propertyId, + ]) + ); + } + + /** + * Returns the description of product/variation property parameter fields. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.getFields', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-fields.html', + 'Returns the description of product/variation property parameter fields' + )] + public function getFields(): ProductPropertyFeatureFieldsResult + { + return new ProductPropertyFeatureFieldsResult( + $this->core->call('catalog.productPropertyFeature.getFields') + ); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php new file mode 100644 index 00000000..0a97825d --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ProductPropertyFeatureItemResult::class)] +class ProductPropertyFeatureItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ProductPropertyFeature $productPropertyFeatureService; + + private int $propertyId; + + #[\Override] + protected function setUp(): void + { + $this->productPropertyFeatureService = Fabric::getServiceBuilder()->getCatalogScope()->productPropertyFeature(); + $this->propertyId = $this->createProductProperty(); + } + + #[\Override] + protected function tearDown(): void + { + $this->deleteProductProperty($this->propertyId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductPropertyFeatureItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawFields = $this->productPropertyFeatureService->getFields()->getFieldsDescription(); + + $this->assertBitrix24AllResultItemFieldsAnnotated(array_keys($rawFields), ProductPropertyFeatureItemResult::class); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductPropertyFeatureItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $productPropertyFeatureItemResult = $this->productPropertyFeatureService->add([ + 'propertyId' => $this->propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ])->productPropertyFeature(); + + self::assertIsInt($productPropertyFeatureItemResult->id); + self::assertIsInt($productPropertyFeatureItemResult->propertyId); + self::assertIsString($productPropertyFeatureItemResult->moduleId); + self::assertIsString($productPropertyFeatureItemResult->featureId); + self::assertIsBool($productPropertyFeatureItemResult->isEnabled); + } + + protected function createProductProperty(): int + { + $core = Fabric::getCore(); + $iblockId = (int)$core->call('catalog.catalog.list', [ + 'select' => ['id', 'iblockId'], + ])->getResponseData()->getResult()['catalogs'][0]['iblockId']; + + return (int)$core->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $iblockId, + 'name' => 'SDK Test Property ' . uniqid('', true), + 'propertyType' => 'S', + 'active' => 'Y', + 'sort' => 100, + ], + ])->getResponseData()->getResult()['productProperty']['id']; + } + + protected function deleteProductProperty(int $id): void + { + $core = Fabric::getCore(); + $core->call('catalog.productProperty.delete', [ + 'id' => $id, + ]); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php new file mode 100644 index 00000000..1c2a840b --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\Batch; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private ProductPropertyFeature $service; + + /** @var int[] */ + private array $propertyIds = []; + + #[\Override] + protected function setUp(): void + { + $this->service = Fabric::getServiceBuilder()->getCatalogScope()->productPropertyFeature(); + } + + #[\Override] + protected function tearDown(): void + { + foreach ($this->propertyIds as $propertyId) { + $this->deleteProductProperty($propertyId); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchAddAndList(): void + { + $propertyId = $this->createProductProperty(); + + $items = [ + [ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + [ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'DETAIL_PAGE_SHOW', + 'isEnabled' => 'N', + ], + ]; + + $addedIds = []; + foreach ($this->service->batch->add($items) as $result) { + $addedIds[] = $result->getId(); + self::assertGreaterThan(0, $result->getId()); + } + + self::assertCount(2, $addedIds); + + $found = []; + foreach ($this->service->batch->list(['id' => 'asc'], ['propertyId' => $propertyId]) as $item) { + self::assertInstanceOf(ProductPropertyFeatureItemResult::class, $item); + $found[] = $item->id; + } + + foreach ($addedIds as $addedId) { + self::assertContains($addedId, $found); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchUpdate(): void + { + $propertyId = $this->createProductProperty(); + + $addedId = $this->service->add([ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ])->getId(); + + $entityItems = [ + $addedId => [ + 'fields' => [ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ], + ], + ]; + + foreach ($this->service->batch->update($entityItems) as $result) { + self::assertTrue($result->isSuccess()); + } + + self::assertFalse($this->service->get($addedId)->productPropertyFeature()->isEnabled); + } + + protected function createProductProperty(): int + { + $core = Fabric::getCore(); + $iblockId = (int)$core->call('catalog.catalog.list', [ + 'select' => ['id', 'iblockId'], + ])->getResponseData()->getResult()['catalogs'][0]['iblockId']; + + $propertyId = (int)$core->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $iblockId, + 'name' => 'SDK Batch Test Property ' . uniqid('', true), + 'propertyType' => 'S', + 'active' => 'Y', + 'sort' => 100, + ], + ])->getResponseData()->getResult()['productProperty']['id']; + + $this->propertyIds[] = $propertyId; + + return $propertyId; + } + + protected function deleteProductProperty(int $id): void + { + $core = Fabric::getCore(); + $core->call('catalog.productProperty.delete', [ + 'id' => $id, + ]); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php new file mode 100644 index 00000000..78731807 --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; + +#[CoversMethod(ProductPropertyFeature::class, 'add')] +#[CoversMethod(ProductPropertyFeature::class, 'update')] +#[CoversMethod(ProductPropertyFeature::class, 'get')] +#[CoversMethod(ProductPropertyFeature::class, 'list')] +#[CoversMethod(ProductPropertyFeature::class, 'getAvailableFeaturesByProperty')] +#[CoversMethod(ProductPropertyFeature::class, 'getFields')] +#[CoversClass(ProductPropertyFeature::class)] +class ProductPropertyFeatureTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ProductPropertyFeature $service; + + private int $propertyId; + + #[\Override] + protected function setUp(): void + { + $this->service = Fabric::getServiceBuilder()->getCatalogScope()->productPropertyFeature(); + $this->propertyId = $this->createProductProperty(); + } + + #[\Override] + protected function tearDown(): void + { + $this->deleteProductProperty($this->propertyId); + } + + public function testFields(): void + { + $fields = $this->service->getFields()->getFieldsDescription(); + self::assertIsArray($fields); + $this->assertNotEmpty($fields); + $this->assertBitrix24AllResultItemFieldsAnnotated(array_keys($fields), ProductPropertyFeatureItemResult::class); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAddGetUpdateList(): void + { + $productPropertyFeatureAddedResult = $this->service->add([ + 'propertyId' => $this->propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ]); + $id = $productPropertyFeatureAddedResult->getId(); + self::assertGreaterThan(0, $id); + + $productPropertyFeatureItemResult = $this->service->get($id)->productPropertyFeature(); + self::assertEquals($this->propertyId, $productPropertyFeatureItemResult->propertyId); + self::assertEquals('iblock', $productPropertyFeatureItemResult->moduleId); + self::assertEquals('LIST_PAGE_SHOW', $productPropertyFeatureItemResult->featureId); + self::assertTrue($productPropertyFeatureItemResult->isEnabled); + + $productPropertyFeatureUpdatedResult = $this->service->update($id, [ + 'propertyId' => $this->propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ]); + self::assertTrue($productPropertyFeatureUpdatedResult->isSuccess()); + self::assertFalse($this->service->get($id)->productPropertyFeature()->isEnabled); + + $list = $this->service->list( + ['id', 'propertyId', 'moduleId', 'featureId', 'isEnabled'], + ['propertyId' => $this->propertyId], + ['id' => 'asc'] + )->productPropertyFeatures(); + self::assertNotEmpty($list); + self::assertEquals($id, $list[0]->id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGetAvailableFeaturesByProperty(): void + { + $features = $this->service->getAvailableFeaturesByProperty($this->propertyId)->features(); + self::assertNotEmpty($features); + self::assertEquals('iblock', $features[0]->moduleId); + } + + protected function createProductProperty(): int + { + $core = Fabric::getCore(); + $iblockId = (int)$core->call('catalog.catalog.list', [ + 'select' => ['id', 'iblockId'], + ])->getResponseData()->getResult()['catalogs'][0]['iblockId']; + + return (int)$core->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $iblockId, + 'name' => 'SDK Test Property ' . uniqid('', true), + 'propertyType' => 'S', + 'active' => 'Y', + 'sort' => 100, + ], + ])->getResponseData()->getResult()['productProperty']['id']; + } + + protected function deleteProductProperty(int $id): void + { + $core = Fabric::getCore(); + $core->call('catalog.productProperty.delete', [ + 'id' => $id, + ]); + } +} diff --git a/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php new file mode 100644 index 00000000..7e2dc1a8 --- /dev/null +++ b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Unit\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedBatchResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedBatchResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\Batch; +use Bitrix24\SDK\Tests\Unit\Stubs\NullBatch; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + #[Test] + #[TestDox('list() yields ProductPropertyFeatureItemResult items')] + public function testListYieldsItemResults(): void + { + $batch = new Batch(new NullBatch(), new NullLogger()); + + foreach ($batch->list() as $item) { + $this->assertInstanceOf(ProductPropertyFeatureItemResult::class, $item); + } + + $this->addToAssertionCount(1); + } + + #[Test] + #[TestDox('add() yields ProductPropertyFeatureAddedBatchResult items and forwards fields wrapper')] + public function testAddYieldsAddedBatchResults(): void + { + $batchOperations = $this->createMock(BatchOperationsInterface::class); + $batchOperations->expects($this->once()) + ->method('addEntityItems') + ->with( + 'catalog.productPropertyFeature.add', + [ + [ + 'fields' => [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + ], + ] + ) + ->willReturn((static function (): \Generator { + yield from []; + })()); + + $batch = new Batch($batchOperations, new NullLogger()); + foreach ($batch->add([ + [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + ]) as $item) { + $this->assertInstanceOf(ProductPropertyFeatureAddedBatchResult::class, $item); + } + + $this->addToAssertionCount(1); + } + + #[Test] + #[TestDox('update() yields ProductPropertyFeatureUpdatedBatchResult items')] + public function testUpdateYieldsUpdatedBatchResults(): void + { + $batch = new Batch(new NullBatch(), new NullLogger()); + + foreach ($batch->update([101 => ['fields' => ['isEnabled' => 'N']]]) as $item) { + $this->assertInstanceOf(ProductPropertyFeatureUpdatedBatchResult::class, $item); + } + + $this->addToAssertionCount(1); + } +} diff --git a/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php new file mode 100644 index 00000000..6b9c1a88 --- /dev/null +++ b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Unit\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\ApiLevelErrorHandler; +use Bitrix24\SDK\Core\Commands\Command; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\AvailableFeaturesResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureFieldsResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeaturesResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\Batch; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\Unit\Stubs\NullBatch; +use Bitrix24\SDK\Tests\Unit\Stubs\NullCore; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\HttpClient\Response\MockResponse; + +#[CoversClass(ProductPropertyFeature::class)] +class ProductPropertyFeatureTest extends TestCase +{ + private ProductPropertyFeature $service; + + #[\Override] + protected function setUp(): void + { + $this->service = new ProductPropertyFeature( + new Batch(new NullBatch(), new NullLogger()), + new NullCore(), + new NullLogger() + ); + } + + #[Test] + public function testAddReturnsProductPropertyFeatureAddedResult(): void + { + $this->assertInstanceOf( + ProductPropertyFeatureAddedResult::class, + $this->service->add([ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ]) + ); + } + + #[Test] + public function testUpdateReturnsProductPropertyFeatureUpdatedResult(): void + { + $this->assertInstanceOf( + ProductPropertyFeatureUpdatedResult::class, + $this->service->update(101, [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ]) + ); + } + + #[Test] + public function testGetReturnsProductPropertyFeatureResult(): void + { + $this->assertInstanceOf(ProductPropertyFeatureResult::class, $this->service->get(101)); + } + + #[Test] + public function testListReturnsProductPropertyFeaturesResult(): void + { + $this->assertInstanceOf(ProductPropertyFeaturesResult::class, $this->service->list()); + } + + #[Test] + public function testGetAvailableFeaturesByPropertyReturnsAvailableFeaturesResult(): void + { + $this->assertInstanceOf(AvailableFeaturesResult::class, $this->service->getAvailableFeaturesByProperty(901)); + } + + #[Test] + public function testGetFieldsReturnsProductPropertyFeatureFieldsResult(): void + { + $this->assertInstanceOf(ProductPropertyFeatureFieldsResult::class, $this->service->getFields()); + } + + #[Test] + public function testUpdateThrowsExceptionForNonPositiveId(): void + { + $this->expectException(InvalidArgumentException::class); + $this->service->update(0, ['propertyId' => 901, 'moduleId' => 'iblock', 'featureId' => 'X', 'isEnabled' => 'Y']); + } + + #[Test] + public function testGetThrowsExceptionForNonPositiveId(): void + { + $this->expectException(InvalidArgumentException::class); + $this->service->get(0); + } + + #[Test] + public function testGetAvailableFeaturesByPropertyThrowsExceptionForNonPositivePropertyId(): void + { + $this->expectException(InvalidArgumentException::class); + $this->service->getAvailableFeaturesByProperty(0); + } + + #[Test] + #[TestDox('add() calls catalog.productPropertyFeature.add with a nested fields object')] + public function testAddSendsNestedFields(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedResult => $productPropertyFeature->add([ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ])); + + $this->assertSame('catalog.productPropertyFeature.add', $method); + $this->assertSame([ + 'fields' => [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + ], $captured); + } + + #[Test] + #[TestDox('update() sends id and nested fields')] + public function testUpdateSendsIdAndFields(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedResult => $productPropertyFeature->update(101, [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ])); + + $this->assertSame('catalog.productPropertyFeature.update', $method); + $this->assertSame(101, $captured['id']); + $this->assertSame([ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ], $captured['fields']); + } + + #[Test] + #[TestDox('get() sends the id')] + public function testGetSendsId(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureResult => $productPropertyFeature->get(101)); + + $this->assertSame('catalog.productPropertyFeature.get', $method); + $this->assertSame(101, $captured['id']); + } + + #[Test] + #[TestDox('list() sends select, filter and order')] + public function testListSendsSelectFilterOrder(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeaturesResult => $productPropertyFeature->list( + ['id', 'propertyId'], + ['propertyId' => 901], + ['id' => 'ASC'] + )); + + $this->assertSame('catalog.productPropertyFeature.list', $method); + $this->assertSame(['id', 'propertyId'], $captured['select']); + $this->assertSame(['propertyId' => 901], $captured['filter']); + $this->assertSame(['id' => 'ASC'], $captured['order']); + } + + #[Test] + #[TestDox('getAvailableFeaturesByProperty() sends propertyId')] + public function testGetAvailableFeaturesByPropertySendsPropertyId(): void + { + [$method, $captured] = $this->call( + static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\AvailableFeaturesResult => $productPropertyFeature->getAvailableFeaturesByProperty(901) + ); + + $this->assertSame('catalog.productPropertyFeature.getAvailableFeaturesByProperty', $method); + $this->assertSame(901, $captured['propertyId']); + } + + #[Test] + #[TestDox('getFields() calls catalog.productPropertyFeature.getFields with no parameters')] + public function testGetFieldsSendsNoParameters(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureFieldsResult => $productPropertyFeature->getFields()); + + $this->assertSame('catalog.productPropertyFeature.getFields', $method); + $this->assertSame([], $captured); + } + + /** + * @return array{0: string, 1: array} + */ + private function call(callable $action): array + { + $method = null; + $captured = []; + $response = new Response( + new MockResponse(''), + new Command('', []), + new ApiLevelErrorHandler(new NullLogger()), + new NullLogger() + ); + + $core = $this->createStub(CoreInterface::class); + $core->method('call')->willReturnCallback( + function (string $apiMethod, array $parameters = []) use (&$method, &$captured, $response): Response { + $method = $apiMethod; + $captured = $parameters; + + return $response; + } + ); + + $action(new ProductPropertyFeature( + new Batch(new NullBatch(), new NullLogger()), + $core, + new NullLogger() + )); + + return [$method, $captured]; + } +}