diff --git a/code_samples/api/product_catalog/src/Command/ProductTypeCommand.php b/code_samples/api/product_catalog/src/Command/ProductTypeCommand.php index 70216e99fe..069be0767d 100644 --- a/code_samples/api/product_catalog/src/Command/ProductTypeCommand.php +++ b/code_samples/api/product_catalog/src/Command/ProductTypeCommand.php @@ -2,8 +2,12 @@ namespace App\Command; +use Ibexa\Contracts\Core\Repository\ContentTypeService; use Ibexa\Contracts\Core\Repository\PermissionResolver; use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\ProductCatalog\AttributeDefinitionServiceInterface; +use Ibexa\Contracts\ProductCatalog\Local\LocalProductTypeServiceInterface; +use Ibexa\Contracts\ProductCatalog\Local\Values\ProductType\AssignAttributeDefinitionStruct; use Ibexa\Contracts\ProductCatalog\ProductTypeServiceInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -18,11 +22,26 @@ final class ProductTypeCommand extends Command private ProductTypeServiceInterface $productTypeService; - public function __construct(UserService $userService, PermissionResolver $permissionResolver, ProductTypeServiceInterface $productTypeService) - { + private LocalProductTypeServiceInterface $localProductTypeService; + + private ContentTypeService $contentTypeService; + + private AttributeDefinitionServiceInterface $attributeDefinitionService; + + public function __construct( + UserService $userService, + PermissionResolver $permissionResolver, + ProductTypeServiceInterface $productTypeService, + LocalProductTypeServiceInterface $localProductTypeService, + ContentTypeService $contentTypeService, + AttributeDefinitionServiceInterface $attributeDefinitionService + ) { $this->userService = $userService; $this->permissionResolver = $permissionResolver; $this->productTypeService = $productTypeService; + $this->localProductTypeService = $localProductTypeService; + $this->contentTypeService = $contentTypeService; + $this->attributeDefinitionService = $attributeDefinitionService; parent::__construct('doc:product_type'); } @@ -41,6 +60,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int $productTypeIdentifier = $input->getArgument('productTypeIdentifier'); + $productTypeCreateStruct = $this->localProductTypeService->newProductTypeCreateStruct( + 'digital_product', + 'eng-GB' + ); + + $productTypeCreateStruct->setNames([ + 'eng-GB' => 'Digital Product', + 'pol-PL' => 'Produkt Cyfrowy', + ]); + + $productTypeCreateStruct->setVirtual(true); + + $contentTypeCreateStruct = $productTypeCreateStruct->getContentTypeCreateStruct(); + + $marketingDescriptionFieldDefinition = $this->contentTypeService->newFieldDefinitionCreateStruct( + 'marketing_description', + 'ezstring' + ); + $marketingDescriptionFieldDefinition->names = ['eng-GB' => 'Marketing Description']; + $marketingDescriptionFieldDefinition->position = 100; + $contentTypeCreateStruct->addFieldDefinition($marketingDescriptionFieldDefinition); + + $sizeAttribute = $this->attributeDefinitionService->getAttributeDefinition('size'); + + $attributeAssignment = new AssignAttributeDefinitionStruct( + $sizeAttribute, + false, + false + ); + + $productTypeCreateStruct->setAssignedAttributesDefinitions([$attributeAssignment]); + + $newProductType = $this->localProductTypeService->createProductType($productTypeCreateStruct); + $productType = $this->productTypeService->getProductType($productTypeIdentifier); $output->writeln($productType->getName()); diff --git a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductType.json.example b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductType.json.example index c12b3a1f14..95bae8997c 100644 --- a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductType.json.example +++ b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductType.json.example @@ -3,6 +3,7 @@ "_media-type": "application/vnd.ibexa.api.ProductType+json", "identifier": "test_pt321", "name": "New Product Type", + "is_virtual": false, "AttributeAssignmentList": [ { "_media-type": "application/vnd.ibexa.api.AttributeAssignment+json", diff --git a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeCreate.json.example b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeCreate.json.example index 261dc4c029..8fadaaf100 100644 --- a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeCreate.json.example +++ b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeCreate.json.example @@ -83,6 +83,7 @@ "is_required": false, "is_discriminator": true } - ] + ], + "is_virtual": false } -} \ No newline at end of file +} diff --git a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeView.json.example b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeView.json.example index 3f11ee9b45..117d302a29 100644 --- a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeView.json.example +++ b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/POST/ProductTypeView.json.example @@ -15,6 +15,7 @@ "_media-type": "application/vnd.ibexa.api.ProductType+json", "identifier": "climbing_shoe", "name": "New Product Type", + "is_virtual": false, "AttributeAssignmentList": [ { "_media-type": "application/vnd.ibexa.api.AttributeAssignment+json", diff --git a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/id/GET/ProductType.json.example b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/id/GET/ProductType.json.example index b8b41ebe62..56ab23208c 100644 --- a/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/id/GET/ProductType.json.example +++ b/docs/api/rest_api/rest_api_reference/input/examples/product/catalog/product_types/id/GET/ProductType.json.example @@ -3,6 +3,7 @@ "_media-type": "application/vnd.ibexa.api.ProductType+json", "identifier": "d_rope", "name": "Dynamic ropes", + "is_virtual": false, "AttributeAssignmentList": [] } -} \ No newline at end of file +} diff --git a/docs/api/rest_api/rest_api_reference/input/ibexa-types.raml b/docs/api/rest_api/rest_api_reference/input/ibexa-types.raml index e8d2bdfb5c..895fd80ec9 100644 --- a/docs/api/rest_api/rest_api_reference/input/ibexa-types.raml +++ b/docs/api/rest_api/rest_api_reference/input/ibexa-types.raml @@ -4526,6 +4526,10 @@ ProductTypeCreate: assigned_attributes: type: array items: ProductTypeAssignedAttribute + is_virtual: + type: boolean + required: false + description: 'Determines whether the product type is virtual (digital products) or physical. Defaults to false (physical).' ProductTypeUpdateWrapper: description: 'JSON object with only a ProductTypeUpdate property.' diff --git a/docs/pim/pim_guide.md b/docs/pim/pim_guide.md index 9767f4ce37..0de51fbfbd 100644 --- a/docs/pim/pim_guide.md +++ b/docs/pim/pim_guide.md @@ -79,6 +79,17 @@ Before you can assign categories to products, you need to [enable product catego ![Product categories](img/product_categories.png) +### Virtual and physical products + +Product types in [[= product_name =]] can be either virtual or physical: + +- **Physical products** are tangible items that require shipping (for example: books, clothing, electronics). +- **Virtual products** are items that don't require physical delivery (for example: software licenses, e-books, online courses, digital downloads, additional warranty, tickets for an event). + +This product type property can affect the checkout process. +A cart of only virtual products skips the [shipping step](shipping_management.md) during checkout. +To learn more about working with virtual products, see [Virtual products]([[= user_doc =]]/pim/create_virtual_product/) in the User Documentation. + ### Currencies Currencies are used when calculating product price. In the system you can find a list of available currencies, but you can also create custom ones by providing its code. diff --git a/docs/pim/product_api.md b/docs/pim/product_api.md index 3d9ac22a3d..c6b90413a6 100644 --- a/docs/pim/product_api.md +++ b/docs/pim/product_api.md @@ -105,16 +105,75 @@ You can retrieve the tags (corresponding to attribute values) of assets with the To work with product types, use [`ProductTypeServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-ProductTypeServiceInterface.html). +### Creating product types + +To create a product type, use [`LocalProductTypeServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-LocalProductTypeServiceInterface.html). + +First, create a product type struct with `LocalProductTypeServiceInterface::newProductTypeCreateStruct()`, providing the identifier and main language code: + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 62, 66) =]] +``` + +You can set names in multiple languages by using `setNames()`: + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 67, 71) =]] +``` + +To create a virtual product type (for products that don't require shipping), use `setVirtual()`: + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 72, 73) =]] +``` + +#### Adding field definitions + +To add custom field definitions to the product type, use `getContentTypeCreateStruct()` to access the underlying content type struct. +For more information about working with content types, see [Adding content types](../content_management/content_api/managing_content.md#adding-content-types). + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 76, 83) =]] +``` + +#### Assigning attributes + +To assign product attributes to the product type, use `setAssignedAttributesDefinitions()` with an array of [`AssignAttributeDefinitionStruct`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-Values-ProductType-AssignAttributeDefinitionStruct.html) objects. + +First, retrieve the attribute definition by using [`AttributeDefinitionServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-AttributeDefinitionServiceInterface.html): + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 84, 85) =]] +``` + +Then create the assignment struct with the attribute definition, and set whether it's required and whether it's a discriminator (used for product variants): + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 86, 93) =]] +``` + +For more information about working with attributes through PHP API, see [Attributes](#attributes). + +#### Storing new product type + +Finally, create the product type with `LocalProductTypeServiceInterface::createProductType()`: + +``` php +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 94, 95) =]] +``` + +### Getting product types + Get a product type object by using `ProductTypeServiceInterface::getProductType()`: ``` php -[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 43, 44) =]] +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 96, 97) =]] ``` You can also get a list of product types with `ProductTypeServiceInterface::findProductTypes()`: ``` php -[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 47, 52) =]] +[[= include_file('code_samples/api/product_catalog/src/Command/ProductTypeCommand.php', 100, 105) =]] ``` ## Product availability