{"id":19622268,"url":"https://github.com/commercetools/commercetools-sdk-php-v2","last_synced_at":"2025-04-06T09:09:59.669Z","repository":{"id":36970102,"uuid":"80029295","full_name":"commercetools/commercetools-sdk-php-v2","owner":"commercetools","description":"The e-commerce SDK from commercetools for PHP.","archived":false,"fork":false,"pushed_at":"2025-04-01T14:39:41.000Z","size":1239922,"stargazers_count":19,"open_issues_count":6,"forks_count":8,"subscribers_count":64,"default_branch":"master","last_synced_at":"2025-04-02T23:56:50.071Z","etag":null,"topics":["commercetools","commercetools-php-sdk","commercetools-scp","commercetools-sdk","php","php-sdk","sdk"],"latest_commit_sha":null,"homepage":"https://commercetools.github.io/commercetools-sdk-php-v2/docs/html/index.html","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/commercetools.png","metadata":{"files":{"readme":"README.md","changelog":"changes.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-25T15:50:51.000Z","updated_at":"2025-04-01T14:39:44.000Z","dependencies_parsed_at":"2023-09-23T10:31:33.601Z","dependency_job_id":"ed2ed917-09e7-42c5-bef4-0c0746faa512","html_url":"https://github.com/commercetools/commercetools-sdk-php-v2","commit_stats":{"total_commits":619,"total_committers":11,"mean_commits":56.27272727272727,"dds":0.6106623586429725,"last_synced_commit":"948975f4bbb614aede0da603c4e993e4c1ee7201"},"previous_names":[],"tags_count":68,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/commercetools%2Fcommercetools-sdk-php-v2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/commercetools%2Fcommercetools-sdk-php-v2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/commercetools%2Fcommercetools-sdk-php-v2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/commercetools%2Fcommercetools-sdk-php-v2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/commercetools","download_url":"https://codeload.github.com/commercetools/commercetools-sdk-php-v2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247457803,"owners_count":20941906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["commercetools","commercetools-php-sdk","commercetools-scp","commercetools-sdk","php","php-sdk","sdk"],"created_at":"2024-11-11T11:26:56.406Z","updated_at":"2025-04-06T09:09:59.651Z","avatar_url":"https://github.com/commercetools.png","language":"PHP","readme":"# Composable Commerce PHP SDK\n\n## Index of Topics\n\n- [Introduction](#introduction)\n- [Package and Installation](#package-and-installation)\n- [Technical Overview](#technical-overview)\n  - [Placeholder values](#placeholder-values)\n- [Getting Started](#getting-started) \n  - [Client Creation](#client-creation)\n  - [Customize Endpoint for different regions](#customize-endpoint-for-different-regions)\n  - [Performing Requests](#performing-requests)\n- [Configuration](#configuration)\n  - [Applying PSRs](#applying-psrs)\n  - [Error Handling](#error-handling)\n- [Authentication](#authentication)\n  - [Token Storage Creation](#token-storage-creation)\n  - [Password Flow](#password-flow)\n  - [Anonymous Flow](#anonymous-flow)\n  - [Refresh Flow](#refresh-flow)\n- [Middlewares](#middlewares)\n  - [DefaultMiddleware](#defaultmiddleware)\n  - [CorrelationIdMiddleware](#correlationidmiddleware)\n  - [RetryNAMiddleware](#retrynamiddleware)\n  - [OAuthHandlerMiddleware](#oauthhandlermiddleware)\n  - [LoggerMiddleware](#loggermiddleware)\n  - [ReauthenticateMiddleware](#reauthenticatemiddleware)\n- [Querying](#querying)\n  - [Predicates](#predicates)\n  - [Get By Id/Key](#get-by-idkey)\n  - [Sorting](#sorting)\n  - [Pagination](#pagination)\n- [Products and ProductTypes](#products-and-producttypes)\n  - [ProductType Creation](#producttype-creation)\n  - [Product Creation](#product-creation)\n  - [Reading Attributes](#reading-attributes)\n  - [Update attribute values of a product](#update-attribute-values-of-a-product)\n- [Serialization](#serialization) \n- [Migration Guidelines From SDK v1](#migration-guidelines-from-sdk-v1)\n- [Observability](#observability)\n- [Documentation](#documentation)\n- [License](#license)\n\n## Introduction\n\nThis repository contains the PHP SDK generated from the Composable Commerce API reference.\n\nClient and Request Builder for making API requests against [Commercetools](https://www.commercetools.com).\n\n## Package and Installation\n\n```sh\ncomposer require commercetools/commercetools-sdk\n```\n\nPackage           | Version\n------------------|--------------------------------------------------------------------\ncommercetools SDK | [![Latest Stable Version](https://img.shields.io/packagist/v/commercetools/commercetools-sdk.svg)](https://packagist.org/packages/commercetools/commercetools-sdk) [![Total Downloads](https://img.shields.io/packagist/dt/commercetools/commercetools-sdk.svg)](https://packagist.org/packages/commercetools/commercetools-sdk)\n\n\n## Technical Overview\n\nThe SDK consists of the following projects:\n* `lib/commercetools-base/src`: Contains Client which communicate with Composable Commerce to execute requests, it contains also the classes related to the client like tokens, middlewares and handlers, and mappers and exceptions.\n* `lib/commercetools-api/src`: Contains all generated models and request builders to communicate with [Composable Commerce HTTP API](https://docs.commercetools.com/api/).\n* `lib/commercetools-import/src`: Contains all generated models and request builders to communicate with the [Import API](https://docs.commercetools.com/import-api/).\n* `lib/commercetools-history/src`: Contains all generated models and request builders to communicate with the [Change History API](https://docs.commercetools.com/api/history/change-history).\n\nIn addition, the SDK has the following directories:\n* `examples/` : One Dockerized Symfony app per APM (New Relic, Datadog, Dynatrace, OpenTelemetry) to demo PHP SDK usage. \n* `test/integration` : Integration Tests for the SDK. A good way for anyone using the PHP SDK to understand it further.\n* `test/unit` : Unit Tests for \n* `lib/commercetools-api-tests` : generated unit test for each class for the api folder\n* `lib/commercetools-history-tests` : generated unit test for each class for the history folder\n* `lib/commercetools-import-tests` : generated unit test for each class for the import folder\n\n\nThe PHP SDK utilizes various standard interfaces and components to ensure consistency and interoperability:\n\n- [JSON serializer](https://www.php.net/manual/en/jsonserializable.jsonserialize.php)\n- [PSR-3 - LoggerInterface](https://www.php-fig.org/psr/psr-3/)\n- [PSR-4 - Autoloader](https://www.php-fig.org/psr/psr-4/)\n- [PSR-6 - CachingInterface](https://www.php-fig.org/psr/psr-6/)\n- [PSR-7 - HTTP Message Interface](https://www.php-fig.org/psr/psr-7/)\n- [PSR-16 - Common Interface for Caching Libraries](https://www.php-fig.org/psr/psr-16/)\n- [PSR-18 - HTTP Client](https://www.php-fig.org/psr/psr-18/)\n- PHP Date API:\n    - [DateTimeImmutable](https://secure.php.net/manual/en/datetimeimmutable.construct.php)\n\n### Placeholder values\n\nExample code in this guide uses placeholders that should be replaced with the following values.\n\nIf you do not have an API Client, follow our [Get your API Client](#client-creation) guide.\n\nPlaceholder     | Replace with | From\n----------------|--------------|-------\n`{projectKey}`  | project_key  | your API Client\n`{clientID}`    | client_id    | your API Client\n`{clientSecret}`| secret       | your API Client\n`{scope}`       | scope        | your API Client\n`{region}`      | your Region  | [Hosts](/../api/general-concepts#hosts)\n\n## Getting Started\n\n### Client Creation\n\nThe example below shows how to create a client with customized URIs passed in the creation of the Client itself.\nYou will find the same classes in the Import API folder.\n\n```php\nnamespace Commercetools;\n\nuse Commercetools\\Api\\Client\\ClientCredentialsConfig;\nuse Commercetools\\Api\\Client\\Config;\nuse Commercetools\\Client\\ClientCredentials;\nuse Commercetools\\Client\\ClientFactory;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n/** @var string $clientId */\n/** @var string $clientSecret */\n/** @var string $scope\n *   Provide the scope when you want to request a specific ones for the client. \n *   Can be omitted to use all scopes of the oauth client.\n *   Format: `\u003cthe scope name\u003e:\u003cthe project key\u003e`.\n *   Example: `manage_products:project1`. $authConfig \n */\n$authConfig = new ClientCredentialsConfig(\n    new ClientCredentials('{clientID}', '{clientSecret}', '{scope}'),\n    [],\n    'https://auth.{region}.commercetools.com/oauth/token'\n);\n$client = ClientFactory::of()-\u003ecreateGuzzleClient(\n    new Config([], 'https://api.{region}.commercetools.com'),\n    $authConfig\n);\n```\n\n### Customize Endpoint for different regions\n\nBy default, the library uses `api.europe-west1.gcp.commercetools.com` endpoint. If you use a different region, you can configure the client to use a custom endpoint. Here is an example for the `us-central1` region:\n```php\n$authConfig = new ClientCredentialsConfig(\n    new ClientCredentials('{clientId}', '{clientSecret}'), \n    [], \n    'https://auth.us-central1.gcp.commercetools.com/oauth/token'\n);\n\n$config = new Config([], 'https://api.us-central1.gcp.commercetools.com');\n$client = ClientFactory::of()-\u003ecreateGuzzleClient(\n    $config,\n    $authConfig,\n);\n```\nNote that the auth endpoint should contain the `/oauth/token` suffix, but the API endpoint - don't.\n\n### Performing Requests\n\nDetailed information of all available methods for the product API can be found [here](lib/commercetools-api/docs/RequestBuilder.md)\n\nInformation for the Import API can be found [here](lib/commercetools-import/docs/RequestBuilder.md).\n\nExamples to retrieve project information\n\n```php\nuse Commercetools\\Api\\Client\\ApiRequestBuilder;\nuse GuzzleHttp\\ClientInterface;\n\n/** @var ClientInterface $client */\n$builder =  new ApiRequestBuilder($client);\n$request = $builder-\u003ewithProjectKey('{projectKey}')-\u003eget();\n```\n\nTo avoid specifying the project key for every request built it's possible to use the ones in the `Commercetools\\Client` namespace instead\n\n```php\nuse Commercetools\\Client\\ApiRequestBuilder;\nuse Commercetools\\Client\\ImportRequestBuilder;\nuse GuzzleHttp\\ClientInterface;\n\n/** @var ClientInterface $client */\n$builder =  new ApiRequestBuilder('{projectKey}', $client);\n$request = $builder-\u003ecategories()-\u003eget();\n\n$importBuilder =  new ImportRequestBuilder('{projectKey}', $client);\n$request = $importBuilder-\u003eimportSinks()-\u003eget();\n```\nThese are some examples about how to execute a request:\n\n```php\nuse Commercetools\\Client\\ApiRequestBuilder;\nuse GuzzleHttp\\ClientInterface;\n\n/** @var ClientInterface $client */\n$builder =  new ApiRequestBuilder('{projectKey}', $client);\n$request = $builder-\u003ewith()-\u003eget();\n\n// executing the request and mapping the response directly to a domain model\n$project = $request-\u003eexecute();\n\n// send the request to get the response object \n$response = $request-\u003esend();\n// map the response to a domain model\n$project = $request-\u003emapFromResponse($response);\n\n// send the request asynchronously \n$promise = $request-\u003esendAsync();\n// map the response to a domain model\n$project = $request-\u003emapFromResponse($promise-\u003ewait());\n\n// send the request using a client instance\n$response = $client-\u003esend($request);\n$project = $request-\u003emapFromResponse($response);\n```\n\n## Configuration\n\n### Applying PSRs\nThe PHP SDK utilizes various standard interfaces and components to ensure consistency and interoperability:\n- [PSR-3 - Logger Interface](https://www.php-fig.org/psr/psr-3/)\n\n```php\n$authHandler = HandlerStack::create();\n$authHandler-\u003epush(\n    MiddlewareFactory::createLoggerMiddleware(new Logger('auth', [new StreamHandler('./logs/requests.log')]))\n);\n$authConfig = new ClientCredentialsConfig(new ClientCredentials($clientId, $clientSecret), [\n    'handler' =\u003e $authHandler,\n]);\n$logger = new Logger('client', [new StreamHandler('./logs/requests.log')]);\n$client = ClientFactory::of()-\u003ecreateGuzzleClientForHandler(\n    new Config(['maxRetries' =\u003e 3]),\n    OAuthHandlerFactory::ofAuthConfig($authConfig),\n    $logger\n);\n```\n- [PSR-6 - CachingInterface](https://www.php-fig.org/psr/psr-6/)\n\n```php\nuse Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter;\n$filesystemCache = new FilesystemAdapter();\n\n$config = new Config(['timeout' =\u003e 30]);\n$client = ClientFactory-\u003ecreateGuzzleClientForHandler(\n    $config,\n    OAuthHandlerFactory::ofAuthConfig($authConfig, $cache)\n);\n```\n- [PSR-7 - HTTP Message Interface](https://www.php-fig.org/psr/psr-7/)\n\n```php\n//set up the client something like the examples before\n\n// create a guzzle request\n/** @var CategoryBuilder $category */\n$request = $client-\u003ewith()-\u003ecategories()-\u003ewithId($category-\u003egetId())-\u003eget()-\u003ewithExpand('parent');\n$result = $request-\u003eexecute();\n\n$request = new \\GuzzleHttp\\Psr7\\Request('GET', '{projectKey}/categories/{ID}');\n$response = $client-\u003esend($request);\n```\n- [PSR-16 - Common Interface for Caching Libraries](https://www.php-fig.org/psr/psr-16/)\n\n```php\nuse Symfony\\Component\\Cache\\Simple\\FilesystemCache;\nuse Symfony\\Component\\Cache\\Psr16Cache;\n\n$filesystemCache = new FilesystemAdapter();\n$cache = new Psr16Cache($filesystemCache);\n\n$config = new Config(['timeout' =\u003e 30]);\n$client = ClientFactory-\u003ecreateGuzzleClientForHandler(\n    $config,\n    OAuthHandlerFactory::ofAuthConfig($authConfig, $cache)\n);\n```\n\n### Error Handling\nThe error handle is already provided with the [ExceptionFactory](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Exception/ExceptionFactory.php) class.\nThe methods contained in this class encapsulate the logic for converting Guzzle exceptions into your custom exceptions based on the HTTP response status codes.\nWhich can be called directly in the request or can be called by a dedicated middleware for the error handling. \nDirect invocation in a request handling or directly handled in a middleware:\n```php\n    if ($e-\u003egetCode() \u003e= 500) {\n        throw ExceptionFactory::createServerException($e, $apiRequest, $response, $result);\n    } else {\n        throw ExceptionFactory::createClientException($e, $apiRequest, $response, $result);\n    }\n```\n\n## Authentication\nThe factory class [ProviderFactory](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/src/Client/ProviderFactory.php) is for managing authentication and token handling.\n\n### Token Storage Creation\nTo generate a TokenStorageProvider that manages tokens using different flows: Refresh Flow and Anonymous Flow, you can use `ProviderFactory::createTokenStorageProvider($anonTokenUrl, $refreshTokenUrl, $clientCredentials, $client, $tokenStorage, $anonymousIdProvider);`.\n\n### Password Flow\nThe `ProviderFactory::createPasswordFlowProvider($passwordTokenUrl, $clientCredentials, $client, $tokenStorage);` method, creates a PasswordFlowTokenProvider for authenticating users with username and password, acquiring tokens securely.\n\n### Anonymous Flow\nThe `createAnonymousFlowProvider($anonTokenUrl, $clientCredentials, $client, $refreshFlowTokenProvider, $anonymousIdProvider);` method builds an AnonymousFlowTokenProvider to manage tokens for anonymous users, integrating with the API's anonymous token endpoint.\n\n### Refresh Flow\nThe `createRefreshFlowProvider($refreshTokenUrl, $clientCredentials, $client, $tokenStorage)` method sets up a RefreshFlowTokenProvider to handle token refresh operations seamlessly, ensuring continuous access to API resources.\n\n## Middlewares\nWe introduced middleware to add functionalities to the requests and the responses in the PHP SDK.\n\nYou can add middleware when creating the PHP SDK client. Multiple middlewares can be added using the array of middlewares.\n\nThe scope of the MiddlewareFactory which is a Factory pattern is to handle all the available middleware and have the chance to have them customized.\n\nThe methods that are contained in this class, are meant to create an array of middlewares.\n\n\n### DefaultMiddleware\n\nThe method [createDefaultMiddlewares](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Client/MiddlewareFactory.php#L26) creates an array with default values of OAuth Handler, Authentication, Logger, Retry and Correlation ID.\n\n```php\n$authConfig = new ClientCredentialsConfig(new ClientCredentials($clientId, $clientSecret), [\n            'handler' =\u003e $authHandler,\n        ]);\n$oauthHandler = OAuthHandlerFactory::ofAuthConfig($authConfig),\n$logger = new Logger('client', [new StreamHandler('./logs/requests.log')]);\n$maxRetries = 3;\n$correlationIdProvider = new DefaultCorrelationIdProvider();\n\n$middlewares = MiddlewareFactory::createDefaultMiddlewares(\n    $oauthHandler,\n    $logger,\n    $maxRetries,\n    $correlationIdProvider\n);\n```\n\n### CorrelationIdMiddleware\n\nThe method [createCorrelationIdMiddleware](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Client/MiddlewareFactory.php#L51) creates a middleware that adds a correlation ID to the headers of HTTP requests.\n\n```php\n$correlationIdProvider = new DefaultCorrelationIdProvider();\n\n$correlationIdMiddleware = MiddlewareFactory::createCorrelationIdMiddleware(\n    $correlationIdProvider\n);\n```\n\n### RetryNAMiddleware\n\nThe method [createRetryNAMiddleware](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Client/MiddlewareFactory.php#L66) is designed to create middleware that retries HTTP requests under certain conditions are met. This middleware is particularly useful in scenarios where transient errors, such as temporary server unavailability, may occur.\n\n```php\n$maxRetries = 3;\n\n$retryMiddleware = MiddlewareFactory::createRetryNAMiddleware($maxRetries);\n```\n\n### OAuthHandlerMiddleware\n\nThe method [createMiddlewareForOAuthHandler](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Client/MiddlewareFactory.php#L95) creates a middleware for handling OAuth2 authentication ensuring to include the necessary OAuth credentials.\n\n```php\n$tokenProvider = new YourTokenProvider();\n$oauthHandler = OAuthHandlerFactory::ofProvider($tokenProvider),\n\n$oauthMiddleware = MiddlewareFactory::createMiddlewareForOAuthHandler($oauthHandler);\n```\n\n### LoggerMiddleware\n\nThe method [createLoggerMiddleware](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Client/MiddlewareFactory.php#L104) creates a middleware for logging HTTP requests and responses.\n\n```php\n$logger = new Logger('auth');\n$logger-\u003epushHandler(new StreamHandler('./logs/requests.log', Logger::DEBUG));\n\n$loggerMiddleware = MiddlewareFactory::createLoggerMiddleware($logger);\n```\n\n### ReauthenticateMiddleware\n\nThe method [createReauthenticateMiddleware](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Client/MiddlewareFactory.php#L114C28-L114C58) creates a middleware that automatically reauthenticates HTTP requests when an invalid token error (HTTP 401) is encountered. It uses an OAuth2Handler to refresh the token and retry the request up to a specified number of times.\n\n```php\n$authConfig = new ClientCredentialsConfig(new ClientCredentials($clientId, $clientSecret), [\n            'handler' =\u003e $authHandler,\n        ]);\n$oauthHandler = OAuthHandlerFactory::ofAuthConfig($authConfig),\n//maxRetries have the default value 1 as a second parameter of the function\n$reauthMiddleware = MiddlewareFactory::createReauthenticateMiddleware($oauthHandler);\n```\n\n## Querying\nFor the examples that we are mentioning below we are setting the `$builder` like here:\n```php\nuse Commercetools\\Client\\ApiRequestBuilder;\nuse GuzzleHttp\\ClientInterface;\n\n/** @var ClientInterface $client */\n$builder =  new ApiRequestBuilder('{projectKey}', $client);\n```\n\nSince the most of the variables to pass in the `with()` method are scalars, this means that we can pass arrays in the related parameter of the method like in the examples below.\n\n### Predicates\nThe system allows the use of predicates when querying the API. Predicates are added as query parameter string to the request itself. \nThe following example shows the usage of input variables:\n```php\n$builder\n    -\u003ecustomers()\n    -\u003eget()\n    -\u003ewithWhere('lastName=:lastName')\n    -\u003ewithPredicateVar(\"lastName\", $customerSignIn-\u003egetCustomer()-\u003egetLastName());\n```\nIt's also possible to use array values in predicates in case of a varying number of parameters.\n```php\n$builder\n    -\u003eproductProjections()\n    -\u003eget()\n    -\u003ewithWhere('masterVariant(sku in :skus)')\n    -\u003ewithPredicateVar(\"skus\", [\"foo\", \"bar\"]);\n```\n\n### Get By Id/Key\n```php\n$builder\n    -\u003eproductProjections()\n    -\u003ewithId('test_id')\n    -\u003eget();\n```\n```php\n$builder\n    -\u003eproductProjections()\n    -\u003ewithKey('test_key')\n    -\u003eget();\n```\n\n### Sorting\nSee [Sort](https://docs.commercetools.com/api/general-concepts#sorting) for details.\n\nSorting using one parameter:\n```php\n$builder\n    -\u003eproducts()\n    -\u003eget()\n    -\u003ewithSort(\"masterData.current.name.en asc\");\n```\n\nSorting using multiple parameters:\n```php\n$builder\n    -\u003eproducts()\n    -\u003eget()\n    -\u003ewithSort([\"masterData.current.name.en asc\", \"id asc\"]);\n```\n\n### Pagination\nLimiting the number of the returned documents or page size:\n```php\n$builder\n    -\u003eproducts()\n    -\u003eget()\n    -\u003ewithLimit(4)\n    -\u003ewithOffset(4);\n```\n\n## Products and ProductTypes\n\n### ProductType Creation\nA [ProductType](https://commercetools.github.io/commercetools-sdk-php-v2/docs/html/d7/dbf/interface_commercetools_1_1_api_1_1_models_1_1_product_type_1_1_product_type.html) is like a schema that defines how the product attributes are structured.\n\n`ProductType` contains a list of [AttributeDefinition](https://commercetools.github.io/commercetools-sdk-php-v2/docs/html/da/d68/interface_commercetools_1_1_api_1_1_models_1_1_product_type_1_1_attribute_definition.html) which corresponds to the name and type of each attribute, along with some additional information. Each name/type pair must be unique across a Project, so if you create an attribute \"foo\" of type String, you cannot create another ProductType where \"foo\" has another type (e.g. LocalizedString). If you do it anyway you get an error message like:\n\n\"The attribute with name 'foo' has a different type on product type 'exampleproducttype'.\"\n\nIn this scenario we provide two `ProductTypes` **book** and **t-shirt**.\n\nThe **book** product type contains the following attributes:\n\n$isbn as String, International Standard Book Number\nThe **t-shirt** product type contains the following attributes:\n\n$color as [AttributeLocalizedEnumValue](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-api/src/Models/ProductType/AttributeLocalizedEnumValue.php) with the colors green and red and their translations in German and English.\n$size as [AttributePlainEnumValue](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-api/src/Models/ProductType/AttributePlainEnumValue.php) with S, M and X.\n$laundrySymbols as set of [AttributeLocalizedEnumValue](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-api/src/Models/ProductType/AttributeLocalizedEnumValue.php) with temperature and tumble drying.\n$matchingProducts as set of [ProductReference](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-api/src/Models/Product/ProductReference.php), which can point to products that are similar to the current product.\n$rrp as [Money](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-api/src/Models/Common/Money.php) containing the recommended retail price.\n$availableSince as [DateTime](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-api/src/Models/ProductType/AttributeDateTimeType.php) which contains the date since when the product is available for the customer in the shop.\nAll available attribute types you can find here: AttributeType in \"All Known Implementing Classes\".\n\nThe code for the creation of the book ProductType:\n```php\n$isbn = AttributeDefinitionBuilder::of()\n    -\u003ewithType(AttributeTextTypeBuilder::of()-\u003ebuild())\n    -\u003ewithName(self::ISBN_ATTR_NAME)\n    -\u003ewithLabel(LocalizedStringBuilder::of(\"ISBN\")-\u003ebuild())\n    -\u003ewithIsRequired(false)\n    -\u003ebuild();\n\n$productType = ProductTypeBuilder::of()\n    -\u003ewithName(self::BOOK_PRODUCT_TYPE_NAME)\n    -\u003ewithDescription(\"books\")\n    -\u003ewithAttributes(AttributeDefinitionCollection::of()-\u003eadd($isbn))\n    -\u003ebuild();\n    \n$builder =  new ApiRequestBuilder('{projectKey}', $client);\n$request = $builder\n              -\u003eproductTypes()\n              -\u003ewithId($productType-\u003egetId())\n              -\u003eget();\n$productTypeQueryResponse = $request-\u003eexecute();\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nThe code for the creation of the t-shirt ProductType:\n\n```php\n$green = AttributeLocalizedEnumValueBuilder::of()\n                    -\u003ewithKey(\"green\")\n                    -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"green\", \"de\" =\u003e \"grün\"])-\u003ebuild())\n                    -\u003ebuild();\n$red = AttributeLocalizedEnumValueBuilder::of()\n            -\u003ewithKey(\"red\")\n            -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"red\", \"de\" =\u003e \"rot\"])-\u003ebuild())\n            -\u003ebuild();\n$color = AttributeDefinitionDraftBuilder::of()\n            -\u003ewithName(self::COLOR_ATTR_NAME)\n            -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"color\"])-\u003ebuild())\n            -\u003ewithType(AttributeLocalizedEnumTypeBuilder::of()\n                        -\u003ewithValues(AttributeLocalizedEnumValueCollection::fromArray([$green, $red]))\n                        -\u003ebuild())\n            -\u003ewithIsRequired(true)\n            -\u003ebuild();\n$small = AttributePlainEnumValueBuilder::of()\n            -\u003ewithKey(\"S\")\n            -\u003ewithLabel(\"S\")\n            -\u003ebuild();\n$medium = AttributePlainEnumValueBuilder::of()\n            -\u003ewithKey(\"M\")\n            -\u003ewithLabel(\"M\")\n            -\u003ebuild();\n$sizeX = AttributePlainEnumValueBuilder::of()\n            -\u003ewithKey(\"X\")\n            -\u003ewithLabel(\"X\")\n            -\u003ebuild();\n$size = AttributeDefinitionDraftBuilder::of()\n            -\u003ewithName(self::SIZE_ATTR_NAME)\n            -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"Size\"])-\u003ebuild())\n            -\u003ewithType(AttributeEnumTypeBuilder::of()\n                            -\u003ewithValues(AttributePlainEnumValueCollection::fromArray([$small, $medium, $sizeX]))\n                            -\u003ebuild())\n            -\u003ewithIsRequired(true)\n            -\u003ebuild();\n$cold = AttributeLocalizedEnumValueBuilder::of()\n            -\u003ewithKey(\"cold\")\n            -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"Wash at or below 30°C \", \"de\" =\u003e \"30°C\"])-\u003ebuild())\n            -\u003ebuild();\n$hot = AttributeLocalizedEnumValueBuilder::of()\n            -\u003ewithKey(\"hot\")\n            -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"Wash at or below 60°C\", \"de\" =\u003e \"60°C\"])-\u003ebuild())\n            -\u003ebuild();\n$tumbleDrying = AttributeLocalizedEnumValueBuilder::of()\n                    -\u003ewithKey(\"tumbleDrying\")\n                    -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"Tumble Drying\", \"de\" =\u003e \"Trommeltrocknen\"])-\u003ebuild())\n                    -\u003ebuild();\n$noTumbleDrying = AttributeLocalizedEnumValueBuilder::of()\n                    -\u003ewithKey(\"noTumbleDrying\")\n                    -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"no tumble drying\", \"de\" =\u003e \"Nicht im Trommeltrockner trocknen\"])-\u003ebuild())\n                    -\u003ebuild();\n$laundryLabelType = AttributeSetTypeBuilder::of()\n                        -\u003ewithElementType(AttributeLocalizedEnumTypeBuilder::of()\n                                            -\u003ewithValues(AttributeLocalizedEnumValueCollection::fromArray([$cold, $hot, $tumbleDrying, $noTumbleDrying]))\n                                            -\u003ebuild())\n                        -\u003ebuild();\n$laundrySymbols = AttributeDefinitionDraftBuilder::of()\n                    -\u003ewithType($laundryLabelType)\n                    -\u003ewithName(self::LAUNDRY_SYMBOLS_ATTR_NAME)\n                    -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"washing labels\"])-\u003ebuild())\n                    -\u003ewithIsRequired(false)\n                    -\u003ebuild();\n\n$matchingProducts = AttributeDefinitionDraftBuilder::of()\n                        -\u003ewithName(self::MATCHING_PRODUCTS_ATTR_NAME)\n                        -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"matching products\"])-\u003ebuild())\n                        -\u003ewithType(AttributeSetTypeBuilder::of()\n                                    -\u003ewithElementType(AttributeReferenceTypeBuilder::of()\n                                                        -\u003ewithReferenceTypeId(\"product\")\n                                                        -\u003ebuild())\n                                    -\u003ebuild())\n                        -\u003ewithIsRequired(false)\n                        -\u003ebuild();\n$rrp = AttributeDefinitionDraftBuilder::of()\n            -\u003ewithName(self::RRP_ATTR_NAME)\n            -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"recommended retail price\"])-\u003ebuild())\n            -\u003ewithType(AttributeMoneyTypeBuilder::of()-\u003ebuild())\n            -\u003ewithIsRequired(false)\n            -\u003ebuild();\n$availableSince = AttributeDefinitionDraftBuilder::of()\n                    -\u003ewithName(self::AVAILABLE_SINCE_ATTR_NAME)\n                    -\u003ewithLabel(LocalizedStringBuilder::fromArray([\"en\" =\u003e \"available since\"])-\u003ebuild())\n                    -\u003ewithType(AttributeDateTimeTypeBuilder::of()-\u003ebuild())\n                    -\u003ewithIsRequired(false)\n                    -\u003ebuild();\n$attributes = AttributeDefinitionDraftCollection::fromArray([$color, $size, $laundrySymbols, $matchingProducts, $rrp, $availableSince]);\n\n$productTypeDraft = ProductTypeDraftBuilder::of()\n                      -\u003ewithKey(ProductTypeFixture::uniqueProductTypeString())\n                      -\u003ewithName(self::PRODUCT_TYPE_NAME)\n                      -\u003ewithDescription(\"a 'T' shaped cloth\")\n                      -\u003ewithAttributes($attributes)\n                      -\u003ebuild();\n$productType = $builder\n    -\u003ewith()\n    -\u003eproductTypes()\n    -\u003epost($productTypeDraft)\n    -\u003eexecute();\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nProductTypes have a key (String) which can be used as key to logically identify ProductTypes. The key has an unique constraint.\n\n### Product Creation\nTo create a product you need to reference the product type. Since the ProductType ID of the development system will not be the ID of the production system it is necessary to find the product type by name:\n```php\n$productType = $builder\n            -\u003ewith()\n            -\u003eproductTypes()\n            -\u003eget()\n            -\u003ewithQueryParam('where', 'name=\"' . $name . '\"')\n            -\u003eexecute();\n\nreturn $productType-\u003egetResults()-\u003ecurrent() ?: null;\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nThe simplest way of adding attributes to a ProductVariant is to use ```php ProductVariantDraftBuilder::of()-\u003ewithAttributes($attributes)``` which enables you to directly put the value of the attribute to the draft. But it cannot check if you put the right objects and types in it.\n\nA book example:\n```php\n$attributes = AttributeCollection::of()\n                -\u003eadd(\n                    AttributeBuilder::of()\n                        -\u003ewithName(self::ISBN_ATTR_NAME)\n                        -\u003ewithValue(\"978-3-86680-192-9\")\n                        -\u003ebuild());\n$productVariantDraft = ProductVariantDraftBuilder::of()\n                        -\u003ewithAttributes($attributes)\n                        -\u003ebuild();\n$productTypeResourceIdentifier = ProductTypeResourceIdentifierBuilder::of()\n                                    -\u003ewithId($productType-\u003egetId())\n                                    -\u003ebuild();\n$productDraft = ProductDraftBuilder::of()\n                -\u003ewithProductType($productTypeResourceIdentifier)\n                -\u003ewithName(LocalizedStringBuilder::of()-\u003eput(\"en\", \"a book\")-\u003ebuild())\n                -\u003ewithSlug(LocalizedStringBuilder::of()-\u003eput(\"en\", ProductTypeFixture::uniqueProductTypeString())-\u003ebuild())\n                -\u003ewithMasterVariant($productVariantDraft)\n                -\u003ebuild();\n\n$product = $builder-\u003eproducts()\n    -\u003epost($productDraft)\n    -\u003eexecute();\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nA T-shirt example:\n```php\n$referenceableProduct = ProductFixture::referenceableProduct($builder);\n$productType = ProductTypeFixture::fetchProductTypeByName($builder, self::PRODUCT_TYPE_NAME);\n\nif (!$productType) {\n    $productType = ProductTypeFixture::createProductType($builder, self::PRODUCT_TYPE_NAME);\n}\n\n$productReference = ProductReferenceBuilder::of()-\u003ewithId($referenceableProduct-\u003egetId())-\u003ebuild();\n$datetime = new \\DateTime('2015-02-02');\n$datetime = $datetime-\u003eformat(\\DateTime::ATOM);\n$attributes = AttributeCollection::of()\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::COLOR_ATTR_NAME)-\u003ewithValue(\"green\")-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::SIZE_ATTR_NAME)-\u003ewithValue(\"S\")-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::LAUNDRY_SYMBOLS_ATTR_NAME)-\u003ewithValue([\"cold\", \"tumbleDrying\"])-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::RRP_ATTR_NAME)-\u003ewithValue(MoneyBuilder::of()-\u003ewithCentAmount(300)-\u003ewithCurrencyCode(\"EUR\")-\u003ebuild())-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::AVAILABLE_SINCE_ATTR_NAME)-\u003ewithValue($datetime)-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::MATCHING_PRODUCTS_ATTR_NAME)-\u003ewithValue([$productReference])-\u003ebuild());\n$productVariantDraft = ProductVariantDraftBuilder::of()\n    -\u003ewithAttributes($attributes)\n    -\u003ebuild();\n$productTypeResourceIdentifier = ProductTypeResourceIdentifierBuilder::of()\n    -\u003ewithId($productType-\u003egetId())\n    -\u003ebuild();\n$productDraft = ProductDraftBuilder::of()\n    -\u003ewithProductType($productTypeResourceIdentifier)\n    -\u003ewithKey(ProductFixture::uniqueProductString())\n    -\u003ewithName(LocalizedStringBuilder::of()-\u003eput('en', 'basic shirt')-\u003ebuild())\n    -\u003ewithSlug(LocalizedStringBuilder::of()-\u003eput('en', ProductFixture::uniqueProductString())-\u003ebuild())\n    -\u003ewithMasterVariant($productVariantDraft)\n    -\u003ebuild();\n\n$product = $builder-\u003eproducts()\n    -\u003epost($productDraft)\n    -\u003eexecute();\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nA wrong value for a field or an invalid type will cause a BadRequestException with an error code of \"InvalidField\".\n\n```php\n$productType = $builder-\u003eproductTypes()\n            -\u003epost($productTypeDraft)\n            -\u003eexecute();\n$productVariantDraft  = ProductVariantDraftBuilder::of()\n    -\u003ewithAttributes(AttributeCollection::of()\n        -\u003eadd(AttributeBuilder::of()\n                -\u003ewithName(self::COLOR_ATTR_NAME)\n                -\u003ewithValue(1) //1 is of illegal type and of illegal key\n                -\u003ebuild()))\n    -\u003ebuild();\n$productTypeResourceIdentifier = ProductTypeResourceIdentifierBuilder::of()\n    -\u003ewithId($productType-\u003egetId())\n    -\u003ebuild();\n$productDraft = ProductDraftBuilder::of()\n    -\u003ewithProductType($productTypeResourceIdentifier)\n    -\u003ewithName(LocalizedStringBuilder::of()-\u003eput(\"en\", \"basic shirt\")-\u003ebuild())\n    -\u003ewithSlug(LocalizedStringBuilder::of()-\u003eput(\"en\", ProductTypeFixture::uniqueProductTypeString())-\u003ebuild())\n    -\u003ewithMasterVariant($productVariantDraft)\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nAs alternative, you could declare your attributes at the same place and use these to read and write attribute values:\n\n```php\n$green = AttributeLocalizedEnumValueBuilder::of()\n            -\u003ewithKey(\"green\")\n            -\u003ewithLabel(LocalizedStringBuilder::of()-\u003eput(\"en\", \"green \")-\u003eput(\"de\", \"grün\")-\u003ebuild())\n            -\u003ebuild();\n$cold = AttributeLocalizedEnumValueBuilder::of()\n    -\u003ewithKey(\"cold\")\n    -\u003ewithLabel(LocalizedStringBuilder::of()-\u003eput(\"en\", \"Wash at or below 30°C \")-\u003eput(\"de\", \"30°C\")-\u003ebuild())\n    -\u003ebuild();\n$tumbleDrying = AttributeLocalizedEnumValueBuilder::of()\n    -\u003ewithKey(\"tumbleDrying\")\n    -\u003ewithLabel(LocalizedStringBuilder::of()-\u003eput(\"en\", \"tumble drying\")-\u003eput(\"de\", \"Trommeltrocknen\")-\u003ebuild())\n    -\u003ebuild();\n$productReference = ProductReferenceBuilder::of()-\u003ewithId($referenceableProduct-\u003egetId())-\u003ebuild();\n\n$attributes = AttributeCollection::of()\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::COLOR_ATTR_NAME)-\u003ewithValue(\"green\")-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::SIZE_ATTR_NAME)-\u003ewithValue(\"S\")-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::LAUNDRY_SYMBOLS_ATTR_NAME)-\u003ewithValue([\"cold\", \"tumbleDrying\"])-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::RRP_ATTR_NAME)-\u003ewithValue(MoneyBuilder::of()-\u003ewithCentAmount(300)-\u003ewithCurrencyCode(\"EUR\")-\u003ebuild())-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::AVAILABLE_SINCE_ATTR_NAME)-\u003ewithValue($datetime)-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::MATCHING_PRODUCTS_ATTR_NAME)-\u003ewithValue([$productReference])-\u003ebuild());\n$productVariantDraft = ProductVariantDraftBuilder::of()\n    -\u003ewithAttributes($attributes)\n    -\u003ebuild();\n$productTypeResourceIdentifier = ProductTypeResourceIdentifierBuilder::of()\n    -\u003ewithId($productType-\u003egetId())\n    -\u003ebuild();\n$productDraft = ProductDraftBuilder::of()\n    -\u003ewithProductType($productTypeResourceIdentifier)\n    -\u003ewithKey(ProductFixture::uniqueProductString())\n    -\u003ewithName(LocalizedStringBuilder::of()-\u003eput('en', 'basic shirt')-\u003ebuild())\n    -\u003ewithSlug(LocalizedStringBuilder::of()-\u003eput('en', ProductFixture::uniqueProductString())-\u003ebuild())\n    -\u003ewithMasterVariant($productVariantDraft)\n    -\u003ebuild();\n$product = $builder-\u003eproducts()\n    -\u003epost($productDraft)\n    -\u003eexecute();\n\n$masterVariant = $product-\u003egetMasterData()-\u003egetStaged()-\u003egetMasterVariant();\nforeach ($masterVariant-\u003egetAttributes() as $attribute) {\n    if ($attribute-\u003egetName() === self::COLOR_ATTR_NAME) {\n        assertEquals($attribute-\u003egetValue()-\u003ekey, \"green\");\n    }\n    if ($attribute-\u003egetName() === self::SIZE_ATTR_NAME) {\n        assertEquals($attribute-\u003egetValue()-\u003ekey, \"S\");\n    }\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\n### Reading Attributes\nThe simplest way to get the value of the attribute is to use getValue() methods of Attribute, like ```php $attribute-\u003egetValue()```:\n```php\n$product = $this-\u003ecreateProduct();\n$masterVariant = $product-\u003egetMasterData()-\u003egetStaged()-\u003egetMasterVariant();\nforeach ($masterVariant-\u003egetAttributes() as $attribute) {\n    if ($attribute-\u003egetName() === self::SIZE_ATTR_NAME) {\n        assertEquals($attribute-\u003egetValue()-\u003ekey, \"S\");\n    }\n}\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nYou might also use the ```php getValueAs()``` method as a conversion for the attribute, like you have a EnumValue but extract it as boolean because these methods cast the values passed:\n```php\n$product = $builder-\u003eproducts()-\u003epost($productDraft)-\u003eexecute();\n$masterVariant = $product-\u003egetMasterData()-\u003egetStaged()-\u003egetMasterVariant();\n\n$result = null;\nforeach ($masterVariant-\u003egetAttributes() as $attribute) {\n    if ($attribute-\u003egetName() === self::SIZE_ATTR_NAME) {\n        /** @var AttributeAccessor $attrAccessor */\n        $attrAccessor = $attribute-\u003ewith(AttributeAccessor::of());\n\n        $result = $attrAccessor-\u003egetValueAsBool();\n    }\n}\n\n$this-\u003eassertIsBool($result);\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\n### Update attribute values of a product\nHere below some examples about setting attribute values is like a product creation:\n\nExample for books:\n```php\n$product = $this-\u003ecreateBookProduct();\n$masterVariantId = 1;\n$productUpdate = ProductUpdateBuilder::of()\n    -\u003ewithVersion($product-\u003egetVersion())\n    -\u003ewithActions(\n        ProductUpdateActionCollection::fromArray([\n            ProductSetAttributeActionBuilder::of()\n                -\u003ewithVariantId($masterVariantId)\n                -\u003ewithName(self::ISBN_ATTR_NAME)\n                -\u003ewithValue(\"978-3-86680-192-8\")\n                -\u003ebuild()\n        ])\n        )-\u003ebuild();\n\n$productUpdated = $builder\n    -\u003eproducts()\n    -\u003ewithId($product-\u003egetId())\n    -\u003epost($productUpdate)\n    -\u003eexecute();\n$masterVariant = $productUpdated-\u003egetMasterData()-\u003egetStaged()-\u003egetMasterVariant();\n$attribute = ProductTypeFixture::findAttributes($masterVariant-\u003egetAttributes(), self::ISBN_ATTR_NAME);\n\nassertEquals($attribute-\u003egetValue(), \"978-3-86680-192-8\");\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\nExample for T-shirts:\n```php\n$masterVariantId = 1;\n$productUpdatedAction = ProductUpdateBuilder::of()\n    -\u003ewithVersion($product-\u003egetVersion())\n    -\u003ewithActions(\n        ProductUpdateActionCollection::fromArray([\n            ProductSetAttributeActionBuilder::of()\n                -\u003ewithVariantId($masterVariantId)\n                -\u003ewithName(self::COLOR_ATTR_NAME)\n                -\u003ewithValue(\"red\")\n                -\u003ebuild(),\n            ProductSetAttributeActionBuilder::of()\n                -\u003ewithVariantId($masterVariantId)\n                -\u003ewithName(self::SIZE_ATTR_NAME)\n                -\u003ewithValue(\"M\")\n                -\u003ebuild(),\n            ProductSetAttributeActionBuilder::of()\n                -\u003ewithVariantId($masterVariantId)\n                -\u003ewithName(self::LAUNDRY_SYMBOLS_ATTR_NAME)\n                -\u003ewithValue([\"cold\"])\n                -\u003ebuild(),\n            ProductSetAttributeActionBuilder::of()\n                -\u003ewithVariantId($masterVariantId)\n                -\u003ewithName(self::RRP_ATTR_NAME)\n                -\u003ewithValue(MoneyBuilder::of()-\u003ewithCurrencyCode(\"EUR\")-\u003ewithCentAmount(2000)-\u003ebuild())\n                -\u003ebuild(),\n        ])\n    )-\u003ebuild();\n$productUpdated = $builder\n    -\u003ewith()\n    -\u003eproducts()\n    -\u003ewithId($product-\u003egetId())\n    -\u003epost($productUpdatedAction)\n    -\u003eexecute();\n\n$attributesUpdatedProduct = $productUpdated-\u003egetMasterData()-\u003egetStaged()-\u003egetMasterVariant()-\u003egetAttributes();\n\nself::assertEquals(ProductTypeFixture::findAttribute($attributesUpdatedProduct, self::SIZE_ATTR_NAME)-\u003egetValue()-\u003ekey, \"M\");\nself::assertEquals(ProductTypeFixture::findAttribute($attributesUpdatedProduct, self::COLOR_ATTR_NAME)-\u003egetValue()-\u003ekey, \"red\");\nself::assertEquals(ProductTypeFixture::findAttribute($attributesUpdatedProduct, self::LAUNDRY_SYMBOLS_ATTR_NAME)-\u003egetValue()[0]-\u003ekey, \"cold\");\nself::assertEquals(ProductTypeFixture::findAttribute($attributesUpdatedProduct, self::RRP_ATTR_NAME)-\u003egetValue()-\u003ecentAmount, 2000);\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\n### Create attributes for importing orders\n\nImporting attribute values for orders works different from updating products. In orders you provide the full value for enum-like types instead of just the key as done for all other types. This makes it possible to create a new enum value on the fly. The other attributes behave as expected.\n\nExample:\n```php\n$product = $this-\u003ecreateProduct($builder);\n$attributes = AttributeCollection::of()\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::COLOR_ATTR_NAME)-\u003ewithValue(\"yellow\")-\u003ebuild())\n    -\u003eadd(AttributeBuilder::of()-\u003ewithName(self::RRP_ATTR_NAME)-\u003ewithValue(MoneyBuilder::of()-\u003ewithCurrencyCode(\"EUR\")-\u003ewithCentAmount(30)-\u003ebuild())-\u003ebuild());\n\n$productVariantImportDraft = ProductVariantImportDraftBuilder::of()\n                                -\u003ewithId(1)\n                                -\u003ewithAttributes($attributes)\n                                -\u003ebuild();\n$lineItemImportDraft = LineItemImportDraftBuilder::of()\n    -\u003ewithProductId($product-\u003egetId())\n    -\u003ewithVariant($productVariantImportDraft)\n    -\u003ewithQuantity(1)\n    -\u003ewithPrice(ProductFixture::priceDraft())\n    -\u003ewithName(LocalizedStringBuilder::of()-\u003eput(\"en\", \"product name\")-\u003ebuild())\n    -\u003ebuild();\n$orderImportDraft = OrderImportDraftBuilder::of()\n    -\u003ewithLineItems(LineItemImportDraftCollection::of()-\u003eadd($lineItemImportDraft))\n    -\u003ewithTotalPrice(MoneyBuilder::of()-\u003ewithCentAmount(20)-\u003ewithCurrencyCode(\"EUR\")-\u003ebuild())\n    -\u003ewithOrderState(OrderState::COMPLETE)\n    -\u003ebuild();\n$order = $builder-\u003eorders()\n    -\u003eimportOrder()\n    -\u003epost($orderImportDraft)\n    -\u003eexecute();\n    \n$productVariant = $order-\u003egetLineItems()-\u003ecurrent()-\u003egetVariant();\n$colorAttribute = ProductTypeFixture::findAttribute($productVariant-\u003egetAttributes(), self::COLOR_ATTR_NAME);\nassertEquals(\"yellow\", $colorAttribute-\u003egetValue());\n$rrpAttribute = ProductTypeFixture::findAttribute($productVariant-\u003egetAttributes(), self::RRP_ATTR_NAME);\nassertEquals(30, $rrpAttribute-\u003egetValue()-\u003ecentAmount);\n```\nSee the [Test Code](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/test/integration/Api/ProductType/ProductTypeCreationDemoIntegrationTest.php)\n\n## Serialization\nIn the PHP SDK some classes implement the [JsonSerializable](https://www.php.net/manual/en/class.jsonserializable.php) interface, and they have a customized `jsonSerialize()` method to convert the instance of the class to a JSON string easily.\nThis mean that when the method `json_encode()` will be called, the object will be correctly converted and formatted to a JSON string.\n\nSee the example below:\n\n```php\n$messagePayload = new MessageDeliveryPayloadModel(\n    \"{projectKey}\",\n    null, // Replace with an actual Reference object if needed\n    null, // Replace with an actual UserProvidedIdentifiers object if needed\n    \"uniqueId456\", // ID\n    1, // The version\n    new DateTimeImmutable(\"2024-08-06T12:34:56+00:00\"), // CreatedAt\n    new DateTimeImmutable(\"2024-08-06T12:34:56+00:00\"), // LastModifiedAt\n    42, // SequenceNumber\n    1, // Resource version\n    null, // Replace with an actual PayloadNotIncluded object if needed\n    \"Message\" // notification type\n);\n\n$messagePayloadJSON = json_encode($messagePayload);\n```\n\n## Migration Guidelines from SDK v1\nTo migrate from the 1.x to the 2.x, there is a guideline below:\n* [Migration guidelines from v1 to v2](./Migration.md)\n\n## Observability\n\nTo monitor and observe the SDK, see the official documentation [Observability](https://docs.commercetools.com/sdk/observability), there is a [Demo application](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/examples/) which shows how to monitor the PHP SDK with New Relic, Datadog, Dynatrace and Open Telemetry.\n\nTo monitor and observe the SDK, refer to the official documentation on [Observability](https://docs.commercetools.com/sdk/observability).\n\nThe [Demo application](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/examples/) demonstrates how to monitor the PHP SDK with the following APMs:\n\n- [New Relic](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/examples/symfony-app-newrelic)\n- [Datadog](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/examples/symfony-app-datadog)\n- [Dynatrace](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/examples/symfony-app-dynatrace)\n- [OpenTelemetry](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/examples/symfony-app-opentelemetry) (configured to work with New Relic for distributed tracing)\nEach APM integration is implemented through configuration and can be easily enabled using the provided instructions in the demo app for each platform.\n\n## Documentation\n\n* [Documentation](https://commercetools.github.io/commercetools-sdk-php-v2/docs/html/index.html)\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcommercetools%2Fcommercetools-sdk-php-v2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcommercetools%2Fcommercetools-sdk-php-v2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcommercetools%2Fcommercetools-sdk-php-v2/lists"}