From 92e47c65a1a7bc3e5e8af9de7b91854e0cd77468 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 30 Jul 2026 18:13:34 +0400 Subject: [PATCH 1/3] Add catalog.productPropertySection methods --- .php-cs-fixer.php | 1 + CHANGELOG.md | 6 + Makefile | 4 + phpunit.xml.dist | 4 + .../Catalog/Result/CatalogItemResult.php | 2 +- .../Catalog/Catalog/Result/CatalogResult.php | 2 +- .../Catalog/Catalog/Result/CatalogsResult.php | 2 +- .../Catalog/Catalog/Service/Catalog.php | 2 +- .../Catalog/CatalogServiceBuilder.php | 15 ++- src/Services/Catalog/Common/ProductType.php | 2 +- .../Common/Result/AbstractCatalogItem.php | 2 +- .../Product/Result/ProductItemResult.php | 2 +- .../Catalog/Product/Result/ProductResult.php | 2 +- .../Catalog/Product/Result/ProductsResult.php | 2 +- .../Catalog/Product/Service/Batch.php | 6 +- .../Catalog/Product/Service/Product.php | 10 +- .../ProductPropertySectionDisplayType.php | 21 ++++ .../ProductPropertySectionItemResult.php | 30 +++++ .../Result/ProductPropertySectionResult.php | 26 ++++ .../Result/ProductPropertySectionsResult.php | 34 +++++ .../Service/ProductPropertySection.php | 94 ++++++++++++++ .../ProductPropertySectionItemResultTest.php | 118 ++++++++++++++++++ .../Service/ProductPropertySectionTest.php | 109 ++++++++++++++++ .../Service/ProductPropertySectionTest.php | 87 +++++++++++++ 24 files changed, 565 insertions(+), 18 deletions(-) create mode 100644 src/Services/Catalog/ProductPropertySection/ProductPropertySectionDisplayType.php create mode 100644 src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php create mode 100644 src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionResult.php create mode 100644 src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionsResult.php create mode 100644 src/Services/Catalog/ProductPropertySection/Service/ProductPropertySection.php create mode 100644 tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php create mode 100644 tests/Unit/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php 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..014deb8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ ### Added +- Added service `Services\Catalog\ProductPropertySection` with support for + `catalog.productPropertySection.*` methods, + see [catalog.productPropertySection.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/index.html) ([#558](https://github.com/bitrix24/b24phpsdk/issues/558)): + - `get` returns the section settings of a product property or variation by property ID + - `list` returns a list of section settings for product properties/variations by filter + - `set` sets or updates the section settings of a product property or variation - 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..f965995b 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-scope-catalog-product-property-section +test-integration-scope-catalog-product-property-section: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_scope_catalog_product_property_section + # work dev environment .PHONY: php-dev-server-up php-dev-server-up: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0187ae5..4cc6b632 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -262,6 +262,10 @@ ./tests/Integration/Services/Landing/RepoWidget/ + + ./tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php + ./tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php + 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..28772c43 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,16 @@ public function catalog(): Catalog\Catalog\Service\Catalog return $this->serviceCache[__METHOD__]; } -} \ No newline at end of file + + public function productPropertySection(): Catalog\ProductPropertySection\Service\ProductPropertySection + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\ProductPropertySection\Service\ProductPropertySection( + $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/ProductPropertySection/ProductPropertySectionDisplayType.php b/src/Services/Catalog/ProductPropertySection/ProductPropertySectionDisplayType.php new file mode 100644 index 00000000..4948c07f --- /dev/null +++ b/src/Services/Catalog/ProductPropertySection/ProductPropertySectionDisplayType.php @@ -0,0 +1,21 @@ + + * + * 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\ProductPropertySection; + +enum ProductPropertySectionDisplayType: string +{ + case checkboxes = 'F'; + case radioButtons = 'K'; + case dropdownList = 'P'; +} diff --git a/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php new file mode 100644 index 00000000..13a5e8df --- /dev/null +++ b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.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\ProductPropertySection\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\ProductPropertySectionDisplayType; + +/** + * @property-read int $propertyId + * @property-read bool $smartFilter + * @property-read ProductPropertySectionDisplayType $displayType + * @property-read bool $displayExpanded + * @property-read string $filterHint + * @property-read int $iblockId + * @property-read int $sectionId + */ +class ProductPropertySectionItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionResult.php b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionResult.php new file mode 100644 index 00000000..6277bc89 --- /dev/null +++ b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionResult.php @@ -0,0 +1,26 @@ + + * + * 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\ProductPropertySection\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertySectionResult extends AbstractResult +{ + public function productPropertySection(): ProductPropertySectionItemResult + { + return new ProductPropertySectionItemResult( + $this->getCoreResponse()->getResponseData()->getResult()['productPropertySection'] + ); + } +} diff --git a/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionsResult.php b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionsResult.php new file mode 100644 index 00000000..4cd2420e --- /dev/null +++ b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionsResult.php @@ -0,0 +1,34 @@ + + * + * 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\ProductPropertySection\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertySectionsResult extends AbstractResult +{ + /** + * @return ProductPropertySectionItemResult[] + * @throws BaseException + */ + public function getProductPropertySections(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult()['productPropertySections'] as $item) { + $res[] = new ProductPropertySectionItemResult($item); + } + + return $res; + } +} diff --git a/src/Services/Catalog/ProductPropertySection/Service/ProductPropertySection.php b/src/Services/Catalog/ProductPropertySection/Service/ProductPropertySection.php new file mode 100644 index 00000000..b4148664 --- /dev/null +++ b/src/Services/Catalog/ProductPropertySection/Service/ProductPropertySection.php @@ -0,0 +1,94 @@ + + * + * 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\ProductPropertySection\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Result\ProductPropertySectionResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Result\ProductPropertySectionsResult; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class ProductPropertySection extends AbstractService +{ + /** + * Returns the section settings of a product property or variation by the property ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/catalog-product-property-section-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertySection.get', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/catalog-product-property-section-get.html', + 'Returns the section settings of a product property or variation by the property ID.' + )] + public function get(int $propertyId): ProductPropertySectionResult + { + return new ProductPropertySectionResult( + $this->core->call('catalog.productPropertySection.get', ['propertyId' => $propertyId]) + ); + } + + /** + * Returns a list of section settings for product properties and variations based on a filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/catalog-product-property-section-list.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertySection.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/catalog-product-property-section-list.html', + 'Returns a list of section settings for product properties and variations based on a filter.' + )] + public function list(array $select = [], array $filter = [], array $order = []): ProductPropertySectionsResult + { + return new ProductPropertySectionsResult( + $this->core->call('catalog.productPropertySection.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + ]) + ); + } + + /** + * Sets or updates the section settings of a product property or variation. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/catalog-product-property-section-set.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertySection.set', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-section/catalog-product-property-section-set.html', + 'Sets or updates the section settings of a product property or variation.' + )] + public function set(int $propertyId, array $fields): ProductPropertySectionResult + { + return new ProductPropertySectionResult( + $this->core->call('catalog.productPropertySection.set', [ + 'propertyId' => $propertyId, + 'fields' => $fields, + ]) + ); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php b/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php new file mode 100644 index 00000000..28af7e68 --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php @@ -0,0 +1,118 @@ + + * + * 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\ProductPropertySection\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Result\ProductPropertySectionItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Service\ProductPropertySection; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ProductPropertySectionItemResult::class)] +class ProductPropertySectionItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ProductPropertySection $productPropertySection; + + private int $iblockId; + + private int $propertyId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductPropertySectionItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawItem = $this->productPropertySection + ->set($this->propertyId, [ + 'smartFilter' => 'Y', + 'displayType' => 'F', + 'displayExpanded' => 'N', + 'filterHint' => 'test hint', + ]) + ->getCoreResponse()->getResponseData()->getResult()['productPropertySection']; + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + ProductPropertySectionItemResult::class + ); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductPropertySectionItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $item = $this->productPropertySection + ->set($this->propertyId, [ + 'smartFilter' => 'Y', + 'displayType' => 'F', + 'displayExpanded' => 'N', + 'filterHint' => 'test hint', + ]) + ->productPropertySection(); + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $item, + ProductPropertySectionItemResult::class + ); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $this->productPropertySection = Factory::getServiceBuilder() + ->getCatalogScope() + ->productPropertySection(); + + $this->iblockId = Factory::getServiceBuilder()->getCatalogScope()->catalog() + ->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $propertyAddResult = Factory::getCore()->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $this->iblockId, + 'name' => sprintf('test property %s', time()), + 'propertyType' => 'S', + 'code' => sprintf('TEST_PROP_%s', time()), + 'active' => 'Y', + ], + ]); + $this->propertyId = (int)$propertyAddResult->getResponseData()->getResult()['productProperty']['id']; + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + Factory::getCore()->call('catalog.productProperty.delete', ['id' => $this->propertyId]); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php b/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php new file mode 100644 index 00000000..d8abf5f9 --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.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\ProductPropertySection\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\ProductPropertySectionDisplayType; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Service\ProductPropertySection; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ProductPropertySection::class)] +class ProductPropertySectionTest extends TestCase +{ + private ProductPropertySection $productPropertySectionService; + + private int $iblockId; + + private int $propertyId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test ProductPropertySection::set and get')] + public function testSetAndGet(): void + { + $fields = [ + 'smartFilter' => 'Y', + 'displayType' => 'F', + 'displayExpanded' => 'N', + 'filterHint' => 'test hint', + ]; + $setResult = $this->productPropertySectionService->set($this->propertyId, $fields)->productPropertySection(); + $this->assertEquals($this->propertyId, $setResult->propertyId); + $this->assertTrue($setResult->smartFilter); + $this->assertEquals(ProductPropertySectionDisplayType::checkboxes, $setResult->displayType); + $this->assertFalse($setResult->displayExpanded); + $this->assertEquals('test hint', $setResult->filterHint); + + $getResult = $this->productPropertySectionService->get($this->propertyId)->productPropertySection(); + $this->assertEquals($this->propertyId, $getResult->propertyId); + $this->assertEquals('test hint', $getResult->filterHint); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test ProductPropertySection::list')] + public function testList(): void + { + $this->productPropertySectionService->set($this->propertyId, ['smartFilter' => 'Y']); + $items = $this->productPropertySectionService + ->list([], ['propertyId' => $this->propertyId], []) + ->getProductPropertySections(); + $this->assertCount(1, $items); + $this->assertEquals($this->propertyId, $items[0]->propertyId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $this->productPropertySectionService = Factory::getServiceBuilder() + ->getCatalogScope() + ->productPropertySection(); + + $this->iblockId = Factory::getServiceBuilder()->getCatalogScope()->catalog() + ->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $propertyAddResult = Factory::getCore()->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $this->iblockId, + 'name' => sprintf('test property %s', time()), + 'propertyType' => 'S', + 'code' => sprintf('TEST_PROP_%s', time()), + 'active' => 'Y', + ], + ]); + $this->propertyId = (int)$propertyAddResult->getResponseData()->getResult()['productProperty']['id']; + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + Factory::getCore()->call('catalog.productProperty.delete', ['id' => $this->propertyId]); + } +} diff --git a/tests/Unit/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php b/tests/Unit/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php new file mode 100644 index 00000000..e5d9d72a --- /dev/null +++ b/tests/Unit/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php @@ -0,0 +1,87 @@ + + * + * 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\ProductPropertySection\Service; + +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Service\ProductPropertySection; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(ProductPropertySection::class)] +class ProductPropertySectionTest extends TestCase +{ + #[Test] + public function getCallsCoreWithPropertyId(): void + { + $core = $this->createMock(CoreInterface::class); + $core->expects($this->once()) + ->method('call') + ->with('catalog.productPropertySection.get', ['propertyId' => 901]) + ->willReturn($this->createStub(Response::class)); + + (new ProductPropertySection($core, new NullLogger()))->get(901); + } + + #[Test] + public function listCallsCoreWithSelectFilterOrder(): void + { + $core = $this->createMock(CoreInterface::class); + $core->expects($this->once()) + ->method('call') + ->with('catalog.productPropertySection.list', [ + 'select' => ['propertyId'], + 'filter' => ['propertyId' => 901], + 'order' => ['propertyId' => 'ASC'], + ]) + ->willReturn($this->createStub(Response::class)); + + (new ProductPropertySection($core, new NullLogger()))->list( + ['propertyId'], + ['propertyId' => 901], + ['propertyId' => 'ASC'] + ); + } + + #[Test] + public function listCallsCoreWithDefaultEmptyArguments(): void + { + $core = $this->createMock(CoreInterface::class); + $core->expects($this->once()) + ->method('call') + ->with('catalog.productPropertySection.list', [ + 'select' => [], + 'filter' => [], + 'order' => [], + ]) + ->willReturn($this->createStub(Response::class)); + + (new ProductPropertySection($core, new NullLogger()))->list(); + } + + #[Test] + public function setCallsCoreWithPropertyIdAndFields(): void + { + $core = $this->createMock(CoreInterface::class); + $fields = ['smartFilter' => 'Y', 'displayType' => 'F', 'displayExpanded' => 'N', 'filterHint' => 'hint']; + $core->expects($this->once()) + ->method('call') + ->with('catalog.productPropertySection.set', ['propertyId' => 901, 'fields' => $fields]) + ->willReturn($this->createStub(Response::class)); + + (new ProductPropertySection($core, new NullLogger()))->set(901, $fields); + } +} From 548f9be38c7a97d4012346b04ff86644109189be Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 30 Jul 2026 18:17:10 +0400 Subject: [PATCH 2/3] Change using the Factory to Fabric class --- .../Result/ProductPropertySectionItemResultTest.php | 10 +++++----- .../Service/ProductPropertySectionTest.php | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php b/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php index 28af7e68..0995e9bc 100644 --- a/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php +++ b/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php @@ -18,7 +18,7 @@ use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Result\ProductPropertySectionItemResult; use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Service\ProductPropertySection; use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestDox; @@ -87,14 +87,14 @@ public function testAllFieldsHasValidTypeCastingInMagicGetters(): void #[\Override] protected function setUp(): void { - $this->productPropertySection = Factory::getServiceBuilder() + $this->productPropertySection = Fabric::getServiceBuilder() ->getCatalogScope() ->productPropertySection(); - $this->iblockId = Factory::getServiceBuilder()->getCatalogScope()->catalog() + $this->iblockId = Fabric::getServiceBuilder()->getCatalogScope()->catalog() ->list([], [], [], 1)->getCatalogs()[0]->iblockId; - $propertyAddResult = Factory::getCore()->call('catalog.productProperty.add', [ + $propertyAddResult = Fabric::getCore()->call('catalog.productProperty.add', [ 'fields' => [ 'iblockId' => $this->iblockId, 'name' => sprintf('test property %s', time()), @@ -113,6 +113,6 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { - Factory::getCore()->call('catalog.productProperty.delete', ['id' => $this->propertyId]); + Fabric::getCore()->call('catalog.productProperty.delete', ['id' => $this->propertyId]); } } diff --git a/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php b/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php index d8abf5f9..6f9c5de7 100644 --- a/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php +++ b/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php @@ -17,7 +17,7 @@ use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Services\Catalog\ProductPropertySection\ProductPropertySectionDisplayType; use Bitrix24\SDK\Services\Catalog\ProductPropertySection\Service\ProductPropertySection; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -78,14 +78,14 @@ public function testList(): void #[\Override] protected function setUp(): void { - $this->productPropertySectionService = Factory::getServiceBuilder() + $this->productPropertySectionService = Fabric::getServiceBuilder() ->getCatalogScope() ->productPropertySection(); - $this->iblockId = Factory::getServiceBuilder()->getCatalogScope()->catalog() + $this->iblockId = Fabric::getServiceBuilder()->getCatalogScope()->catalog() ->list([], [], [], 1)->getCatalogs()[0]->iblockId; - $propertyAddResult = Factory::getCore()->call('catalog.productProperty.add', [ + $propertyAddResult = Fabric::getCore()->call('catalog.productProperty.add', [ 'fields' => [ 'iblockId' => $this->iblockId, 'name' => sprintf('test property %s', time()), @@ -104,6 +104,6 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { - Factory::getCore()->call('catalog.productProperty.delete', ['id' => $this->propertyId]); + Fabric::getCore()->call('catalog.productProperty.delete', ['id' => $this->propertyId]); } } From 513de1829f385492a1cf3b2e3388911d84e5ab3c Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 30 Jul 2026 18:47:14 +0400 Subject: [PATCH 3/3] Fix on liter results --- Makefile | 2 +- .../ProductPropertySectionItemResult.php | 19 ++++++++++++-- .../ProductPropertySectionItemResultTest.php | 26 ++----------------- .../Service/ProductPropertySectionTest.php | 16 ++++++------ 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index f965995b..1f8ca937 100644 --- a/Makefile +++ b/Makefile @@ -494,7 +494,7 @@ test-integration-landing-repowidget: .PHONY: test-integration-scope-catalog-product-property-section test-integration-scope-catalog-product-property-section: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_scope_catalog_product_property_section + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_catalog_product_property_section # work dev environment .PHONY: php-dev-server-up diff --git a/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php index 13a5e8df..1d93f0aa 100644 --- a/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php +++ b/src/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Catalog\ProductPropertySection\Result; -use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Bitrix24\SDK\Core\Result\AbstractItem; use Bitrix24\SDK\Services\Catalog\ProductPropertySection\ProductPropertySectionDisplayType; /** @@ -25,6 +25,21 @@ * @property-read int $iblockId * @property-read int $sectionId */ -class ProductPropertySectionItemResult extends AbstractAnnotatedItem +class ProductPropertySectionItemResult extends AbstractItem { + /** + * @param int|string $offset + * + * @return bool|int|ProductPropertySectionDisplayType|string|null + */ + public function __get($offset) + { + return match ($offset) { + 'smartFilter', 'displayExpanded' => $this->data[$offset] === 'Y', + 'displayType' => ProductPropertySectionDisplayType::from($this->data[$offset]), + 'propertyId', 'iblockId', 'sectionId' => (int)$this->data[$offset], + 'filterHint' => (string)$this->data[$offset], + default => $this->data[$offset] ?? null, + }; + } } diff --git a/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php b/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php index 0995e9bc..ab3c46ed 100644 --- a/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php +++ b/tests/Integration/Services/Catalog/ProductPropertySection/Result/ProductPropertySectionItemResultTest.php @@ -58,28 +58,6 @@ public function testAllFieldsAreAnnotated(): void ); } - /** - * @throws BaseException - * @throws TransportException - */ - #[Test] - #[TestDox('all fields in ProductPropertySectionItemResult have valid type casting in magic getters')] - public function testAllFieldsHasValidTypeCastingInMagicGetters(): void - { - $item = $this->productPropertySection - ->set($this->propertyId, [ - 'smartFilter' => 'Y', - 'displayType' => 'F', - 'displayExpanded' => 'N', - 'filterHint' => 'test hint', - ]) - ->productPropertySection(); - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( - $item, - ProductPropertySectionItemResult::class - ); - } - /** * @throws BaseException * @throws TransportException @@ -94,7 +72,7 @@ protected function setUp(): void $this->iblockId = Fabric::getServiceBuilder()->getCatalogScope()->catalog() ->list([], [], [], 1)->getCatalogs()[0]->iblockId; - $propertyAddResult = Fabric::getCore()->call('catalog.productProperty.add', [ + $response = Fabric::getCore()->call('catalog.productProperty.add', [ 'fields' => [ 'iblockId' => $this->iblockId, 'name' => sprintf('test property %s', time()), @@ -103,7 +81,7 @@ protected function setUp(): void 'active' => 'Y', ], ]); - $this->propertyId = (int)$propertyAddResult->getResponseData()->getResult()['productProperty']['id']; + $this->propertyId = (int)$response->getResponseData()->getResult()['productProperty']['id']; } /** diff --git a/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php b/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php index 6f9c5de7..26f91dd5 100644 --- a/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php +++ b/tests/Integration/Services/Catalog/ProductPropertySection/Service/ProductPropertySectionTest.php @@ -44,12 +44,12 @@ public function testSetAndGet(): void 'displayExpanded' => 'N', 'filterHint' => 'test hint', ]; - $setResult = $this->productPropertySectionService->set($this->propertyId, $fields)->productPropertySection(); - $this->assertEquals($this->propertyId, $setResult->propertyId); - $this->assertTrue($setResult->smartFilter); - $this->assertEquals(ProductPropertySectionDisplayType::checkboxes, $setResult->displayType); - $this->assertFalse($setResult->displayExpanded); - $this->assertEquals('test hint', $setResult->filterHint); + $productPropertySectionItemResult = $this->productPropertySectionService->set($this->propertyId, $fields)->productPropertySection(); + $this->assertEquals($this->propertyId, $productPropertySectionItemResult->propertyId); + $this->assertTrue($productPropertySectionItemResult->smartFilter); + $this->assertEquals(ProductPropertySectionDisplayType::checkboxes, $productPropertySectionItemResult->displayType); + $this->assertFalse($productPropertySectionItemResult->displayExpanded); + $this->assertEquals('test hint', $productPropertySectionItemResult->filterHint); $getResult = $this->productPropertySectionService->get($this->propertyId)->productPropertySection(); $this->assertEquals($this->propertyId, $getResult->propertyId); @@ -85,7 +85,7 @@ protected function setUp(): void $this->iblockId = Fabric::getServiceBuilder()->getCatalogScope()->catalog() ->list([], [], [], 1)->getCatalogs()[0]->iblockId; - $propertyAddResult = Fabric::getCore()->call('catalog.productProperty.add', [ + $response = Fabric::getCore()->call('catalog.productProperty.add', [ 'fields' => [ 'iblockId' => $this->iblockId, 'name' => sprintf('test property %s', time()), @@ -94,7 +94,7 @@ protected function setUp(): void 'active' => 'Y', ], ]); - $this->propertyId = (int)$propertyAddResult->getResponseData()->getResult()['productProperty']['id']; + $this->propertyId = (int)$response->getResponseData()->getResult()['productProperty']['id']; } /**