Giter VIP home page Giter VIP logo

commercetools-php-sdk's Introduction

Composable Commerce PHP SDK

โš ๏ธ **This Composable Commerce PHP SDK is deprecated effective 1st September 2022., We recommend to use our PHP SDK V2.

Build Status Scrutinizer Scrutinizer Packagist Packagist

The PHP SDK allows developers to integrate with Composable Commerce APIs using PHP native interfaces, models and helpers instead of manually using the HTTP and JSON API.

You gain lots of IDE Auto-Completion, type checks on a literal API, Warnings, Object Mapping, i18n support etc.. The Client manages the OAuth2 security tokens, provides caches and interfaces for concurrent and asynchronous API calls.

The SDK is licensed under the permissive MIT License. Don't hesitate to contribute!

Using the SDK

The PHP API documentation provides all the details you need in a searchable form (link points to latest stable release).

Install & Integrate the SDK into your Project

The SDK requires a PHP version of 5.6 or higher. The SDK tries to use the APC(u) as it's default cache. If you provide a PSR-6 or PSR-16 compliant cache adapter, APC(u) is not necessary. The cache/filesystem-adapter is tried to be used if no APC(u) is installed.

The curl extension is recommended but not strictly necessary because the SDK is using the Guzzle library library, which falls back to PHP stream wrappers if curl is not available. The intl extension is required to directly output Money objects as a String.

The recommended way to install the SDK is through Composer.

# Install Composer if not installed yet
curl -sS https://getcomposer.org/installer | php

Next, run the Composer command to install the latest version of the SDK:

composer require commercetools/php-sdk

The SDK supports Guzzle6 as well as Guzzle5 as HTTP client. For Guzzle6:

composer require guzzlehttp/guzzle ^6.0

When you want to use Guzzle5 you have to ensure react/promise at minimum version 2.2:

composer require guzzlehttp/guzzle ^5.3.1
composer require react/promise ^2.2

After installing, you need to require Composer's autoloader if that's not yet the case:

require 'vendor/autoload.php';

If you don't use Composer, just download a zip archive of the latest release, manually integrate it and configure your own autoloader.

The project follows the semantic versioning guidelines, i.e. everything but major version changes are backwards-compatible. This matches composer's default behavior.

With composer just run composer update commercetools/php-sdk to update to compatible versions. Edit your composer.json file to update to incompatible versions.

Please read the Changelog before updating in any case.

Getting started

To get up and running, create a free test Project on Composable Commerce. You can create your first API Client in The Merchant Center (Menu "Settings"->"Developer settings"->"Create new API client"). You need to select the template for the "Admin client".

<?php

require '../vendor/autoload.php';

use Commercetools\Core\Builder\Request\RequestBuilder;
use Commercetools\Core\Client;
use Commercetools\Core\Config;
use Commercetools\Core\Model\Common\Context;

$config = [
    'client_id' => 'my client id',
    'client_secret' => 'my client secret',
    'project' => 'my project id'
];
$context = Context::of()->setLanguages(['en'])->setLocale('en_US')->setGraceful(true);
$config = Config::fromArray($config)->setContext($context);

/**
 * create a search request and a client,
 * execute the request and get the PHP Object
 * (the client can and should be re-used)
 */
$search = RequestBuilder::of()->productProjections()->search()
    ->addParam('text.en', 'red');

$client = Client::ofConfig($config);
$products = $client->execute($search)->toObject();

/**
 * show result (would be a view layer in real world)
 */
header('Content-Type: text/html; charset=utf-8');

foreach ($products as $product) {
    echo $product->getName()->en . '<br/>';
}

If you prefer not to have a client with all admin rights, you need to explicitly include the client's permission scopes that you selected when creating the client, on the client's configuration:

<?php
$config = [
    'client_id' => 'my client id',
    'client_secret' => 'my client secret',
    'project' => 'my project id',
    'scope' => 'permission_scope and_another_scope'
];

In projects you will not put your API credentials directly into code but use a config file or your framework's config or dependency injection system for that.

Using the client factory

When using a Guzzle version of 6 or greater, it's also possible to use a preconfigured Guzzle client using the client factory. At the moment this is limited to client credentials authentication flow.

<?php

require '../vendor/autoload.php';

use Commercetools\Core\Builder\Request\RequestBuilder;
use Commercetools\Core\Client\ClientFactory;
use Commercetools\Core\Config;
use Commercetools\Core\Error\ApiException;
use Commercetools\Core\Model\Common\Context;

$config = [
    'client_id' => 'my client id',
    'client_secret' => 'my client secret',
    'project' => 'my project id'
];
$context = Context::of()->setLanguages(['en'])->setLocale('en_US')->setGraceful(true);
$config = Config::fromArray($config)->setContext($context)->setThrowExceptions(true);

/**
 * create a search request and a client,
 * execute the request and get the PHP Object
 * (the client can and should be re-used)
 */
$request = RequestBuilder::of()->productProjections()->search()
    ->addParam('text.en', 'red');

$client = ClientFactory::of()->createClient($config);

try {
    $response = $client->execute($request);
} catch (ApiException $exception) {
    throw new \Exception("Ooops! Something happened.", 0, $exception);
}
$products = $request->mapFromResponse($response);

header('Content-Type: text/html; charset=utf-8');

foreach ($products as $product) {
    echo $product->getName()->en . '<br/>';
}

Synchronous execution

$request = ProductProjectionSearchRequest::of();
$response = $client->execute($request);
$products = $request->mapFromResponse($response);

Asynchronous execution

The asynchronous execution will return a promise to fulfill the request.

$response = $client->executeAsync(ProductProjectionSearchRequest::of());
$products = $request->mapFromResponse($response->wait());

Batch execution

By filling the batch queue and starting the execution all requests will be executed in parallel.

$responses = GuzzleHttp\Pool::batch(
    $client,
    [ProductProjectionSearchRequest::of()->httpRequest(), CartByIdGetRequest::ofId($cartId)->httpRequest()]
);

Using a logger

The client uses the PSR-3 logger interface for logging requests and deprecation notices. To enable logging provide a PSR-3 compliant logger (e.g. Monolog).

$logger = new \Monolog\Logger('name');
$logger->pushHandler(new StreamHandler('./requests.log'));
$client = ClientFactory::of()->createClient($config, $logger);

Using a cache adapter

The client will automatically request an OAuth token and store the token in the provided cache.

It's also possible to use a different cache adapter. The SDK provides a Doctrine, a Redis and an APCu cache adapter. By default the SDK tries to instantiate the APCu or a PSR-6 filesystem cache adapter if there is no cache given. E.g. Redis:

$redis = new \Redis();
$redis->connect('localhost');
$client = ClientFactory::of()->createClient($config, $logger, $redis);

Using cache and logger

$client = ClientFactory::of()->createClient($config, $logger, $cache);

Middlewares

Adding middlewares to the clients for Composable Commerce as well for the authentication can be done using the config by setting client options.

For using a custom HandlerStack

$handler = HandlerStack::create();
$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
    ...
    return $request; })
);
$config = Config::of()->setClientOptions(['handler' => $handler])

For using an array of middlewares

$middlewares = [
    Middleware::mapRequest(function (RequestInterface $request) {
    ...
    return $request; }),
    ...
]
$config = Config::of()->setClientOptions(['middlewares' => $middlewares])

Timeouts

The clients are configured to timeout by default after 60 seconds. This can be changed by setting the client options in the Config instance

$config = Config::of()->setClientOptions([
    'defaults' => [
        'timeout' => 10
    ]
])

Another option is to specify the timeout per request

$request = ProductProjectionSearchRequest::of();
$response = $client->execute($request, null, ['timeout' => 10]);

Retrying

As a request can error in multiple ways it's possible to add a retry middleware to the client config. E.g.: Retrying in case of service unavailable errors

$config = Config::of()->setClientOptions([
    'defaults' => [
        'timeout' => 10
    ]
])
$maxRetries = 3;
$clientOptions = [
    'middlewares' => [
        'retry' => Middleware::retry(
            function ($retries, RequestInterface $request, ResponseInterface $response = null, $error = null) use ($maxRetries) {
                if ($response instanceof ResponseInterface && $response->getStatusCode() < 500) {
                    return false;
                }
                if ($retries > $maxRetries) {
                    return false;
                }
                if ($error instanceof ServiceUnavailableException) {
                    return true;
                }
                if ($error instanceof ServerException && $error->getCode() == 503) {
                    return true;
                }
                if ($response instanceof ResponseInterface && $response->getStatusCode() == 503) {
                    return true;
                }
                return false;
            },
            [RetryMiddleware::class, 'exponentialDelay']
        )
    ]
];
$config->setClientOptions($clientOptions);

Using the phar distribution

Since version 1.6 the SDK is also released as a PHAR. You can find them in the releases section at Github.

Usage example:

<?php

require __DIR__ . '/commercetools-php-sdk.phar';

use Commercetools\Core\Client\ClientFactory;
use Commercetools\Core\Builder\Request\RequestBuilder;

$config = \Commercetools\Core\Config::fromArray([
    'client_id' => 'myClientId',
    'client_secret' => 'myClientSecret',
    'project' => 'myProjectId'
]);
$client = ClientFactory::of()->createClient($config);
$request = RequestBuilder::of()->project()->get();

$response = $client->execute($request);

$project = $request->mapFromResponse($response);
var_dump($project->toArray());

Improve & Contribute to the SDK project

Mac OS X preparations:

assuming Homebrew is installed, do the following:

xcode-select --install
brew tap homebrew/dupes
brew tap homebrew/versions
brew tap homebrew/homebrew-php
brew install php56
brew install php56-intl
brew install php56-xdebug
brew install ant
# you probably also need to fix a (=any) timezone in your php.ini:
echo "date.timezone='Europe/Berlin'" >> /usr/local/etc/php/5.6/conf.d/60-user.ini
# initialize the dependencies:
php composer.phar update

Linux preparations :

  • install php 5.6+, xdebug and ant according to their distro's package system.
  • make sure the curl, intl, mbstring and openssl extensions are activated in php.ini

Windows preparations:

  • install php 5.6+, i.e. extract ZIP and make add php.exe location to your PATH. Use WAMP etc. if you like, but plain PHP commandline is all you really need (you can test example code in the built-in webserver).
  • enable the curl, intl, mbstring and openssl extenstions in php.ini
  • make a working ant available in the PATH
  • and install composer.

Start working:

Clone the develop branch of the repository (we're using the gitflow branching model, so master is for releases only):

git clone [email protected]:commercetools/commercetools-php-sdk.git

Please follow the PSR-2 coding style, ideally via your IDE settings (see below for PhpStorm instructions).

Please make sure that exiting Unit and Integration tests don't fail and fully cover your new code with Unit Tests. You can run all tests locally:

ant

Built In Test Server

You can use the docroot directory with the built-in PHP web server. Add to the docroot directory a file called "myapp.yml". Add following content and setup with your API credentials:

parameters:
    client_id: my client id
    client_secret: my client secret
    project: my project id

Then activate the php builtin web server

cd <project_folder>
php -S localhost:8000 -t docroot

Now navigate to http://localhost:8000 in your browser.

PhpStorm configuration

To enable code style checks directly in PhpStorm you have to configure the path to the phpcs at Preferences > Languages & Frameworks > PHP > Code Sniffer. Now you can enable at Preferences > Editor > Inspections > PHP the "PHP code sniffer validation" with PSR-2 standard. Change the severity if needed.

Running integration tests

For running the integration tests you need an empty Project and have to create an API client using the commercetools Merchant Center with the scopes:

manage_project
view_orders
view_products
manage_my_shopping_lists
manage_my_orders
manage_my_payments
manage_my_profile
manage_api_clients
create_anonymous_token

Local environment

composer update
vendor/bin/phpunit

Using docker

Running the test image:

echo "COMMERCETOOLS_CLIENT_ID=YourClientID" > env.list
echo "COMMERCETOOLS_CLIENT_SECRET=YourClientSecret" >> env.list
echo "COMMERCETOOLS_PROJECT=YourProjectKey" >> env.list

docker run --env-file env.list -v $PWD:/opt/app -w /opt/app --rm=true jenschude/php-test-base tools/docker-phpunit.sh

Contribute

On bigger effort changes, please open a GitHub issue and ask if you can help or get help with your idea. For typos and documentation improvements just make a pull request.

Then:

  1. fork the repository on GitHub
  2. code and add tests that cover the created code. Your code should be warning-free.
  3. stick to PSR-2 and and don't reformat existing code.
  4. make a pull request. @jenschude will review it and pull or come back to you.

commercetools-php-sdk's People

Contributors

ashishhk avatar automation-commercetools avatar barbara79 avatar haehnchen avatar hajoeichler avatar ibrahim-abuelalaa avatar jarkt avatar jenschude avatar jherey avatar jhofm avatar migo315 avatar nikossvnk avatar nkuehn avatar quingkhaos avatar renovate-bot avatar schrank avatar skyriakou avatar teiling88 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

commercetools-php-sdk's Issues

Integration / System tests against live SPHERE API

functional depth can be discussed for the M2 release, but the general setup should be there and do full-trip calls including OAuth, Typed response models etc.

Must run on Travis, too, so we probably must create encrypted variables again etc.

Limit number of update actions in one request (+pooling helper)

The best practice limit must be preconfigured in the client (please ask SPHERE PM how much it is), but overrideable per Client instance.

If a request contains too many update actions, simply reject it with a message.

We can optionally create a special method that splits an Update Request into many with max amount of updates and returns a structure that can directly be passed into the Batch Pool Request API.

Remove Guzzle Dependency from User Code

As discussed. It's not necessary to expose the full depth of the guzzle API in an own API, but at least all Headers, the Status code and maybe raw text body.
Probably means that we have to wrap a bunch of exceptions, too.

This is a breaking change for the part of the user code that looks into the response details or handles guzzle exceptions.

maybe a separate "best practices" package with implemented use cases

Would it be a good Idea to provide a kind of "add on" package to the SDK that contains higher-level best practice code for typical use cases in a shopfront? Should be a separate Repo with own versioning.

e.g. a request that gets the cart for the final step in the checkout (you have to send a cart recalculate update request with reference expansion of the products and use the returned cart for display). Or a template request Object that creates a Cart that is configured to be usable as a user whishlist. etc. pp.

Stupid Idea?

Finish Typed Response Objects

We have the framework for typed response Objects (Models) and it will be filled piece by piece.

At some point (which is this ticket) we have to go through everything and complete the typing. Doesn't help and splitting it into pieces will just confuse users.

When done, we'll do performance optimizations

Review Namespace Concept

Especially:

  • should we switch to domain / endpoint focused subnamespaces in the "model" Namespace?
  • generally start with domain / endpoint?
  • How to handle the reusable models that aren't bound to an endpoint (e.g. Address)

Handle X-Correlation-ID header

SPHERE.IO API response contains a header called X-Correlation-ID. This header should also be used when handling events (e.g. warning, errors etc.).

Log warning if API returns the deprecated header

Since right now the API returns a special header if a deprecated endpoint, parameter or value syntax has been used.
The PHP SDK should log a Warning including the specific reason returned by the API (if that is the case).
Also, the documentation should explicitly call for enabling a logger to see this kind of stuff.

The header takes the following form :
"X-DEPRECATION-NOTICE" : "message"

Add an async / future / callback API for requests

Some requirements:

  • separate callback attachments per request object
  • common callback once all concurrent requests have finished
  • request chaining possible (via a callback that adds another request that has to finish before "everything finished" is called)

IMHO it would be okay to re-use the Guzzle API concept for callbacks etc. if that's functionally suitable. Many devs will know that API already.

Limit and set Pool size on Batch Requests

Not sure what the Guzzle default ist, but we need to make sure that the batchExecute API limits the number of concurrent requests but still makes sure that all requests will be done in the end ("concurrency rate limiting"?).

a default must be set (please check with SPHERE PM what's a good default), but should be overrideably per client instance.

Cart Draft Object + MVP update actions

Necessary update actions:

  • add line item (regular line item, custom line items can come later)
  • remove line item
  • change line item quantity
  • set email address
  • set billing address
  • set shipping address
  • set country
  • set set shipping method
  • set customer id
  • add/remove discount code
  • recalculate !!

MVP Order Request, Draft and Updates

(proably better do the customer before starting this)

As usal:

  • request with explicit required params.
  • draft object

Plus the following update actions:

  • create order
  • change payment state
  • set order number

Simple Concurrent Batch Requests API

Add a simple API to send multiple requests concurrently.
This is not yet meant for async / future type flows.

  • return object must allow simple mapping of result to request
  • requests are added one by one (separte calls "batchAdd"?) and sent via a special execute without parameters ("batchExecute()" ?)
  • normal "execute()" should not execute the waiting batch requests, too (different return object).

Implementation can more or less mirror the Guzzle batch API

MVP Customer Request, Drafts and Updates

probably helpful if this is done before cart and order

MVP update actions:

  • Name
  • email
  • address
  • shipping address
  • billing address
  • company name
  • date of birth
  • VAT ID (for B2B)

Finish cart updates

everyhing, e.g. the following update actions:

  • set custom shipping method
  • add / remove custom line items

Add working examples to README

I propose to fire a search request as the basic example and to do a category tree via reference expansion. (batch and future / async requests later once they're done)

Domain Model Caching

I would prefer to provide a well-configured Model cache instead of leaving that completely to the "userspace". Providing a cache on HTTP Level was an original Idea, but is not efficient from a performance point of view and is prone to confusion with real HTTP caches on the network level.

Use Case: Categories, User Cart, some custom objects etc.

Wishlist:

  • deserialization from cache faster than the "regular" stuff (probably hard or even not possible)
  • tightly integrated, but not intransparent API
  • separate cache provider than the one for Oauth configurable (domain objects can get too large for apcu, so a slower but bigger external cache makes sense).

Design decisions to be made

  • domain specific defaults for expiration time
  • how to handle references (serialize as children or flatten the stuff and put children and references that have an ID into the cache separately and get them back on deserialization? )
  • format? json_encode / PHP serialize / custom stuff like msgpack? First impression: either we just cache the raw "data" array (as json or whatever is fast and compact) plus a custom class prefix or we use PHP builtin serialization.
  • API / how to integrate tight

finally: does this make sense at all ? Or is this userspace.
PRO: it's easy to mess up stuff in this area if you don't know the structure of the API very exactly. So better do that in the SDK.
CON: too much application logic?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.