# Product API

## Products

Ibexa DXP's Product API provides two services for handling product information, which differ in function:

| Service name                                                                                                                                                                  | Description                                                                                                                                                                                                                                           |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`ProductServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-ProductServiceInterface.html)                 | Use it to retrieve product data regardless of the source: Ibexa DXP, [Quable](https://doc.ibexa.co/en/latest/product_catalog/quable/quable/index.md), or [remote PIM](https://doc.ibexa.co/en/latest/product_catalog/add_remote_pim_support/index.md) |
| [`LocalProductServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-LocalProductServiceInterface.html) | Use it to modify products defined in Ibexa DXP                                                                                                                                                                                                        |

Product REST API

To learn how to load products using the REST API, see [REST API reference](https://doc.ibexa.co/en/latest/api/rest_api/rest_api_reference/rest_api_reference.html#tag/Product/operation/api_productcatalogproductsview_post).

### Getting product information

Get an individual product by using the `ProductServiceInterface::getProduct()` method:

```
        $product = $this->productService->getProduct($productCode);

        $output->writeln('Product with code ' . $product->getCode() . ' is ' . $product->getName());
```

Find multiple products with `ProductServiceInterface::findProducts()`.

Provide the method with optional filter, query or Sort Clauses.

```
        $criteria = new Criterion\ProductType([$productType]);
        $sortClauses = [new SortClause\ProductName(ProductQuery::SORT_ASC)];

        $productQuery = new ProductQuery(null, $criteria, $sortClauses);

        $products = $this->productService->findProducts($productQuery);

        foreach ($products as $product) {
            $output->writeln($product->getName() . ' of type ' . $product->getProductType()->getName());
        }
```

See [Product Search Criteria](https://doc.ibexa.co/en/latest/search/criteria_reference/product_search_criteria/index.md) and [Product Sort Clauses](https://doc.ibexa.co/en/latest/search/sort_clause_reference/product_sort_clauses/index.md) references for more information about how to use the [`ProductQuery`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Product-ProductQuery.html) class.

### Modifying products

To create, update and delete products, use the `LocalProductServiceInterface`.

```
        $productUpdateStruct = $this->localProductService->newProductUpdateStruct($product);
        $productUpdateStruct->setCode('NEWMODIFIEDPRODUCT');

        $this->localProductService->updateProduct($productUpdateStruct);
```

To create a product, use `LocalProductServiceInterface::newProductCreateStruct()` to get a [`ProductCreateStruct`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-Values-Product-ProductCreateStruct.html). Provide the method with the product type object and the main language code. You also need to set (at least) the code for the product and the required Field of the underlying content type, `name`:

```
        $productType = $this->productTypeService->getProductType($productType);

        $createStruct = $this->localProductService->newProductCreateStruct($productType, 'eng-GB');
        $createStruct->setCode('NEWPRODUCT');
        $createStruct->setField('name', 'New Product');

        $this->localProductService->createProduct($createStruct);
```

To delete a product, use `LocalProductServiceInterface::deleteProduct()`:

```
        $this->localProductService->deleteProduct($product);
```

### Product variants

#### Searching for variants of a specific product

You can access the variants of a product by using the [`ProductServiceInterface::findProductVariants()`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-ProductServiceInterface.html#method_findProductVariants) method. The method takes the product object and a [`ProductVariantQuery`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Product-ProductVariantQuery.html) object as parameters.

You can filter variants by:

- variant codes:

  ```
      // Get variants filtered by variant codes
      $codeQuery = new ProductVariantQuery();
      $codeQuery->setVariantCodes(['DESK-red', 'DESK-blue']);
      $specificVariants = $this->productService->findProductVariants($product, $codeQuery)->getVariants();
  ```

- product criteria:

  To use [Product Search Criteria](https://doc.ibexa.co/en/latest/search/criteria_reference/product_search_criteria/index.md) with [`ProductVariantQuery`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Product-ProductVariantQuery.html), wrap it with the [`ProductCriterionAdapter`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Content-Query-Criterion-ProductCriterionAdapter.html) class, as in the example below:

  ```
      // Get variants with specific attributes
      $combinedQuery = new ProductVariantQuery();
      $combinedQuery->setAttributesCriterion(
          new ProductCriterionAdapter(
              new Criterion\LogicalAnd([
                  new Criterion\ColorAttribute('color', ['red', 'blue']),
                  new Criterion\IntegerAttribute('size', 42),
              ])
          )
      );
      $filteredVariants = $this->productService->findProductVariants($product, $combinedQuery)->getVariants();
  ```

From a variant ([`ProductVariantInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-ProductVariantInterface.html)), you can access the attributes that are used to generate the variant by using the [`ProductVariantInterface::getDiscriminatorAttributes()`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-ProductVariantInterface.html#method_getDiscriminatorAttributes) method.

```
            $attributes = $variant->getDiscriminatorAttributes();
            foreach ($attributes as $attribute) {
                $output->writeln($attribute->getIdentifier() . ': ' . $attribute->getValue() . ' ');
            }
```

#### Searching for variants across all products

To search for variants across all products, use the [`ProductServiceInterface::findVariants()`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-ProductServiceInterface.html#method_findVariants) method. This method takes a [`ProductVariantQuery`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Product-ProductVariantQuery.html) object and returns variants regardless of their base product.

Unlike `findProductVariants()`, which requires a specific product object, `findVariants()` allows you to search the entire variant catalog.

You can filter variants by:

- variant codes:

  ```
      // Search variants across all products
      $query = new ProductVariantQuery();
      $query->setVariantCodes(['DESK-red', 'DESK-blue']);
      $variantList = $this->productService->findVariants($query);
  ```

- product criteria:

  To use [Product Search Criteria](https://doc.ibexa.co/en/latest/search/criteria_reference/product_search_criteria/index.md) with [`ProductVariantQuery`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Product-ProductVariantQuery.html), wrap it with the [`ProductCriterionAdapter`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Content-Query-Criterion-ProductCriterionAdapter.html) class, as in the example below:

  ```
      // Search variants with attribute criterion
      $colorQuery = new ProductVariantQuery();
      $colorQuery->setAttributesCriterion(
          new ProductCriterionAdapter(
              new Criterion\ColorAttribute('color', ['red'])
          )
      );
      $redVariants = $this->productService->findVariants($colorQuery);
  ```

#### Creating variants

To create a product variant, use `LocalProductServiceInterface::createProductVariants()`. This method takes the product and an array of [`ProductVariantCreateStruct`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-Values-Product-ProductVariantCreateStruct.html) objects as parameters. `ProductVariantCreateStruct` specifies the attribute values and the code for the new variant.

```
        $query->setVariantCodes(['DESK-red', 'DESK-blue']);
        $variantList = $this->productService->findVariants($query);

        foreach ($variantList->getVariants() as $variant) {
            $output->writeln($variant->getName());
        }
```

### Product assets

You can get assets assigned to a product by using [`AssetServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-AssetServiceInterface.html).

Use `AssetServiceInterface` to get a single asset by providing the product object and the assets's ID as parameters:

```
        $singleAsset = $this->assetService->getAsset($product, '1');
        $output->writeln($singleAsset->getName());
```

To get all assets assigned to a product, use `AssetServiceInterface::findAssets()`. You can retrieve the tags (corresponding to attribute values) of assets with the `AssetInterface::getTags()` method:

```
        $assetCollection = $this->assetService->findAssets($product);

        foreach ($assetCollection as $asset) {
            $output->writeln($asset->getIdentifier() . ': ' . $asset->getName());
            $tags = $asset->getTags();
            foreach ($tags as $tag) {
                $output->writeln($tag);
            }
        }
```

## Product types

To work with product types, use [`ProductTypeServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-ProductTypeServiceInterface.html).

### Creating product types

To create a product type, use [`LocalProductTypeServiceInterface`](https://doc.ibexa.co/en/latest/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:

```
        $productTypeCreateStruct = $this->localProductTypeService->newProductTypeCreateStruct(
            'digital_product',
            'eng-GB'
        );
```

You can set names in multiple languages by using `setNames()`:

```
        $productTypeCreateStruct->setNames([
            'eng-GB' => 'Digital Product',
            'pol-PL' => 'Produkt Cyfrowy',
        ]);
```

To create a virtual product type (for products that don't require shipping), use `setVirtual()`:

```
        $productTypeCreateStruct->setVirtual(true);
```

#### 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](https://doc.ibexa.co/en/latest/content_management/content_api/managing_content/#adding-content-types).

```
        $marketingDescriptionFieldDefinition = $this->contentTypeService->newFieldDefinitionCreateStruct(
            'marketing_description',
            'ibexa_string'
        );
        $marketingDescriptionFieldDefinition->names = ['eng-GB' => 'Marketing Description'];
        $marketingDescriptionFieldDefinition->position = 100;
        $contentTypeCreateStruct->addFieldDefinition($marketingDescriptionFieldDefinition);
```

#### Assigning attributes

To assign product attributes to the product type, use `setAssignedAttributesDefinitions()` with an array of [`AssignAttributeDefinitionStruct`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-Values-ProductType-AssignAttributeDefinitionStruct.html) objects.

First, retrieve the attribute definition by using [`AttributeDefinitionServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-AttributeDefinitionServiceInterface.html):

```
        $sizeAttribute = $this->attributeDefinitionService->getAttributeDefinition('size');
```

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):

```
        $attributeAssignment = new AssignAttributeDefinitionStruct(
            $sizeAttribute,
            false,
            false
        );

        $productTypeCreateStruct->setAssignedAttributesDefinitions([$attributeAssignment]);
```

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()`:

```
        $newProductType = $this->localProductTypeService->createProductType($productTypeCreateStruct);
```

### Getting product types

Get a product type object by using `ProductTypeServiceInterface::getProductType()`:

```
        $productType = $this->productTypeService->getProductType($productTypeIdentifier);
```

You can also get a list of product types with `ProductTypeServiceInterface::findProductTypes()`:

```
        $productTypes = $this->productTypeService->findProductTypes();

        foreach ($productTypes as $productType) {
            $output->writeln($productType->getName() . ' with identifier ' . $productType->getIdentifier());
        }
```

## Product availability

Product availability is an object which defines whether a product is available, and if so, in what stock. To manage it, use [`ProductAvailabilityServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-ProductAvailabilityServiceInterface.html).

To check whether a product is available (with or without stock defined), use `ProductAvailabilityServiceInterface::hasAvailability()`.

Get the availability object with `ProductAvailabilityServiceInterface::getAvailability()`. You can then use `ProductAvailabilityServiceInterface::getStock()` to get the stock number for the product:

```
        if ($this->productAvailabilityService->hasAvailability($product)) {
            $availability = $this->productAvailabilityService->getAvailability($product);

            $output->write($availability->isAvailable() ? 'Available' : 'Unavailable');
            $output->writeln(' with stock ' . $availability->getStock());
        }
```

To change availability for a product, use `ProductAvailabilityServiceInterface::updateProductAvailability()` with a [`ProductAvailabilityUpdateStruct`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-Availability-ProductAvailabilityUpdateStruct.html) and provide it with the product object. The second parameter defines whether product is available, and the third whether its stock is infinite. The fourth parameter is the stock number:

```
            $productAvailabilityUpdateStruct = new ProductAvailabilityUpdateStruct($product, true, false, 80);

            $this->productAvailabilityService->updateProductAvailability($productAvailabilityUpdateStruct);
```

## Attributes

To get information about product attribute groups, use the [`AttributeGroupServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-AttributeGroupServiceInterface.html), or [`LocalAttributeGroupServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-LocalAttributeGroupServiceInterface.html) to modify attribute groups.

`AttributeGroupServiceInterface::getAttributeGroup()` enables you to get a single attribute group by its identifier. `AttributeGroupServiceInterface::findAttributeGroups()` gets attribute groups, all of them or filtered with an optional [`AttributeGroupQuery`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Values-AttributeGroup-AttributeGroupQuery.html) object:

```
        $attributeGroup = $this->attributeGroupService->getAttributeGroup('dimensions');

        $attributeGroups = $this->attributeGroupService->findAttributeGroups();

        foreach ($attributeGroups as $attributeGroup) {
            $output->writeln('Attribute group ' . $attributeGroup->getIdentifier() . ' with name ' . $attributeGroup->getName());
        }
```

To create an attribute group, use `LocalAttributeGroupServiceinterface::createAttributeGroup()` and provide it with an [`AttributeGroupCreateStruct`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-Values-AttributeGroup-AttributeGroupCreateStruct.html):

```
        $attributeGroupCreateStruct = $this->localAttributeGroupService->newAttributeGroupCreateStruct('dimensions');
        $attributeGroupCreateStruct->setNames(['eng-GB' => 'Size']);

        $this->localAttributeGroupService->createAttributeGroup($attributeGroupCreateStruct);
```

To get information about product attributes, use the [`AttributeDefinitionServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-AttributeDefinitionServiceInterface.html), or [`LocalAttributeDefinitionServiceInterface`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-LocalAttributeDefinitionServiceInterface.html) to modify attributes.

```
        $attribute = $this->attributeDefinitionService->getAttributeDefinition('length');
        $output->writeln($attribute->getName());
```

To create an attribute, use `LocalAttributeGroupServiceinterface::createAttributeDefinition()` and provide it with an [`AttributeDefinitionCreateStruct`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-ProductCatalog-Local-Values-AttributeDefinition-AttributeDefinitionCreateStruct.html):

```
        $attributeCreateStruct = $this->localAttributeDefinitionService->newAttributeDefinitionCreateStruct('size');
        $attributeCreateStruct->setType($attributeType);
        $attributeCreateStruct->setName('eng-GB', 'Size');
        $attributeCreateStruct->setGroup($attributeGroup);

        $this->localAttributeDefinitionService->createAttributeDefinition($attributeCreateStruct);
```
