1
0
mirror of synced 2024-11-21 12:56:08 +03:00

New API client

This commit is contained in:
Pavel 2021-06-02 17:00:32 +03:00 committed by GitHub
parent 6ed0d7eb0d
commit 962ff0b1da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
788 changed files with 79633 additions and 11006 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{less,css,yml,json}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false

6
.env.dist Normal file
View File

@ -0,0 +1,6 @@
# This file can be used change api url and api key in tests. Useful when you want to use real networking for test case.
# Follow these steps if you want to use real networking for your test case:
# - Replace credentials below with your own.
# - Use `php-http/curl-client` as an argument for `TestClientFactory::createClient`.
API_URL=https://test.retailcrm.pro/
API_KEY=testkey

View File

@ -1,4 +1,4 @@
name: ci
name: CI
on:
push:
@ -8,26 +8,25 @@ on:
- '*.*'
pull_request:
env:
RETAILCRM_URL: ${{ secrets.RETAILCRM_URL }}
RETAILCRM_KEY: ${{ secrets.RETAILCRM_KEY }}
RETAILCRM_VERSION: ${{ secrets.RETAILCRM_VERSION }}
RETAILCRM_SITE: ${{ secrets.RETAILCRM_SITE }}
RETAILCRM_USER: ${{ secrets.RETAILCRM_USER }}
jobs:
test:
name: "PHPUnit"
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['7.0', '7.1', '7.2', '7.3', '7.4']
php-version: ['7.3', '7.4']
# TODO These dependencies blocks PHP 8.0 support for us:
# - `liip/serializer` and `liip/metadata-parser` - shouldn't be a big deal. It's clearly possible to fix that.
# - `phpmd/phpmd` - doesn't work for PHP 8.0 (it won't find any errors). Requires big changes in its dependencies, but work is already being done.
# php-version: ['7.3', '7.4', '8.0']
steps:
- uses: actions/checkout@v2
- name: Check out code into the workspace
uses: actions/checkout@v2
- name: Setup PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: xdebug
coverage: pcov
- name: Composer cache
uses: actions/cache@v2
with:
@ -35,7 +34,9 @@ jobs:
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
- name: Install dependencies
run: composer install -o
- name: Configure matchers
uses: mheap/phpunit-matcher-action@v1
- name: Run tests
run: php ./vendor/phpunit/phpunit/phpunit -c phpunit.xml.dist
run: composer run-script phpunit-ci
- name: Coverage
run: bash <(curl -s https://codecov.io/bash)

42
.github/workflows/code_quality.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: "Code Quality Check"
on:
pull_request:
paths:
- "**.php"
- "phpcs.xml"
- ".github/workflows/code_quality.yml"
jobs:
phpcs:
name: "PHP CodeSniffer"
runs-on: ubuntu-latest
steps:
- name: Check out code into the workspace
uses: actions/checkout@v2
- name: Run PHPCS
uses: chekalsky/phpcs-action@v1
phpmd:
name: "PHP MessDetector"
runs-on: ubuntu-latest
steps:
- name: Check out code into the workspace
uses: actions/checkout@v2
- name: Run PHPMD
uses: GeneaLabs/action-reviewdog-phpmd@1.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
level: 'warning'
reporter: github-pr-check
standard: './phpmd.xml'
target_directory: 'src'
phpstan:
name: PHPStan
runs-on: ubuntu-18.04
steps:
- name: Check out code into the workspace
uses: actions/checkout@v2
- name: Run PHPStan
uses: docker://oskarstark/phpstan-ga:0.12.88
with:
args: analyse src -c phpstan.neon --memory-limit=1G --no-progress

47
.github/workflows/documentation.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: phpDocumentor
on:
push:
branches:
- 'master'
tags:
- 'v*'
jobs:
test:
name: "phpDocumentor"
runs-on: ubuntu-latest
steps:
- name: Check GitHub Pages status
if: ${{ github.ref != 'refs/heads/master' }}
uses: crazy-max/ghaction-github-status@v2
with:
pages_threshold: major_outage
- name: Check out code into the workspace
if: success() && ${{ github.ref != 'refs/heads/master' }}
uses: actions/checkout@v2
- name: Setup PHP 7.4
if: ${{ github.ref != 'refs/heads/master' }}
uses: shivammathur/setup-php@v2
with:
php-version: "7.4"
- name: Cache phpDocumentor
id: cache-phpdocumentor
uses: actions/cache@v2
with:
path: phpDocumentor.phar
key: phpdocumentor
- name: Download latest phpDocumentor
if: steps.cache-phpdocumentor.outputs.cache-hit != 'true'
run: curl -O -L https://phpdoc.org/phpDocumentor.phar
- name: Generate documentation
if: ${{ github.ref != 'refs/heads/master' }}
run: php phpDocumentor.phar
- name: Deploy documentation to GitHub Pages
if: ${{ github.ref != 'refs/heads/master' }}
uses: crazy-max/ghaction-github-pages@v2
with:
target_branch: gh-pages
build_dir: docs/build/html
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

18
.gitignore vendored
View File

@ -1,10 +1,25 @@
# Composer files.
/vendor
/bin
composer.lock
composer.phar
# Code Quality tools artifacts.
coverage.xml
test-report.xml
phpunit.xml
.php_cs.cache
.phpunit.result.cache
# phpDocumentor files.
.phpdoc
docs
phpDocumentor.phar
# Ignore autogenerated code.
models/*.php
models/checksum.json
# Different environment-related files.
.idea
.DS_Store
.settings
@ -12,3 +27,4 @@ phpunit.xml
.project
.swp
/nbproject
.env

332
README.md
View File

@ -1,125 +1,293 @@
[![Build Status](https://github.com/retailcrm/api-client-php/workflows/ci/badge.svg)](https://github.com/retailcrm/api-client-php/actions)
[![Covarage](https://img.shields.io/codecov/c/gh/retailcrm/api-client-php/master.svg?logo=codecov&logoColor=white)](https://codecov.io/gh/retailcrm/api-client-php)
[![Build Status](https://github.com/retailcrm/api-client-php/workflows/CI/badge.svg)](https://github.com/retailcrm/api-client-php/actions)
[![Coverage](https://img.shields.io/codecov/c/gh/retailcrm/api-client-php/master.svg?logo=codecov&logoColor=white)](https://codecov.io/gh/retailcrm/api-client-php)
[![Latest stable](https://img.shields.io/packagist/v/retailcrm/api-client-php.svg)](https://packagist.org/packages/retailcrm/api-client-php)
[![PHP from Packagist](https://img.shields.io/packagist/php-v/retailcrm/api-client-php.svg?logo=php&logoColor=white)](https://packagist.org/packages/retailcrm/api-client-php)
# RetailCRM API PHP client
This is php RetailCRM API client. This library allows to use all available API versions. [API documentation](http://retailcrm.github.io/api-client-php)
This is the PHP RetailCRM API client. This library allows using of the actual API version.
You can find more info in the [documentation](doc/index.md).
# Table of contents
* [Requirements](#requirements)
* [Installation](#installation)
* [Usage](#usage)
* [Examples](#examples)
* [Notes](#notes)
* [Documentation](doc/index.md)
## Requirements
* PHP 5.4 and above
* PHP 7.3 and above
* PHP's cURL support
* PHP's JSON support
* PHP's Fileinfo support
* Any HTTP client compatible with PSR-18 (covered by the installation instructions).
* Any HTTP factories implementation compatible with PSR-17 (covered by the installation instructions).
* Any HTTP messages implementation compatible with PSR-7 (covered by the installation instructions).
* Other dependencies listed in the `composer.json` (covered by the installation instructions)
## Install
## Installation
1) Get [composer](https://getcomposer.org/download/)
Follow those steps to install the library:
2) Run into your project directory:
1. Download and install [Composer](https://getcomposer.org/download/) package manager.
2. Install the library from the Packagist by executing this command:
```bash
composer require retailcrm/api-client-php ~5.0
composer require retailcrm/api-client-php:"~6.0"
```
During the installation, you'll see a message which will look like this:
```sh
The following packages have new compilation tasks:
- retailcrm/api-client-php has 1 task
Allow these packages to compile? ([y]es, [a]lways, [n]o, [l]ist, [h]elp)
```
That's because the Client uses code generation to speed up serialization and deserialization of models in production. This code should be generated during installation or update. Without that code, the library itself will not work at all.
Just type `y` here and press Enter. The DTO cache will be generated after that.
If you skipped the compilation task - don't worry, it can be executed manually at any time with this command:
```sh
composer compile --all
```
If you have not used `composer` before, include autoloader into your project.
3. **Optional.** Disable compilation prompt that you have seen in the previous step. Run this command to do that:
```sh
./vendor/bin/retailcrm-client compiler:prompt
```
Replace `vendor/bin` with your bin directory path if it's different from the default. You can find more information about this step in the [documentation](doc/compilation_prompt.md).
**Note:** You should not skip this step if your application is using CI/CD pipeline because the interactive terminal is not available
in that environment which will result in failure during the dependencies installation.
4. Include the autoloader if it's not included, or you didn't use Composer before.
```php
require 'path/to/vendor/autoload.php';
```
Replace `path/to/vendor/autoload.php` with the correct path to Composer's `autoload.php`.
**Note:** API client uses `php-http/curl-client` and `nyholm/psr7` as a PSR-18, PSR-17 and PSR-7 implementation.
You can replace those implementations during installation by installing this library with the implementation of your choice, like this:
```sh
composer require symfony/http-client guzzlehttp/psr7 retailcrm/api-client-php:"~6.0"
```
More information about that can be found in the [documentation](doc/customization/different_psr_implementations.md).
## Usage
### Get order
Firstly, you should initialize the Client. The easiest way to do this is to use the `SimpleClientFactory`:
```php
$client = new \RetailCrm\ApiClient(
'https://demo.retailcrm.pro',
'T9DMPvuNt7FQJMszHUdG8Fkt6xHsqngH',
\RetailCrm\ApiClient::V5
);
$client = \RetailCrm\Api\Factory\SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
```
The client is separated into several resource groups, all of which are accessible through the Client's public properties.
You can call API methods from those groups like this:
```php
$client->api->credentials();
```
For example, you can retrieve the customers list:
```php
$client = \RetailCrm\Api\Factory\SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$response = $client->customers->list();
```
Or the orders list:
```php
$client = \RetailCrm\Api\Factory\SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$response = $client->orders->list();
```
To handle errors you must use two types of exceptions:
* `RetailCrm\Api\Interfaces\ClientExceptionInterface` for the network or other runtime errors.
* `RetailCrm\Api\Interfaces\ApiExceptionInterface` for the errors from the API.
An example of error handling can be found in the next section of this document.
Each resource group is responsible for the corresponding API section. For example, `costs` resource group provide methods
for costs manipulation and `loyalty` resource group allows interacting with loyalty programs, accounts, bonuses, etc.
Use annotations to determine which DTOs you need for sending the requests. If annotations are not provided by your IDE - you
probably should configure them. It'll ease your work with this (and any other) library a lot.
More information about the usage including examples can be found in the [documentation](doc/usage/usage.md).
## Examples
Listing orders:
```php
<?php
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
try {
$response = $client->request->ordersGet('M-2342');
} catch (\RetailCrm\Exception\CurlException $e) {
echo "Connection error: " . $e->getMessage();
$response = $client->orders->list();
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface and ClientExceptionInterface instance implements __toString() method.
exit(-1);
}
if ($response->isSuccessful()) {
echo $response->order['totalSumm'];
// or $response['order']['totalSumm'];
// or
// $order = $response->getOrder();
// $order['totalSumm'];
} else {
echo sprintf(
"Error: [HTTP-code %s] %s",
$response->getStatusCode(),
$response->getErrorMsg()
);
foreach ($response->orders as $order) {
printf("Order ID: %d\n", $order->id);
printf("First name: %s\n", $order->firstName);
printf("Last name: %s\n", $order->lastName);
printf("Patronymic: %s\n", $order->patronymic);
printf("Phone #1: %s\n", $order->phone);
printf("Phone #2: %s\n", $order->additionalPhone);
printf("E-Mail: %s\n", $order->email);
// error details
//if (isset($response['errors'])) {
// print_r($response['errors']);
//}
if ($order->customer instanceof CustomerCorporate) {
echo "Customer type: corporate\n";
} else {
echo "Customer type: individual\n";
}
foreach ($order->items as $item) {
echo PHP_EOL;
printf("Product name: %s\n", $item->productName);
printf("Quantity: %d\n", $item->quantity);
printf("Initial price: %f\n", $item->initialPrice);
}
echo PHP_EOL;
printf("Discount: %f\n", $order->discountManualAmount);
printf("Total: %f\n", $order->totalSumm);
echo PHP_EOL;
}
```
### Create order
```php
Fetching a specific order by it's ID:
$client = new \RetailCrm\ApiClient(
'https://demo.retailcrm.pro',
'T9DMPvuNt7FQJMszHUdG8Fkt6xHsqngH',
\RetailCrm\ApiClient::V5
);
```php
<?php
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Enum\ByIdentifier;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Request\BySiteRequest;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
try {
$response = $client->request->ordersCreate(array(
'externalId' => 'some-shop-order-id',
'firstName' => 'John',
'lastName' => 'Doe',
'items' => array(
//...
),
'delivery' => array(
'code' => 'fedex',
)
));
} catch (\RetailCrm\Exception\CurlException $e) {
echo "Connection error: " . $e->getMessage();
$response = $client->orders->get(1234, new BySiteRequest(ByIdentifier::ID, 'site'));
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface instance should implement __toString() method.
exit(-1);
}
if ($response->isSuccessful() && 201 === $response->getStatusCode()) {
echo 'Order successfully created. Order ID into RetailCRM = ' . $response->id;
// or $response['id'];
// or $response->getId();
} else {
echo sprintf(
"Error: [HTTP-code %s] %s",
$response->getStatusCode(),
$response->getErrorMsg()
);
// error details
//if (isset($response['errors'])) {
// print_r($response['errors']);
//}
}
echo 'Order: ' . print_r($response->order, true);
```
### Set custom headers and client timeout
Creating a new customer:
```php
$client = new \RetailCrm\ApiClient(
'https://demo.retailcrm.pro',
'T9DMPvuNt7FQJMszHUdG8Fkt6xHsqngH',
\RetailCrm\ApiClient::V5
);
<?php
$options = new \RetailCrm\Http\RequestOptions(
['X-Rlimit-Token' => 'example_token'], // array of custom headers
10 // client timeout (in seconds)
);
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Entity\Customers\Customer;
use RetailCrm\Api\Model\Request\Customers\CustomersCreateRequest;
$client->request->setOptions($options);
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$request = new CustomersCreateRequest();
$request->customer = new Customer();
$request->site = 'aliexpress';
$request->customer->email = 'john.doe@example.com';
$request->customer->firstName = 'John';
$request->customer->lastName = 'Doe';
try {
$response = $client->customers->create($request);
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface instance should implement __toString() method.
exit(-1);
}
echo 'Customer ID: ' . $response->id;
```
Creating a task for the user with a specific email:
```php
<?php
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Entity\Tasks\Task;use RetailCrm\Api\Model\Filter\Users\ApiUserFilter;
use RetailCrm\Api\Model\Request\Tasks\TasksCreateRequest;
use RetailCrm\Api\Model\Request\Users\UsersRequest;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$usersRequest = new UsersRequest();
$usersRequest->filter = new ApiUserFilter();
$usersRequest->filter->email = 'john.doe@example.com';
try {
$usersResponse = $client->users->list($usersRequest);
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface instance should implement __toString() method.
exit(-1);
}
if (0 === count($usersResponse->users)) {
echo 'User is not found.';
exit(-1);
}
$tasksRequest = new TasksCreateRequest();
$tasksRequest->task = new Task();
$tasksRequest->task->performerId = $usersResponse->users[0]->id;
$tasksRequest->task->text = 'Do something!';
$tasksRequest->site = 'site';
try {
$tasksResponse = $client->tasks->create($tasksRequest);
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface instance should implement __toString() method.
exit(-1);
}
echo 'Created task with ID: ' . $tasksResponse->id;
```
The error handling in the examples above is good enough for real production usage.
You can safely assume that `ApiExceptionInterface` is an error from the API, and `ClientExceptionInterface` is a client error
(e.g. network error or any runtime error, use `HttpClientException` to catch only PSR-18 client errors).
However, you can implement more complex error handling if you want.
Also, both `ApiExceptionInterface` and `ClientExceptionInterface` implements `__toString()`. This means that you can just
convert those exceptions to string and put the results into logs without any special treatment for the exception data.
More examples can be found in the [documentation](doc/usage/examples/index.md).
You can use a PSR-14 compatible event dispatcher to receive events from the client. See [documentation](doc/index.md) for details.
## Notes
This library uses HTTPlug abstractions. Visit [official documentation](http://docs.php-http.org/en/latest/httplug/users.html) to learn more about it.

49
bin/retailcrm-client Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env php
<?php
namespace RetailCrm\Api;
use ReflectionClass;
use RetailCrm\Api\Component\ComposerLocator;
use RetailCrm\Api\Component\PhpFilesIterator;
use Symfony\Component\Console\Application;
if (!in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
}
set_time_limit(0);
require __DIR__ . '/../src/Component/ComposerLocator.php';
$composerAutoloader = ComposerLocator::findAutoloader();
if ('' === $composerAutoloader) {
echo 'Cannot find autoload.php. Please install dependencies first.' . PHP_EOL;
exit(-1);
}
require $composerAutoloader;
if (!class_exists('Symfony\Component\Console\Application')) {
echo 'Cannot find Symfony\Component\Console\Application class. Please install dependencies first.';
exit(-1);
}
$application = new Application();
$commands = new PhpFilesIterator(implode(DIRECTORY_SEPARATOR, [dirname(__DIR__), 'src', 'Command']));
foreach ($commands as $command) {
if (!array_key_exists('fqn', $command)) {
continue;
}
$commandFqn = $command['fqn'];
if (class_exists($commandFqn) && !(new ReflectionClass($commandFqn))->isAbstract()) {
$application->add(new $commandFqn());
}
}
$application->setName('RetailCRM API Client Management Tool');
$application->run();

View File

@ -2,7 +2,11 @@
"name": "retailcrm/api-client-php",
"description": "PHP client for RetailCRM API",
"type": "library",
"keywords": ["API", "RetailCRM", "REST"],
"keywords": [
"API",
"RetailCRM",
"REST"
],
"homepage": "http://www.retailcrm.pro/",
"license": "MIT",
"authors": [
@ -12,28 +16,101 @@
}
],
"require": {
"php": ">=5.4.0",
"ext-curl": "*",
"php": ">=7.3.0",
"ext-json": "*",
"ext-fileinfo": "*"
"psr/log": "^1.1",
"psr/http-client": "^1.0",
"psr/http-message": "^1.0",
"psr/http-message-implementation": "^1.0",
"php-http/client-implementation": "^1.0",
"php-http/message-factory": "^1.0",
"php-http/discovery": "^1.13",
"doctrine/annotations": "^1.11",
"liip/serializer": "^2.0",
"php-http/httplug": "^2.2",
"civicrm/composer-compile-plugin": "^0.15.0",
"symfony/console": "^4.0|^5.0",
"psr/event-dispatcher": "^1.0",
"neur0toxine/psr.http-client-implementation.php-http-curl": "*",
"neur0toxine/psr.http-factory-implementation.nyholm": "*",
"neur0toxine/psr.http-message-implementation.nyholm": "*",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"symfony/cache": ">=v3.1.0"
},
"require-dev": {
"phpunit/phpunit": "6.*",
"squizlabs/php_codesniffer": "3.*"
"php-http/mock-client": "^1.0",
"squizlabs/php_codesniffer": "^3.5",
"phpmd/phpmd": "^2.10",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.1",
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^0.12.74",
"vlucas/phpdotenv": "^5.3",
"phpunit/phpunit": "^9.5",
"php-http/curl-client": "^2.2",
"nyholm/psr7": "^1.3",
"league/event": "^3.0",
"league/container": "^3.3",
"neur0toxine/pock": "^0.7"
},
"suggest": {
"ext-curl": "Most HTTP clients need ext-curl to work properly.",
"php-http/client-implementation": "PSR-18 compatible client should be available to use this library.",
"psr/http-message-implementation": "PSR-7 compatible HTTP message implementation should be available to use to use this library.",
"psr/http-factory-implementation": "PSR-17 compatible factories should be available to use this library.",
"symfony/event-dispatcher": "PSR-14 compatible event dispatcher.",
"league/event": "PSR-14 compatible event dispatcher.",
"nyholm/psr7": "This is recommended PSR-7 and PSR-17 implementation.",
"php-http/curl-client": "Simplest PSR-18 client implementation.",
"symfony/http-client": "One of the most popular HTTP clients. Has PSR-18 compatible adapter.",
"psr/log-implementation": "You can use log implementation for debug purposes."
},
"scripts": {
"phpunit": "./vendor/bin/phpunit -c phpunit.xml.dist --coverage-text",
"phpunit-ci": "@php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude=\"~vendor~\" ./vendor/bin/phpunit --teamcity -c phpunit.xml.dist",
"phpmd": "./vendor/bin/phpmd src text ./phpmd.xml",
"phpcs": "./vendor/bin/phpcs -p src --runtime-set testVersion 7.3-8.0 && ./vendor/bin/phpcs -p tests --runtime-set testVersion 7.3-8.0 --warning-severity=0",
"phpstan": "./vendor/bin/phpstan analyse -c phpstan.neon src --memory-limit=-1",
"lint:fix": "./vendor/bin/phpcbf src",
"lint": [
"@phpcs",
"@phpmd",
"@phpstan"
],
"verify": [
"@lint",
"@phpunit"
],
"models": "@php bin/retailcrm-client models:generate --all"
},
"support": {
"email": "support@retailcrm.pro"
},
"autoload": {
"psr-0": { "RetailCrm\\": "lib/" }
"psr-4": {
"RetailCrm\\Api\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"RetailCrm\\TestUtils\\": "tests/utils/",
"RetailCrm\\Tests\\": "tests/src/"
}
},
"bin": [
"bin/retailcrm-client"
],
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
}
"dev-master": "6.x-dev"
},
"compile": [
{
"run": "@composer run-script models"
}
]
},
"config": {
"bin-dir": "vendor/bin",
"process-timeout": 600
}
}
}

83
doc/compilation_prompt.md Normal file
View File

@ -0,0 +1,83 @@
## Compilation prompt
After almost every Composer operation you will see this prompt:
```sh
The following packages have new compilation tasks:
- retailcrm/api-client-php has 1 task
Allow these packages to compile? ([y]es, [a]lways, [n]o, [l]ist, [h]elp)
```
That's because the API client utilizes code generation to speed up the serialization and deserialization of the requests. However,
this prompt may be annoying and sometimes can even break the application lifecycle pipeline (in the CI/CD environment). We can't just
disable it for everyone [because of security concerns](https://github.com/composer/composer/issues/1193). But you can disable it for your project.
There are two ways of disabling this prompt:
1. Automated way.
2. Manual way.
### Disable or enable compilation prompt via CLI
For the automated way, you can use `retailcrm-client` CLI utility. It will be in your binary directory. By default, it'll be in the
`vendor/bin` directory if not defined otherwise in the composer.json `config.bin-dir` entry.
#### Disabling compilation prompt
You can disable the compiler prompt by running this command:
```sh
./vendor/bin/retailcrm-client compiler:prompt
```
Replace `vendor/bin` with your bin directory path if it's different from the default.
You should see this message after that:
```sh
✓ Done, generator prompt is now enabled.
```
#### Enabling compilation prompt
If you want to revert this change and enable the compilation prompt then just run this command again with the `--activate` flag:
```sh
./vendor/bin/retailcrm-client compiler:prompt --activate
```
### Disable or enable compilation prompt manually
#### Enabling compilation prompt
It is possible to replicate the same actions manually. Add these params into the `extra` segment of your `composer.json` if
you want to execute code generation automatically after library installation or update.
```json
"compile-mode": "whitelist",
"compile-whitelist": ["retailcrm/api-client-php"]
```
Your `composer.json` file will look like this:
```json
{
"name": "author/some-project",
"description": "Description of the project.",
"type": "project",
"license": "MIT",
"require": {
"php": ">=7.3.0",
"symfony/http-client": "^5.2",
"nyholm/psr7": "^1.4",
"retailcrm/api-client-php": "~6.0"
},
"extra": {
"compile-mode": "whitelist",
"compile-whitelist": ["retailcrm/api-client-php"]
}
}
```
Voilà! You won't see the annoying prompt again.
#### Enabling compilation prompt
Just remove `extra.compile-mode` and `extra.compile-whitelist` params from your `composer.json`.

View File

@ -0,0 +1,19 @@
## Customization
* [Using different PSR-18, PSR-17 and PSR-7 implementations](different_psr_implementations.md)
* [Customizing request and response processing](pipelines/implementing_a_handler.md)
+ [Using a predefined handler](pipelines/using_a_predefined_handler.md)
+ [Built-in handlers](pipelines/using_a_predefined_handler.md#built-in-handlers)
+ [Modifying the default pipeline](pipelines/using_a_predefined_handler.md#modifying-the-default-pipeline)
+ [Constructing the pipeline from scratch](pipelines/using_a_predefined_handler.md#constructing-the-pipeline-from-scratch)
+ [Implementing a handler](pipelines/implementing_a_handler.md)
Both `ClientFactory` and `ClientBuilder` provide the necessary functionality to replace PSR dependencies with any other compatible implementation.
By default, those dependencies will be detected via service discovery. But service discovery supports a limited amount of implementation.
If your implementation is not supported - the client won't work unless you provide the necessary dependencies manually.
Another case would be testing. You can provide special HTTP client implementation which will return mocked responses instead of making
real requests.
The Client uses [chain of responsibility](https://refactoring.guru/design-patterns/chain-of-responsibility) pattern to process requests.
Each request and response is being processed by the pipeline of handlers. Every handler can apply some mutation to the request or response
and can pass it to the next handler. In fact, API authentication is made possible by using a special handler which appends API key to every request.

View File

@ -0,0 +1,69 @@
## Controlling HTTP abstraction layer
You can replace default PSR dependencies in the client while using `ClientFactory` or `ClientBuilder`. It can be useful for tests
or if you want to use a specific implementation that is not supported by the service discovery.
Both `ClientFactory` and `ClientBuilder` provide those methods:
```php
/**
* Set your PSR-18 HTTP client.
*
* Service discovery will be used if no client has been provided.
*
* @param \Psr\Http\Client\ClientInterface $httpClient
*/
public function setHttpClient(\Psr\Http\Client\ClientInterface $httpClient);
/**
* Sets PSR-17 compatible stream factory. You can skip this step if you want to use service discovery.
*
* @param \Psr\Http\Message\StreamFactoryInterface|null $streamFactory
*/
public function setStreamFactory(?\Psr\Http\Message\StreamFactoryInterface $streamFactory);
/**
* Sets PSR-17 compatible request factory. You can skip this step if you want to use service discovery.
*
* @param \Psr\Http\Message\RequestFactoryInterface|null $requestFactory
*/
public function setRequestFactory(?\Psr\Http\Message\RequestFactoryInterface $requestFactory);
/**
* Sets PSR-17 compatible URI factory. You can skip this step if you want to use service discovery.
*
* @param \Psr\Http\Message\UriFactoryInterface|null $uriFactory
*/
public function setUriFactory(?\Psr\Http\Message\UriFactoryInterface $uriFactory);
```
They can be used to specify PSR dependencies like this:
```php
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$factory = new \RetailCrm\Api\Factory\ClientFactory();
$factory->setHttpClient(new \Http\Client\Curl\Client())
->setRequestFactory($psr17Factory)
->setStreamFactory($psr17Factory)
->setUriFactory($psr17Factory);
$client = $factory->createClient('https://test.retailcrm.pro', 'apiKey');
```
Or like this:
```php
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$builder = new \RetailCrm\Api\Builder\ClientBuilder();
$client = $builder
->setApiUrl('https://test.retailcrm.pro')
->setAuthenticatorHandler(new \RetailCrm\Api\Handler\Request\HeaderAuthenticatorHandler('apiKey'))
->setHttpClient(new \Http\Client\Curl\Client())
->setRequestFactory($psr17Factory)
->setStreamFactory($psr17Factory)
->setUriFactory($psr17Factory)
->build();
```
By replacing the HTTP client in the test environment you can easily mock requests and responses via libraries like
[`neur0toxine/pock`](https://packagist.org/packages/neur0toxine/pock) or [`php-http/mock-client`](https://packagist.org/packages/php-http/mock-client).

View File

@ -0,0 +1,23 @@
## Implementing a handler
You can implement your own handler using the `RetailCrm\Api\Interfaces\HandlerInterface`.
`RetailCrm\Api\Handler\AbstractHandler` provides boilerplate code for the chain of responsibility.
`AbstractHandler::next` method will call next handler in the chain. You can safely use `return parent::next()` in your code
while using `AbstractHandler`.
Most of the information about how handlers operate can be found in the [chain of responsibility](https://refactoring.guru/design-patterns/chain-of-responsibility)
pattern explanation. There are some specific details about handlers in the client. Client will pass desired dependencies to
the handler if handler implements one of those interfaces:
* Any request handler
+ `RetailCrm\Api\Interfaces\PsrFactoriesAwareInterface` will inject PSR-17 factories into the handler.
* Any response handler
+ `RetailCrm\Api\Interfaces\SerializerAwareInterface` will inject a `liip/serializer` instance into the handler.
+ `RetailCrm\Api\Interfaces\ApiExceptionFactoryAwareInterface` will inject a `Liip\Serializer\SerializerInterface` instance into the handler.
+ `RetailCrm\Api\Interfaces\EventDispatcherAwareInterface` will inject a `Psr\EventDispatcher\EventDispatcherInterface` instance into the handler.
All of those interfaces above have the corresponding implementation traits in the `RetailCrm\Api\Traits` namespace.
For example, `RetailCrm\Api\Interfaces\EventDispatcherAwareInterface` implementation can be found in the
`RetailCrm\Api\Traits\EventDispatcherAwareTrait` trait.
Handlers can be used in the both chains just like callback handlers (see ["using a predefined hander"](using_a_predefined_handler.md)).

View File

@ -0,0 +1,292 @@
## Using a predefined handler
* [Built-in handlers](#built-in-handlers)
* [Modifying the default pipeline](#modifying-the-default-pipeline)
* [Constructing the pipeline from scratch](#constructing-the-pipeline-from-scratch)
### Built-in handlers
You can use a predefined handler to modify the process of request or response processing. Most of the predefined handlers are
already in use by the client, except for the `CallbackRequestHandler` and `CallbackResponseHandler`.
Those are handlers that are present in the client and can be used for request processing:
* `CallbackRequestHandler` - processes a request using provided callback.
* `GetParameterAuthenticatorHandler` - appends a GET `apiKey` parameter with the API key.
* `HeaderAuthenticatorHandler` - appends `X-Api-Key` header with the API key.
* `PsrRequestHandler` - constructs base PSR-7 request with method and URI.
* `RequestDataHandler` - fills base PSR-7 request with the data from the DTO.
Request handlers are using `RetailCrm\Api\Model\RequestData` DTO to share state between handlers.
Those are handlers that can be used for response processing:
* `AccountNotFoundHandler` - detects when RetailCRM with specified API URL is not exist and throws specified exception.
* `CallbackResponseHandler` - processes a response using provided callback.
* `ErrorResponseHandler` - detects an error response, throws correct exception.
* `FilesDownloadResponseHandler` - handles `/api/v5/files/download` response.
* `UnmarshalResponseHandler` - unmarshals response body into response DTO.
Response handlers are using `RetailCrm\Api\Model\ResponseData` DTO to share state between handlers.
There are two built-in handlers which are most useful to users who want to modify the processing pipelines: `CallbackRequestHandler` and
`CallbackResponseHandler`. Both handlers use provided callbacks to modify a request or response. You can initialize those
with the examples below:
```php
use RetailCrm\Api\Handler\Request\CallbackRequestHandler;
use RetailCrm\Api\Model\RequestData;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
$handler = new CallbackRequestHandler(
static function (
RequestData $requestData,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory,
UriFactoryInterface $uriFactory
) {
if (null !== $requestData->request) {
$requestData->request = $requestData->request->withHeader('X-Rlimit-Token', 'example_token');
}
}
);
```
```php
use RetailCrm\Api\Exception\Api\MissingParameterException;
use RetailCrm\Api\Factory\ApiExceptionFactory;
use RetailCrm\Api\Handler\Response\CallbackResponseHandler;
use RetailCrm\Api\Model\Response\Api\Credentials;
use RetailCrm\Api\Model\Response\ErrorResponse;
use RetailCrm\Api\Model\ResponseData;
use Liip\Serializer\SerializerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
$handler = new CallbackResponseHandler(
static function (
ResponseData $data,
SerializerInterface $serializer,
EventDispatcherInterface $eventDispatcher,
ApiExceptionFactory $apiExceptionFactory
) {
if (
$data->responseModel instanceof Credentials &&
!in_array('/api/customers/create', $data->responseModel->credentials)
) {
$data->responseModel = new ErrorResponse();
$data->responseModel->errorMsg = 'Parameter "/api/customers/create" is missing';
throw new MissingParameterException($data->responseModel, 400);
}
}
);
```
Both handlers above can be appended to request and response pipelines. Let's move on to the next part to learn how
you can append handlers to the chain using `ClientFactory` or `ClientBuilder`.
### Modifying the default pipeline
Two limitations apply to default pipeline modifying:
1. Chain ordering cannot be changed. Your handler will always be at the end of the default chain.
1. Callback handlers will always call the next handler. There is no way to stop the next handlers in the chain.
However, you can construct your own pipeline which won't be bound by the first limitation. And the second limitation can be
circumvented by [implementing your own handler](implementing_a_handler.md).
Both `ClientFactory` and `ClientBuilder` allow you to append your own handlers to the processing pipeline. They provide
those methods:
```php
/**
* Appends an additional request handler into the request processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface $handler
*/
public function appendRequestHandler(HandlerInterface $handler);
/**
* Appends an additional handler into the response processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface $handler
*/
public function appendResponseHandler(HandlerInterface $handler);
/**
* Appends an additional request handlers into the request processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface[] $handlers
*/
public function appendRequestHandlers(array $handlers);
/**
* Appends an additional response handlers into the response processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface[] $handlers
*/
public function appendResponseHandlers(array $handlers);
```
You can use those methods to pass the handler into desired chain. Take a look at the examples. This code will initialize the client
with provided `CallbackRequestHandler`. Instantiation will be done via `ClientFactory`:
```php
use RetailCrm\Api\Factory\ClientFactory;
use RetailCrm\Api\Handler\Request\CallbackRequestHandler;
use RetailCrm\Api\Model\RequestData;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
$handler = new CallbackRequestHandler(
static function (
RequestData $requestData,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory,
UriFactoryInterface $uriFactory
) {
if (null !== $requestData->request) {
$requestData->request = $requestData->request->withHeader('X-Rlimit-Token', 'example_token');
}
}
);
$factory = new ClientFactory();
$client = $factory->appendRequestHandler($handler)->createClient('https://test.retailcrm.pro', 'apiKey');
```
And this code will instantiate a client using `ClientBuilder`. But this time we're modifying response pipeline:
```php
use RetailCrm\Api\Builder\ClientBuilder;
use RetailCrm\Api\Builder\FormEncoderBuilder;
use RetailCrm\Api\Exception\Api\MissingParameterException;
use RetailCrm\Api\Factory\ApiExceptionFactory;
use RetailCrm\Api\Handler\Request\HeaderAuthenticatorHandler;
use RetailCrm\Api\Handler\Response\CallbackResponseHandler;
use RetailCrm\Api\Model\Response\Api\Credentials;
use RetailCrm\Api\Model\Response\ErrorResponse;
use RetailCrm\Api\Model\ResponseData;
use Liip\Serializer\SerializerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
$handler = new CallbackResponseHandler(
static function (
ResponseData $data,
SerializerInterface $serializer,
EventDispatcherInterface $eventDispatcher,
ApiExceptionFactory $apiExceptionFactory
) {
if (
$data->responseModel instanceof Credentials &&
!in_array('/api/customers/create', $data->responseModel->credentials)
) {
$data->responseModel = new ErrorResponse();
$data->responseModel->errorMsg = 'Parameter "/api/customers/create" is missing';
throw new MissingParameterException($data->responseModel, 400);
}
}
);
$formEncoder = (new FormEncoderBuilder())->setCacheDir('cache')->build();
$builder = new ClientBuilder();
$client = $builder->setApiUrl('https://test.retailcrm.pro')
->setAuthenticatorHandler(new HeaderAuthenticatorHandler('apiKey'))
->setFormEncoder($formEncoder)
->appendRequestHandler($handler)
->build();
```
That's how you can modify request and response processing chains. However, you also can use the default pipeline factory
to pass desired handlers into the chain. Default request and response pipelines are constructed by those static factories:
```php
RetailCrm\Api\Factory\RequestPipelineFactory::createDefaultPipeline()
RetailCrm\Api\Factory\ResponsePipelineFactory::createDefaultPipeline()
```
Look at the example below to learn how to use those static factories. In this example we will modify both the request
and response pipelines:
```php
use Liip\Serializer\SerializerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use RetailCrm\Api\Builder\ClientBuilder;
use RetailCrm\Api\Builder\FormEncoderBuilder;
use RetailCrm\Api\Component\Transformer\RequestTransformer;
use RetailCrm\Api\Component\Transformer\ResponseTransformer;
use RetailCrm\Api\Exception\Api\MissingParameterException;
use RetailCrm\Api\Factory\ApiExceptionFactory;
use RetailCrm\Api\Factory\RequestPipelineFactory;
use RetailCrm\Api\Factory\ResponsePipelineFactory;
use RetailCrm\Api\Handler\Request\CallbackRequestHandler;
use RetailCrm\Api\Handler\Request\HeaderAuthenticatorHandler;
use RetailCrm\Api\Handler\Response\CallbackResponseHandler;
use RetailCrm\Api\Model\RequestData;
use RetailCrm\Api\Model\Response\Api\Credentials;
use RetailCrm\Api\Model\Response\ErrorResponse;
use RetailCrm\Api\Model\ResponseData;
$requestHandler = new CallbackRequestHandler(
static function (
RequestData $requestData,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory,
UriFactoryInterface $uriFactory
) {
if (null !== $requestData->request) {
$requestData->request = $requestData->request->withHeader('X-Rlimit-Token', 'example_token');
}
}
);
$responseHandler = new CallbackResponseHandler(
static function (
ResponseData $data,
SerializerInterface $serializer,
EventDispatcherInterface $eventDispatcher,
ApiExceptionFactory $apiExceptionFactory
) {
if (
$data->responseModel instanceof Credentials &&
!in_array('/api/customers/create', $data->responseModel->credentials)
) {
$data->responseModel = new ErrorResponse();
$data->responseModel->errorMsg = 'Parameter "/api/customers/create" is missing';
throw new MissingParameterException($data->responseModel, 400);
}
}
);
$builder = new ClientBuilder();
$formEncoder = (new FormEncoderBuilder())->setCacheDir('cache')->build();
$client = $builder->setApiUrl('https://test.retailcrm.pro')
->setAuthenticatorHandler(new HeaderAuthenticatorHandler('apiKey'))
->setRequestTransformer(new RequestTransformer(
RequestPipelineFactory::createDefaultPipeline(
$formEncoder,
null, // PSR factories will be found by the service discovery.
null,
null,
$requestHandler
)
))
->setResponseTransformer(new ResponseTransformer(
ResponsePipelineFactory::createDefaultPipeline(
$formEncoder->getSerializer(),
new ApiExceptionFactory(),
null, // No EventDispatcherInterface was provided
$responseHandler
)
))->build();
```
### Constructing the pipeline from scratch
Both `RequestTransformer` and `ResponseTransformer` accept the `HandlerInterface` instance as the first argument. You can
look into `RequestPipelineFactory` and `ResponsePipelineFactory` source code to learn how to build your own pipeline from
scratch. This is, however, is discouraged.

29
doc/index.md Normal file
View File

@ -0,0 +1,29 @@
# Documentation
* [Compilation prompt](compilation_prompt.md)
+ [Disable or enable compilation prompt via CLI](compilation_prompt.md#disable-or-enable-compilation-prompt-via-cli)
+ [Disable or enable compilation prompt manually](compilation_prompt.md#disable-or-enable-compilation-prompt-manually)
* [Client structure](structure.md)
+ [Design principles](structure.md#design-principles)
+ [Resource groups](structure.md#resource-groups)
* [Usage](usage/usage.md)
+ [Instantiation](usage/instantiation.md)
+ [Sending a request](usage/sending_a_request.md)
+ [Choosing correct resource group, DTOs, and method](usage/sending_a_request.md#choosing-correct-resource-group-dtos-and-method)
+ [Sending a request](usage/sending_a_request.md#sending-a-request)
+ [Dealing with exceptions](usage/sending_a_request.md#dealing-with-exceptions)
+ [Error handling](usage/error_handling.md)
+ [Examples](usage/examples)
+ [How to create an order](usage/examples/create_order.md)
+ [How to receive the list of orders](usage/examples/fetch_orders.md)
+ [How to handle all Client's exceptions](usage/examples/complete_error_handling_example.md)
+ [Event handling](usage/event_handing.md)
* [Customization](customization/customization.md)
+ [Controlling HTTP abstraction layer](customization/different_psr_implementations.md)
+ [Customizing request and response processing](customization/pipelines/implementing_a_handler.md)
+ [Using a predefined handler](customization/pipelines/using_a_predefined_handler.md)
+ [Built-in handlers](customization/pipelines/using_a_predefined_handler.md#built-in-handlers)
+ [Modifying the default pipeline](customization/pipelines/using_a_predefined_handler.md#modifying-the-default-pipeline)
+ [Constructing the pipeline from scratch](customization/pipelines/using_a_predefined_handler.md#constructing-the-pipeline-from-scratch)
+ [Implementing a handler](customization/pipelines/implementing_a_handler.md)
* [Troubleshooting](troubleshooting.md)

45
doc/structure.md Normal file
View File

@ -0,0 +1,45 @@
## Client structure
The client is separated into several resource groups, all of which are accessible through the Client's public properties. Each resource group is responsible for the corresponding API section. It is much easier to understand how the Client works by learning its design principles.
### Design principles
The general principles of the Client design are:
1. The Client itself doesn't contain any API methods implementations.
1. ResourceGroups implements all API methods.
1. Every method can accept from zero to several arguments.
1. URI path arguments (not the query string ones) are always passed as separate method arguments.
1. Query parameters are always passed in the form of the Request model. The same principle applies to the POST form data.
1. If the Request contains only one parameter - it'll probably accept it through the constructor. This doesn't apply to the filter requests, which don't have constructors at all.
1. All DTO's you'll ever need can be found in the `RetailCrm\Api\Model` namespace.
1. Every method has an example of the usage in its DocBlock. Yes, all of them. You can learn how to use any of the methods just by looking at the DocBlock help tooltip provided by your IDE. If it's not provided by your IDE - you'll need to look up how to configure it. It'll ease your work with this (and any other) library a lot.
### Resource groups
The resource groups list into which client is separated:
* `api`
* `costs`
* `customFields`
* `customers`
* `customersCorporate`
* `delivery`
* `files`
* `integration`
* `loyalty`
* `orders`
* `packs`
* `payments`
* `references`
* `segments`
* `settings`
* `store`
* `tasks`
* `telephony`
* `users`
* `verification`
* `statistics`
Every group implements corresponding API documentation block:
* [English](https://docs.retailcrm.pro/Developers/API/APIVersions/APIv5)
* [Русский](https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5)

59
doc/troubleshooting.md Normal file
View File

@ -0,0 +1,59 @@
### I've got error that looks like this: `Cannot deserialize body: Type "RetailCrm\Api\Model\Response\Api\ApiVersionsResponse" is not known.`
Run this command to fix the problem:
```sh
composer compile --all
```
The details about that error can be found in the [documentation](compilation_prompt.md).
### I've got "No Message Factories" error.
This means that PSR-17 implementation you have is not supported by the service discovery, therefore, API client cannot find proper factories.
In order to use your own PSR-17 implementation you need to use `ClientBuilder`. Also, you can just install supported implementation which is
much easier:
```sh
composer require nyholm/psr7
```
### I've got `Http\Discovery\Exception\DiscoveryFailedException` or any other error with message like "`Could not find resource using any discovery strategy`".
That's because you don't have any supported PSR-18, PSR-7 or PSR-17 implementation available. This usually happens if you do have any implementation for those
standards, but it's not supported by service discovery. You can fix this easily by installing supported implementations. We recommend using `symfony/http-client`
and `nyholm/psr7`. Install those using this command:
```sh
composer require nyholm/psr7 symfony/http-client
```
Alternatively, you can use `ClientBuilder` which allows you to pass your own HTTP client and message & URI factories.
### There are too many available exceptions! How do I catch them all?
Every exception in the library implements either `ApiExceptionInterface` or `ClientExceptionInterface`. First will be thrown in case of any
errors from the API, and the second will be thrown in case of any problems with the client or network itself. Concrete exception types are meant
to be used to determine what exactly gone wrong while sending a request.
Also, you can use PSR-14 compatible event dispatcher to handle some exceptions globally. The Client will send `FailureRequestEvent` in case of any exceptions.
You can call `FailureRequestEvent::suppressThrow()` to prevent client from throwing an exception.
### I can't test my app in the CI because Composer fails while installing dependencies.
That's because you should explicitly allow code generation for the library. Otherwise, Composer will ask you to run compilation task at runtime
which is not possible in the CI since it lacks the support for interactive input.
You can allow code generation tasks without interactive approval. Just add parameters from the JSON below to
the `"extra"` key of your project's `composer.json` file.
```json
{
"compile-mode": "whitelist",
"compile-whitelist": ["retailcrm/api-client-php"]
}
```
### How can I choose proper DTO class for my request?
Request DTO's can be found in the `RetailCrm\Api\Model\Request` namespace. They are separated by the API resource groups.
For example, requests for interaction with customer entities can be found in the `RetailCrm\Api\Model\Request\Customers` namespace
and requests for operations with the orders can be found in the `RetailCrm\Api\Model\Request\Orders` namespace. Every request method
defines it's input and output types. Also, you can choose correct type for child entities by looking at type annotations.

View File

@ -0,0 +1,52 @@
## Error handling
The API Client exceptions are hierarchical. It means that groups of exceptions can be caught using more generic exception types.
Let's take a look at the exceptions hierarchy:
- `ApiException` is an abstraction and will never be thrown by itself.
- `AccessDeniedException` is thrown if the `Access denied.` error string is received from the API.
- `AccountDoesNotExistException` is thrown if the RetailCRM account with a specified API URL does not exist.
- `ApiErrorException` is thrown in case of any generic API error which is not recognized as a more specific type.
- `InvalidCredentialsException` is thrown if the provided API key is not correct.
- `MissingCredentialsException` is thrown if no API key was provided (this can happen if you initialize the Client without an authenticator).
- `MissingParameterException` is thrown if `Parameter '*' is missing` error is returned from the API (`*` is any parameter name).
- `ValidationException` is thrown if request validation is failed on the server-side (incorrect request data).
- `ClientException` is an abstraction and will never be thrown by itself.
- `BuilderException` is thrown by any builder if something goes wrong.
- `HandlerException` is thrown by the handlers in the request / response processing chain if they encountered an error.
- `HttpClientException` is thrown if underlying HTTP client throws an exception.
`ApiException` implements `ApiExceptionInterface` and `ClientException` implements `ClientExceptionInterface`.
It means that you can catch any exception in the `ApiException` leaf using `ApiExceptionInterface` as an exception type.
Also, you can catch any exception in the `ClientException` leaf using the `ClientExceptionInterface` type.
`ClientExceptionInterface` and `ApiExceptionInterface` implements `__toString()`. The first one will return a string with an error
message and stack trace and the second one will print out the error message, stack trace, and error response data.
Let's take a look at the example:
```php
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
try {
$apiVersions = $client->api->apiVersions();
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception;
return;
}
echo 'Available API versions: ' . implode(', ', $apiVersions->versions);
```
All Client exceptions can be found in the `RetailCrm\Api\Exception` namespace. `ApiException` children reside in the `RetailCrm\Api\Exception\Api` namespace,
and `ClientException` children reside at the `RetailCrm\Api\Exception\Client` namespace.
It is recommended to look at the [complete error handling example](examples/complete_error_handling_example.md).
The most useful case would be the error message generation for the client in your app, which can look like this:
- `InvalidCredentialsException` - show the `Invalid API key` message.
- `AccountDoesNotExistException` - show the `Incorrect RetailCRM URL` or `This RetailCRM instance does not exist` message.
- `AccessDeniedException` - show the `Please enable the /api/v5/... method for this key` message.
- and so on.

105
doc/usage/event_handing.md Normal file
View File

@ -0,0 +1,105 @@
## Events
You can use a PSR-14 compatible event dispatcher to receive events from the client.
It may be useful if you want to process certain events with the same logic without duplicating calls to such code.
These events are provided by the library:
- `RetailCrm\Api\Event\SuccessRequestEvent` will be dispatched if the request was successful.
- `RetailCrm\Api\Event\FailureRequestEvent` will be dispatched if the request was not successful. It won't be dispatched if the request cannot be formed at all.
Event API documentation can be found here:
* [`FailureRequestEvent`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Event-FailureRequestEvent.html)
* [`SuccessRequestEvent`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Event-SuccessRequestEvent.html)
Here is an example of event handling with Symfony. We're using an empty Symfony 5.x project with annotations routing. `IndexController`
outputs `/api/credentials` response without any changes. We'll handle all exceptions inside this event listener which will allow us
to call the method without any `try...catch` blocks.
**config/services.yml**
```yaml
services:
# ClientFactory definition.
RetailCrm\Api\Interfaces\ClientFactoryInterface:
class: 'RetailCrm\Api\Factory\ClientFactory'
calls:
- setCacheDir: ['%kernel.cache_dir%']
- setEventDispatcher: ['@event_dispatcher']
# Event listener definition. This listener will suppress exceptions and log them.
App\EventListener\FailureRequestListener:
tags:
- { name: kernel.event_listener, event: RetailCrm\Api\Event\FailureRequestEvent, method: onRequestFailure }
```
**src/EventListener/FailureRequestListener.php**
```php
<?php
namespace App\EventListener;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Log\LoggerInterface;
use RetailCrm\Api\Event\FailureRequestEvent;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
class FailureRequestListener
{
/** @var \Psr\Log\LoggerInterface */
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function onRequestFailure(FailureRequestEvent $event): void
{
$exception = $event->getException();
if ($exception instanceof ApiExceptionInterface) {
$event->suppressThrow();
$this->logger->error(sprintf(
'CRM URL "%s", API error: %s',
$event->getApiUrl(),
(string) $exception
));
}
if ($exception instanceof ClientExceptionInterface) {
$event->suppressThrow();
$this->logger->error(sprintf(
'CRM URL "%s", client error: %s, trace: %s',
$event->getApiUrl(),
$exception->getMessage(),
$exception->getTraceAsString()
));
}
}
}
```
**src/Controller/IndexController.php**
```php
<?php
namespace App\Controller;
use RetailCrm\Api\Interfaces\ClientFactoryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class IndexController extends AbstractController
{
/**
* @Route("/", name="index")
*/
public function index(ClientFactoryInterface $clientFactory): Response
{
$client = $clientFactory->createClient('https://test3487687.retailcrm.pro', 'key');
$credentials = $client->api->credentials();
// Will print out empty model because https://test3487687.retailcrm.pro account does not exist.
return $this->json($credentials);
}
}
```

View File

@ -0,0 +1,115 @@
Here is a complete example of the Client usage with error handling. It fetches the filtered orders data and prints it out.
**Note:** it's not recommended using this example to process all API client errors in your application. It will clutter your code too much.
There are other options for exception processing. You can [match more generic exceptions in the hierarchy](../error_handling.md) or use
[events](../event_handing.md) to process exceptions.
```php
<?php
use Psr\Http\Client\ClientExceptionInterface as PsrClientExceptionInterface;
use Psr\Http\Client\NetworkExceptionInterface;
use Psr\Http\Client\RequestExceptionInterface;
use RetailCrm\Api\Exception\Api\AccessDeniedException;
use RetailCrm\Api\Exception\Api\AccountDoesNotExistException;
use RetailCrm\Api\Exception\Api\ApiErrorException;
use RetailCrm\Api\Exception\Api\InvalidCredentialsException;
use RetailCrm\Api\Exception\Api\MissingCredentialsException;
use RetailCrm\Api\Exception\Api\MissingParameterException;
use RetailCrm\Api\Exception\Api\ValidationException;
use RetailCrm\Api\Exception\Client\HandlerException;
use RetailCrm\Api\Exception\Client\HttpClientException;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate;
use RetailCrm\Api\Model\Filter\Orders\OrderFilter;
use RetailCrm\Api\Model\Request\Orders\OrdersRequest;
use Throwable;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$request = new OrdersRequest();
$request->filter = new OrderFilter();
$request->filter->ids = [7141];
try {
$response = $client->orders->list($request);
} catch (HandlerException $exception) {
echo 'Error while trying to prepare request: ' . $exception->getMessage();
exit(-1);
} catch (HttpClientException $exception) {
$prefix = 'Unknown error';
if ($exception->getPrevious() instanceof NetworkExceptionInterface) {
$prefix = 'Network error';
}
if ($exception->getPrevious() instanceof RequestExceptionInterface) {
$prefix = 'Invalid request';
}
if ($exception->getPrevious() instanceof PsrClientExceptionInterface) {
$prefix = 'HTTP client exception';
}
echo $prefix . ': ' . $exception->getMessage();
exit(-1);
} catch (
InvalidCredentialsException |
AccessDeniedException |
AccountDoesNotExistException |
MissingCredentialsException |
MissingParameterException $exception
) {
echo $exception->getMessage();
exit(-1);
} catch (ValidationException $exception) {
echo 'Errors in fields:' . PHP_EOL;
foreach ($exception->getErrorResponse()->errors as $field => $error) {
printf(" - %s: %s\n", $field, $error);
}
exit(-1);
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiException and ClientException implements __toString() method
exit(-1);
} catch (Throwable $throwable) {
echo 'Unknown runtime exception: ' . $throwable->getMessage() . PHP_EOL;
echo $throwable->getTraceAsString();
exit(-1);
}
foreach ($response->orders as $order) {
printf("Order ID: %d\n", $order->id);
printf("First name: %s\n", $order->firstName);
printf("Last name: %s\n", $order->lastName);
printf("Patronymic: %s\n", $order->patronymic);
printf("Phone #1: %s\n", $order->phone);
printf("Phone #2: %s\n", $order->additionalPhone);
printf("E-Mail: %s\n", $order->email);
if ($order->customer instanceof CustomerCorporate) {
echo "Customer type: corporate\n";
} else {
echo "Customer type: individual\n";
}
foreach ($order->items as $item) {
echo PHP_EOL;
printf("Product name: %s\n", $item->productName);
printf("Quantity: %d\n", $item->quantity);
printf("Initial price: %f\n", $item->initialPrice);
}
echo PHP_EOL;
printf("Discount: %f\n", $order->discountManualAmount);
printf("Total: %f\n", $order->totalSumm);
echo PHP_EOL;
}
```

View File

@ -0,0 +1,253 @@
<?php
/**
* PHP version 7.3
*
* @category PaginatedRequestIterator
*/
use RetailCrm\Api\Client;
use RetailCrm\Api\Exception\Client\HandlerException;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Interfaces\RequestInterface;
use RetailCrm\Api\Model\Response\AbstractPaginatedResponse;
/**
* Class PaginatedRequestIterator
*
* @category PaginatedRequestIterator
* @template T
* @implements Iterator<int|string, T>
*/
class PaginatedRequestIterator implements Iterator
{
/** @var \RetailCrm\Api\Client */
private $client;
/** @var array<int|string, mixed> */
private $items = [];
/** @var int */
private $position = 0;
/** @var int */
private $seek = 0;
/** @var int */
private $currentPage = 1;
/** @var bool */
private $fetchFailed = false;
/** @var \Throwable|null */
private $error;
/** @var bool */
private $finalized = false;
/** @var \RetailCrm\Api\Interfaces\RequestInterface */
private $request;
/** @var callable */
private $nextPageFunc;
/** @var callable */
private $sendResponseFunc;
/** @var callable */
private $extractBatchFunc;
/**
* PaginatedRequestIterator constructor.
*
* @param \RetailCrm\Api\Client $client
*/
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* This request will be used in every iteration.
*
* @param \RetailCrm\Api\Interfaces\RequestInterface $request
*
* @return self
*/
public function setRequest(RequestInterface $request): self
{
$this->request = $request;
return $this;
}
/**
* This function should modify provided request which will be used in the next iteration.
*
* These params will be supplied in this exact order:
* - The request instance.
* - The response instance.
* - Next page number.
*
* @param callable $nextPageFunc
*
* @return self
*/
public function setNextPageFunc(callable $nextPageFunc): self
{
$this->nextPageFunc = $nextPageFunc;
return $this;
}
/**
* This function should send the request and return the response.
*
* These params will be supplied in this exact order:
* - The Client instance.
* - The request instance.
*
* @param callable $sendResponseFunc
*
* @return self
*/
public function setSendResponseFunc(callable $sendResponseFunc): self
{
$this->sendResponseFunc = $sendResponseFunc;
return $this;
}
/**
* This function should extract the batch of objects from the response.
*
* These params will be supplied in this exact order:
* - The response instance.
*
* @param callable $extractBatchFunc
*
* @return PaginatedRequestIterator
*/
public function setExtractBatchFunc(callable $extractBatchFunc): PaginatedRequestIterator
{
$this->extractBatchFunc = $extractBatchFunc;
return $this;
}
/**
* @inheritDoc
* @return mixed
*/
public function current()
{
return $this->items[$this->getPosition()];
}
/**
* @inheritDoc
*/
public function next(): void
{
++$this->position;
}
/**
* @inheritDoc
*/
public function key(): int
{
return $this->position;
}
/**
* @inheritDoc
*/
public function valid(): bool
{
if (empty($this->items) || !array_key_exists($this->getPosition(), $this->items)) {
$this->fetchChunk();
}
return isset($this->items[$this->getPosition()]);
}
/**
* @inheritDoc
*/
public function rewind(): void
{
$this->items = [];
$this->position = 0;
$this->seek = 0;
$this->currentPage = 1;
$this->error = null;
}
/**
* @return bool
*/
public function isFetchFailed(): bool
{
return $this->fetchFailed;
}
/**
* @return \Throwable|null
*/
public function getError(): ?Throwable
{
return $this->error;
}
/**
* @return int
*/
private function getPosition(): int
{
return $this->position - $this->seek;
}
/**
* fetchChunk obtains chunk of data
*/
private function fetchChunk(): void
{
if ($this->finalized || $this->fetchFailed) {
return;
}
try {
if ($this->currentPage > 1) {
time_nanosleep(0, 100000000);
}
$response = call_user_func($this->sendResponseFunc, $this->client, $this->request);
if ($response instanceof AbstractPaginatedResponse) {
$batch = call_user_func($this->extractBatchFunc, $response);
if (empty($batch)) {
$this->items = [];
$this->fetchFailed = true;
$this->error = new HandlerException('Received an empty response');
} else {
$this->seek += count($this->items);
$this->items = array_values($batch);
if ($this->currentPage < $response->pagination->totalPageCount) {
call_user_func($this->nextPageFunc, $this->request, $response, ++$this->currentPage);
} else {
$this->finalized = true;
}
}
}
} catch (Throwable $exception) {
if ($exception instanceof ApiExceptionInterface && 503 === $exception->getStatusCode()) {
time_nanosleep(1, 0);
$this->fetchChunk();
return;
}
$this->fetchFailed = true;
$this->error = $exception;
}
}
}

View File

@ -0,0 +1,39 @@
<?php
use RetailCrm\Api\Client;
use RetailCrm\Api\Enum\PaginationLimit;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Filter\Customers\CustomerHistoryFilter;
use RetailCrm\Api\Model\Request\Customers\CustomersHistoryRequest;
use RetailCrm\Api\Model\Response\Customers\CustomersHistoryResponse;
require __DIR__ . '/../../../../vendor/autoload.php';
require __DIR__ . '/PaginatedRequestIterator.php';
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro/', 'apiKey');
$request = new CustomersHistoryRequest();
$request->filter = new CustomerHistoryFilter();
$request->filter->startDate = (new DateTime())->sub(new DateInterval('P7D')); // 7 days
$request->limit = PaginationLimit::LIMIT_100;
$paginator = (new PaginatedRequestIterator($client))
->setRequest($request)
->setNextPageFunc(static function (CustomersHistoryRequest $request, CustomersHistoryResponse $response, int $page) {
$request->filter->startDate = null;
$request->filter->sinceId = end($response->history)->id;
})
->setExtractBatchFunc(static function (CustomersHistoryResponse $response) {
return $response->history;
})
->setSendResponseFunc(static function (Client $client, CustomersHistoryRequest $request) {
return $client->customers->history($request);
});
/** @var \RetailCrm\Api\Model\Entity\Customers\Customer $customer */
foreach ($paginator as $historyEntry) {
print_r($historyEntry);
}
if ($paginator->isFetchFailed()) {
echo $paginator->getError();
}

View File

@ -0,0 +1,47 @@
<?php
use RetailCrm\Api\Client;
use RetailCrm\Api\Enum\PaginationLimit;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Entity\Customers\CustomerPhone;
use RetailCrm\Api\Model\Request\Customers\CustomersRequest;
use RetailCrm\Api\Model\Response\Customers\CustomersResponse;
require __DIR__ . '/../../../../vendor/autoload.php';
require __DIR__ . '/PaginatedRequestIterator.php';
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro/', 'apiKey');
$request = new CustomersRequest();
$request->limit = PaginationLimit::LIMIT_100;
$request->page = 1;
$paginator = (new PaginatedRequestIterator($client))
->setRequest($request)
->setNextPageFunc(static function (CustomersRequest $request, CustomersResponse $response, int $page) {
$request->page = $page;
})
->setExtractBatchFunc(static function (CustomersResponse $response) {
return $response->customers;
})
->setSendResponseFunc(static function (Client $client, CustomersRequest $request) {
return $client->customers->list($request);
});
/** @var \RetailCrm\Api\Model\Entity\Customers\Customer $customer */
foreach ($paginator as $customer) {
printf(
'%d: %s %s %s <%s> (%s) %s',
$customer->id,
$customer->firstName,
$customer->lastName,
$customer->patronymic,
implode(', ', array_map(static function (CustomerPhone $phone) {
return $phone->number;
}, $customer->phones)),
$customer->email, PHP_EOL
);
}
if ($paginator->isFetchFailed()) {
echo $paginator->getError();
}

View File

@ -0,0 +1,8 @@
Here are some complex examples of pagination handling. In this case, we're using a special component that handles the pagination for us.
As a result, we can just iterate over batches of the data using `foreach`.
More than that, this component automatically handles rate limit exceptions.
* [Fetching customers](example_customers_list.php)
* [Fetching customers history](example_customers_history.php)
* [Pagination component (`PaginatedRequestIterator`)](PaginatedRequestIterator.php)

View File

@ -0,0 +1,104 @@
That's how you can create a new order:
```php
<?php
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Enum\CountryCodeIso3166;
use RetailCrm\Api\Enum\Customers\CustomerType;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Model\Entity\Orders\Delivery\OrderDeliveryAddress;
use RetailCrm\Api\Model\Entity\Orders\Delivery\SerializedOrderDelivery;
use RetailCrm\Api\Model\Entity\Orders\Items\Offer;
use RetailCrm\Api\Model\Entity\Orders\Items\OrderProduct;
use RetailCrm\Api\Model\Entity\Orders\Items\PriceType;
use RetailCrm\Api\Model\Entity\Orders\Items\Unit;
use RetailCrm\Api\Model\Entity\Orders\Order;
use RetailCrm\Api\Model\Entity\Orders\Payment;
use RetailCrm\Api\Model\Entity\Orders\SerializedRelationCustomer;
use RetailCrm\Api\Model\Request\Orders\OrdersCreateRequest;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$request = new OrdersCreateRequest();
$order = new Order();
$payment = new Payment();
$delivery = new SerializedOrderDelivery();
$deliveryAddress = new OrderDeliveryAddress();
$offer = new Offer();
$item = new OrderProduct();
$payment->type = 'bank-card';
$payment->status = 'paid';
$payment->amount = 1000;
$payment->paidAt = new DateTime();
$deliveryAddress->index = '344001';
$deliveryAddress->countryIso = CountryCodeIso3166::RUSSIAN_FEDERATION;
$deliveryAddress->region = 'Region';
$deliveryAddress->city = 'City';
$deliveryAddress->street = 'Street';
$deliveryAddress->building = '10';
$delivery->address = $deliveryAddress;
$delivery->cost = 0;
$delivery->netCost = 0;
$offer->name = 'Offer №1445123';
$offer->displayName = 'Offer №1445123';
$offer->xmlId = 'tGunLo27jlPGmbA8BrHxY2';
$offer->article = '14451445-14451445';
$offer->unit = new Unit('796', 'Piece', 'pcs');
$item->offer = $offer;
$item->priceType = new PriceType('base');
$item->quantity = 1;
$item->purchasePrice = 60;
$order->delivery = $delivery;
$order->items = [$item];
$order->payments = [$payment];
$order->orderType = 'test';
$order->orderMethod = 'phone';
$order->countryIso = CountryCodeIso3166::RUSSIAN_FEDERATION;
$order->firstName = 'Test';
$order->lastName = 'User';
$order->patronymic = 'Patronymic';
$order->phone = '89003005069';
$order->email = 'testuser12345678901@example.com';
$order->managerId = 28;
$order->customer = SerializedRelationCustomer::withIdAndType(
4924,
CustomerType::CUSTOMER
);
$order->status = 'assembling';
$order->statusComment = 'Assembling order';
$order->weight = 1000;
$order->shipmentStore = 'main12';
$order->shipmentDate = (new DateTime())->add(new DateInterval('P7D'));
$order->shipped = false;
$order->customFields = [
"galka" => false,
"test_number" => 0,
"otpravit_dozakaz" => false,
];
$request->order = $order;
$request->site = 'moysklad';
try {
$response = $client->orders->create($request);
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface instance should implement __toString() method.
exit(-1);
}
printf(
'Created order id = %d with the following data: %s',
$response->id,
print_r($response->order, true)
);
```

View File

@ -0,0 +1,50 @@
That's how you can fetch the orders list:
```php
<?php
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Model\Entity\CustomersCorporate\CustomerCorporate;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
try {
$response = $client->orders->list();
} catch (ApiExceptionInterface | ClientExceptionInterface $exception) {
echo $exception; // Every ApiExceptionInterface instance should implement __toString() method.
exit(-1);
}
foreach ($response->orders as $order) {
printf("Order ID: %d\n", $order->id);
printf("First name: %s\n", $order->firstName);
printf("Last name: %s\n", $order->lastName);
printf("Patronymic: %s\n", $order->patronymic);
printf("Phone #1: %s\n", $order->phone);
printf("Phone #2: %s\n", $order->additionalPhone);
printf("E-Mail: %s\n", $order->email);
if ($order->customer instanceof CustomerCorporate) {
echo "Customer type: corporate\n";
} else {
echo "Customer type: individual\n";
}
foreach ($order->items as $item) {
echo PHP_EOL;
printf("Product name: %s\n", $item->productName);
printf("Quantity: %d\n", $item->quantity);
printf("Initial price: %f\n", $item->initialPrice);
}
echo PHP_EOL;
printf("Discount: %f\n", $order->discountManualAmount);
printf("Total: %f\n", $order->totalSumm);
echo PHP_EOL;
}
```

View File

@ -0,0 +1,7 @@
## Examples
* [How to create an order](create_order.md)
* [How to receive the list of orders](fetch_orders.md)
* [How to handle all Client's exceptions](complete_error_handling_example.md)
* [Fetching orders history](orders_history.md)
* [Complex pagination example](complex_pagination/index.md)

View File

@ -0,0 +1,34 @@
In this example we will fetch all history entries for 1 month from the API.
```php
use RetailCrm\Api\Enum\PaginationLimit;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
use RetailCrm\Api\Model\Filter\Orders\OrderHistoryFilterV4Type;
use RetailCrm\Api\Model\Request\Orders\OrdersHistoryRequest;
$history = [];
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey');
$request = new OrdersHistoryRequest();
$request->filter = new OrderHistoryFilterV4Type();
$request->limit = PaginationLimit::LIMIT_100;
$request->filter->startDate = (new DateTime())->sub(new DateInterval('P1M')); // History for 1 month by default.
do {
time_nanosleep(0, 100000000); // 10 requests per second
try {
$response = $client->orders->history($request);
} catch (ClientExceptionInterface | ApiExceptionInterface $exception) {
echo $exception;
break;
}
$history = array_merge($history, $response->history);
$request->filter->startDate = null;
$request->filter->sinceId = end($response->history)->id;
} while ($response->pagination->currentPage < $response->pagination->totalPageCount);
// Here you can process all history entries in the `$history` variable.
```

255
doc/usage/instantiation.md Normal file
View File

@ -0,0 +1,255 @@
## Instantiation
There are several ways to instantiate the Client. Let's take look at them: from the simplest one to the most complex.
* [Instantiation via `SimpleClientFactory`](#instantiation-via-simpleclientfactory)
* [Instantiation via `ClientFactory`](#instantiation-via-clientfactory)
+ [Simple example](#simple-example)
+ [Abstract example](#abstract-example)
+ [Real-world example (Symfony)](#real-world-example-symfony)
* [Instantiation via `ClientBuilder`](#instantiation-via-clientbuilder)
### Instantiation via `SimpleClientFactory`
It's the easiest way to instantiate an API client. You can use this method if you just want to use API client without
thinking much about integration with the app or framework.
Example:
```php
use RetailCrm\Api\Factory\SimpleClientFactory;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
```
That's it. You can use `$client` to make requests. However, even this simple method allows you to pass the `CacheItemPoolInterface` instance
(`symfony/cache` is being used by default) or path to the cache directory. The cache is being used by the client to store
annotations which omits the necessity to parse them each time you send a request.
Example with the `CacheItemPoolInterface` instance (we're using Redis as cache here):
```php
use Symfony\Component\Cache\Adapter\RedisAdapter;
use RetailCrm\Api\Enum\CacheDirectories;
use RetailCrm\Api\Factory\SimpleClientFactory;
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$client = SimpleClientFactory::createWithCache(
'https://test.retailcrm.pro',
'key',
new RedisAdapter($redis, CacheDirectories::DIR_NAME)
);
```
`CacheDirectories::DIR_NAME` is optional here. You can use any other namespace you want. By default, `CacheDirectories::DIR_NAME`
will be used as a namespace by default if the cache instance was created by the client automatically.
It's easier to use `SimpleClientFactory::createWithCacheDir` if you just want to specify your directory for cache instead of
default temporary directory:
```php
use RetailCrm\Api\Factory\SimpleClientFactory;
$client = SimpleClientFactory::createWithCacheDir('https://test.retailcrm.pro', 'key', __DIR__ . '/cache');
```
You can find more details about `SimpleClientFactory` in the documentation:
* [`SimpleClientFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-SimpleClientFactory.html)
### Instantiation via `ClientFactory`
The main difference between `SimpleClientFactory` and `ClientFactory` is the fact that the second factory is stateful.
It allows you to better integrate this client with your application. Consider the following: you may just set all global client
dependencies into the `ClientFactory` during the dependency container instantiation, and use the factory after that
to instantiate any number of clients you want.
#### Simple example
Here's an example of `ClientFactory` usage without any integration with the framework or the app:
```php
use RetailCrm\Api\Factory\ClientFactory;
use League\Event\EventDispatcher;
$eventDispatcher = new EventDispatcher();
$factory = new ClientFactory();
$factory
->setCacheDir('/tmp/retailcrm_cache')
->setEventDispatcher($eventDispatcher);
$client = $factory->createClient('https://test.retailcrm.pro', 'key');
```
Doesn't look that impressive, right? That's because `ClientFactory` is not supposed to be used just like that.
It won't give you any benefits in comparison to `SimpleClientFactory` with such usage.
Let's take a look at the more complex example. We'll use `league/container` here to demonstrate how you can integrate
`ClientFactory` with the DI you're using.
#### Abstract example
Let's assume that you have a specific service that should instantiate a Client for some internal purposes.
Here it is:
```php
namespace App\Services;
use RetailCrm\Api\Interfaces\ClientFactoryInterface;
use Throwable;
class ClientFactoryDependentService
{
/** @var ClientFactoryInterface */
private $clientFactory;
public function __construct(ClientFactoryInterface $clientFactory)
{
$this->clientFactory = $clientFactory;
}
public function isApiAccessible(string $apiUrl, string $apiKey): bool
{
try {
$client = $this->clientFactory->createClient($apiUrl, $apiKey);
$client->api->apiVersions();
return true;
} catch (Throwable $throwable) {
return false;
}
}
}
```
And you have a controller which returns a JSON response with the API availability flag. It requires the service above.
```php
namespace App\Controller;
use Some\Framework\Specific\Annotations\Route;
use Some\Framework\Specific\Controller;
use Some\Framework\Specific\Response;
class HealthCheckController extends Controller
{
/** @var ClientFactoryDependentService */
private $service;
public function __construct(ClientFactoryDependentService $service)
{
$this->service = $service;
}
/**
* @Route("/healthcheck")
*/
public function healthCheckAction(): Response
{
return $this->responseJson([
'is_api_accessible' => $this->service->isApiAccessible($_GET['apiUrl'], $_GET['apiKey'])
]);
}
}
```
During the container configuration you configure the container to instantiate proper `ClientFactory` once. After that
you can use the factory in any service you want. Here's an example with the `league/container`:
```php
use RetailCrm\Api\Factory\ClientFactory;
use RetailCrm\Api\Interfaces\ClientFactoryInterface;
use League\Container\Container;
use League\Event\EventDispatcher;
use Psr\Cache\CacheItemPoolInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use App\Services\ClientFactoryDependentService;
use App\Controller\HealthCheckController;
$container = new Container();
$container->add(CacheItemPoolInterface::class, new FilesystemAdapter('test_app'));
$container->add(EventDispatcherInterface::class, EventDispatcher::class);
$container->add(ClientFactoryInterface::class, ClientFactory::class)
->addMethodCalls([
'setCache' => [CacheItemPoolInterface::class],
'setEventDispatcher' => [EventDispatcherInterface::class],
]);
$container->add(ClientFactoryDependentService::class)->addArgument(ClientFactoryInterface::class);
$container->add(HealthCheckController::class)->addArgument(ClientFactoryDependentService::class);
```
That's it! Now your controller is using the underlying service to form a response. The service is using `ClientFactory`
which is being instantiated only once by the container. `ClientFactory` can be injected into other services as well, and it
will be the same instance.
#### Real-world example (Symfony)
You can use this exact service definition to instantiate a `ClientFactory` instance:
```yaml
RetailCrm\Api\Interfaces\ClientFactoryInterface:
class: 'RetailCrm\Api\Factory\ClientFactory'
calls:
- setCacheDir: ['%kernel.cache_dir%']
- setEventDispatcher: ['@event_dispatcher']
```
That's it. Now you can autowire `ClientFactoryInterface` in your services. It also allows you to mock the factory itself
in the tests using `services_test.yml`.
You can find more details about `ClientFactory` in the documentation:
* [`ClientFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-ClientFactory.html)
### Instantiation via `ClientBuilder`
`ClientBuilder` is the most powerful, and most complex method of client instantiation. It allows you to replace any client's
external dependencies with your own implementations. Here's an example of `ClientBuilder` usage:
```php
use Http\Client\Curl\Client as CurlClient;
use League\Event\EventDispatcher;
use Nyholm\Psr7\Factory\Psr17Factory;
use RetailCrm\Api\Builder\ClientBuilder;
use RetailCrm\Api\Builder\FormEncoderBuilder;
use RetailCrm\Api\Component\Transformer\RequestTransformer;
use RetailCrm\Api\Component\Transformer\ResponseTransformer;
use RetailCrm\Api\Factory\ApiExceptionFactory;
use RetailCrm\Api\Factory\RequestPipelineFactory;
use RetailCrm\Api\Factory\ResponsePipelineFactory;
use RetailCrm\Api\Handler\Request\HeaderAuthenticatorHandler;
$eventDispatcher = new EventDispatcher();
$psr17Factory = new Psr17Factory();
$httpClient = new CurlClient();
$builder = new ClientBuilder();
$formEncoder = (new FormEncoderBuilder())->setCacheDir('cache')->build();
$client = $builder->setApiUrl('https://test.retailcrm.pro')
->setAuthenticatorHandler(new HeaderAuthenticatorHandler('apiKey'))
->setFormEncoder($formEncoder)
->setHttpClient($httpClient)
->setRequestTransformer(new RequestTransformer(
RequestPipelineFactory::createDefaultPipeline(
$formEncoder,
$psr17Factory, // PSR-17 UriFactoryInterface
$psr17Factory, // PSR-17 RequestFactoryInterface
$psr17Factory // PSR-17 StreamFactoryInterface
)
))
->setResponseTransformer(new ResponseTransformer(
ResponsePipelineFactory::createDefaultPipeline(
$formEncoder->getSerializer(),
new ApiExceptionFactory(),
$eventDispatcher
)
))->build();
```
You can find more details about this in the documentation:
* [`ClientBuilder`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Builder-ClientBuilder.html)
* [`FormEncoderBuilder`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Builder-FormEncoderBuilder.html)
* [`RequestPipelineFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-RequestPipelineFactory.html)
* [`ResponsePipelineFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-ResponsePipelineFactory.html)

View File

@ -0,0 +1,144 @@
## Sending a request
You need three things to send a request:
1. [Resource group, DTOs, and method](#choosing-correct-resource-group-dtos-and-method)
2. [Send a request](#sending-a-request)
3. [Deal with exceptions](#dealing-with-exceptions)
### Choosing correct resource group, DTOs, and method
First, take a look at the API itself:
* [English](https://docs.retailcrm.pro/Developers/API/APIVersions/APIv5)
* [Русский](https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5)
Choose a method you want to use. Which one is yours depend on the task you want to perform.
Then take look at the API again. It consists of several blocks, each block is responsible for a specific set of features.
The Client itself is also separated to those blocks or resource groups (full list of them can be found [here](../structure.md#resource-groups)).
Each resource group corresponds to an equal block in the documentation. For example. `customersCorporate` resource group implements methods
in the _Corporate customers_ API block (рус. _Корпоративные клиенты_).
Just look at the method you want to use and pick a proper resource group. That's it. Let's move on to the DTOs.
Choosing proper DTOs is also easy. Each client method defines expected parameter types. If the method doesn't have any
parameters, then the API method is also doesn't require any parameters and can be just called. The most noteworthy would be
`/api/api-versions` and `/api/credentials` methods. They can be called from the Client instance like that:
```php
use RetailCrm\Api\Factory\SimpleClientFactory;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
$client->api->apiVersions();
$client->api->credentials();
```
The majority of the methods will require some parameters. Usually it's a request DTO, some path parameters (like customer id)
or both. For example, `/api/v5/customers/create` method requires request DTO, `/api/v5/customers/{externalId}` requires
customer ID and `/api/v5/customers/{externalId}/edit` requires both.
The corresponding methods on the client instance are:
| API method | Client instance method |
| ------------------------------------- | ---------------------------- |
| `/api/v5/customers/create` | `$client->customers->create` |
| `/api/v5/customers/{externalId}` | `$client->customers->get` |
| `/api/v5/customers/{externalId}/edit` | `$client->customers->edit` |
Look at the method definitions (they have also shown by the IDE):
```php
public function create(CustomersCreateRequest $request): IdResponse;
public function get($identifier, ?BySiteRequest $request = null): CustomersGetResponse;
public function edit($identifier, CustomersEditRequest $request): CustomersEditResponse;
```
Just use types from the type hints and everything will work. The DTO's fields also have type declarations but in the form
of the `@var` annotation tags. That's how you can choose the correct type for the DTO fields.
### Sending a request
Using information from the previous article you can easily construct a request.
This request will create a new customer.
```php
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Entity\Customers\Customer;
use RetailCrm\Api\Model\Request\Customers\CustomersCreateRequest;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
$request = new CustomersCreateRequest();
$request->customer = new Customer();
$request->customer->email = 'test@example.com';
$request->site = 'site';
$response = $client->customers->create($request);
```
This one will fetch specific customer from the API by the ID and site.
```php
use RetailCrm\Api\Enum\ByIdentifier;use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Request\BySiteRequest;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
$response = $client->customers->get(1, new BySiteRequest(ByIdentifier::ID, 'site'));
echo $response->customer->email;
```
And this one will edit specific customer:
```php
use RetailCrm\Api\Enum\ByIdentifier;
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Model\Entity\Customers\Customer;
use RetailCrm\Api\Model\Request\Customers\CustomersEditRequest;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
$request = new CustomersEditRequest();
$request->customer = new Customer();
$request->customer->email = 'test@example.com';
$request->site = 'site';
$request->by = ByIdentifier::ID;
$response = $client->customers->edit(1, $request);
echo "Edited customer ID: " . $response->id;
```
### Dealing with exceptions
What will happen if an API error is returned for the one of requests above? Or the network is not working at all?
An exception will happen.
There are two main exception types:
* `RetailCrm\Api\Interfaces\ApiExceptionInterface` is thrown if API returned an error.
* `RetailCrm\Api\Interfaces\ClientExceptionInterface` is thrown if the request cannot be sent due to an internal error.
You can use those like this:
```php
use RetailCrm\Api\Factory\SimpleClientFactory;
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
use RetailCrm\Api\Interfaces\ClientExceptionInterface;
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
try {
$response = $client->customers->list();
} catch (ApiExceptionInterface $exception) {
echo $exception;
} catch (ClientExceptionInterface $exception) {
echo 'Client error: ' . $exception->getMessage();
}
if (isset($response)) {
echo "Customers: " . print_r($response->customers, true);
}
```
More information about exceptions can be found on the [error handling](error_handling.md) page.

15
doc/usage/usage.md Normal file
View File

@ -0,0 +1,15 @@
## Usage
* [Instantiation](instantiation.md)
* [Sending a request](sending_a_request.md)
* [Choosing correct resource group, DTOs, and method](sending_a_request.md#choosing-correct-resource-group-dtos-and-method)
* [Sending a request](sending_a_request.md#sending-a-request)
* [Dealing with exceptions](sending_a_request.md#dealing-with-exceptions)
* [Error handling](error_handling.md)
* [Examples](examples)
+ [How to create an order](examples/create_order.md)
+ [How to receive the list of orders](examples/fetch_orders.md)
+ [How to handle all Client's exceptions](examples/complete_error_handling_example.md)
+ [Fetching orders history](examples/orders_history.md)
+ [Complex pagination example](examples/complex_pagination/index.md)
* [Event handling](event_handing.md)

View File

@ -1,70 +0,0 @@
<?php
/**
* PHP version 5.4
*
* API client class
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm;
use RetailCrm\Client\ApiVersion3;
use RetailCrm\Client\ApiVersion4;
use RetailCrm\Client\ApiVersion5;
/**
* PHP version 5.4
*
* API client class
*
* @category RetailCrm
* @package RetailCrm
*/
class ApiClient
{
public $request;
public $version;
const V3 = 'v3';
const V4 = 'v4';
const V5 = 'v5';
/**
* Init version based client
*
* @param string $url api url
* @param string $apiKey api key
* @param string $version api version
* @param string $site site code
* @param bool $debug debug mode
*/
public function __construct($url, $apiKey, $version = self::V5, $site = null, $debug = false)
{
$this->version = $version;
switch ($version) {
case self::V4:
$this->request = new ApiVersion4($url, $apiKey, $version, $site, $debug);
break;
case self::V3:
$this->request = new ApiVersion3($url, $apiKey, $version, $site, $debug);
break;
default:
$this->request = new ApiVersion5($url, $apiKey, $version, $site, $debug);
break;
}
}
/**
* Get API version
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
}

View File

@ -1,174 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Abstract API client
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Client;
use RetailCrm\Http\Client;
use RetailCrm\Http\RequestOptions;
/**
* PHP version 5.4
*
* Abstract API client class
*
* @category RetailCrm
* @package RetailCrm
*/
abstract class AbstractLoader
{
/** @var string|null */
protected $siteCode;
/** @var \RetailCrm\Http\Client */
protected $client;
/** @var string */
protected $crmUrl;
/**
* Init version based client
*
* @param string $url api url
* @param string $apiKey api key
* @param string $version api version
* @param string $site site code
* @param bool $debug debug mode
*/
public function __construct($url, $apiKey, $version, $site = null, $debug = false)
{
if ('/' !== $url[strlen($url) - 1]) {
$url .= '/';
}
$this->crmUrl = $url;
if (empty($version) || !in_array($version, ['v3', 'v4', 'v5'])) {
throw new \InvalidArgumentException(
'Version must be not empty and must be equal one of v3|v4|v5'
);
}
$url = $url . 'api/' . $version;
$this->client = new Client($url, ['apiKey' => $apiKey], $debug);
$this->siteCode = $site;
}
/**
* Set request options
*
* @param RequestOptions $options
*/
public function setOptions(RequestOptions $options)
{
$this->client->setOptions($options);
}
/**
* Set site
*
* @param string $site site code
*
* @return void
*/
public function setSite($site)
{
$this->siteCode = $site;
}
/**
* Check ID parameter
*
* @param string $by identify by
*
* @throws \InvalidArgumentException
*
* @return bool
*/
protected function checkIdParameter($by)
{
$allowedForBy = [
'externalId',
'id'
];
if (!in_array($by, $allowedForBy, false)) {
throw new \InvalidArgumentException(
sprintf(
'Value "%s" for "by" param is not valid. Allowed values are %s.',
$by,
implode(', ', $allowedForBy)
)
);
}
return true;
}
/**
* Fill params by site value
*
* @param string $site site code
* @param array $params input parameters
*
* @return array
*/
protected function fillSite($site, array $params)
{
if ($site) {
$params['site'] = $site;
} elseif ($this->siteCode) {
$params['site'] = $this->siteCode;
}
return $params;
}
/**
* Return current site
*
* @return string
*/
public function getSite()
{
return $this->siteCode;
}
/**
* Getting the list of available api versions
*
* @return \RetailCrm\Response\ApiResponse
*/
public function availableVersions()
{
return $this->client->makeRequest(
$this->crmUrl . 'api/api-versions',
"GET",
[],
true
);
}
/**
* Getting the list of available api methods and stores for current key
*
* @return \RetailCrm\Response\ApiResponse
*/
public function credentials()
{
return $this->client->makeRequest(
$this->crmUrl . 'api/credentials',
"GET",
[],
true
);
}
}

View File

@ -1,47 +0,0 @@
<?php
/**
* PHP version 5.4
*
* API client v3
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Client;
use RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* API client v3 class
*
* @category RetailCrm
* @package RetailCrm
*/
class ApiVersion3 extends AbstractLoader
{
/**
* Init version based client
*
* @param string $url api url
* @param string $apiKey api key
* @param string $version api version
* @param string $site site code
* @param bool $debug debug mode
*/
public function __construct($url, $apiKey, $version, $site, $debug = false)
{
parent::__construct($url, $apiKey, $version, $site, $debug);
}
use V3\Customers;
use V3\Orders;
use V3\Packs;
use V3\References;
use V3\Statistic;
use V3\Stores;
use V3\Telephony;
}

View File

@ -1,51 +0,0 @@
<?php
/**
* PHP version 5.4
*
* API client v4
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Client;
use RetailCrm\Methods\V4;
/**
* PHP version 5.4
*
* API client v4 class
*
* @category RetailCrm
* @package RetailCrm
*/
class ApiVersion4 extends AbstractLoader
{
/**
* Init version based client
*
* @param string $url api url
* @param string $apiKey api key
* @param string $version api version
* @param string $site site code
* @param bool $debug debug mode
*/
public function __construct($url, $apiKey, $version, $site, $debug = false)
{
parent::__construct($url, $apiKey, $version, $site, $debug);
}
use V4\Customers;
use V4\Delivery;
use V4\Marketplace;
use V4\Orders;
use V4\Packs;
use V4\References;
use V4\Settings;
use V4\Statistic;
use V4\Stores;
use V4\Telephony;
use V4\Users;
}

View File

@ -1,58 +0,0 @@
<?php
/**
* PHP version 5.4
*
* API client v5
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Client;
use RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* API client v5 class
*
* @category RetailCrm
* @package RetailCrm
*/
class ApiVersion5 extends AbstractLoader
{
/**
* Init version based client
*
* @param string $url api url
* @param string $apiKey api key
* @param string $version api version
* @param string $site site code
* @param bool $debug debug mode
*/
public function __construct($url, $apiKey, $version, $site, $debug = false)
{
parent::__construct($url, $apiKey, $version, $site, $debug);
}
use V5\Customers;
use V5\CustomersCorporate;
use V5\Costs;
use V5\CustomFields;
use V5\Delivery;
use V5\Files;
use V5\Module;
use V5\Orders;
use V5\Packs;
use V5\References;
use V5\Segments;
use V5\Settings;
use V5\Statistic;
use V5\Stores;
use V5\Tasks;
use V5\Telephony;
use V5\Users;
use V5\IntegrationPayments;
}

View File

@ -1,24 +0,0 @@
<?php
/**
* PHP version 5.4
*
* CurlException
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Exception;
/**
* PHP version 5.4
*
* Class CurlException
*
* @category RetailCrm
* @package RetailCrm
*/
class CurlException extends \RuntimeException
{
}

View File

@ -1,24 +0,0 @@
<?php
/**
* PHP version 5.4
*
* InvalidJsonException
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Exception;
/**
* PHP version 5.4
*
* Class InvalidJsonException
*
* @category RetailCrm
* @package RetailCrm
*/
class InvalidJsonException extends \DomainException
{
}

View File

@ -1,24 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Class LimitException
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Exception;
/**
* PHP version 5.4
*
* Class LimitException
*
* @category RetailCrm
* @package RetailCrm
*/
class LimitException extends \DomainException
{
}

View File

@ -1,24 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Class LimitException
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Exception;
/**
* PHP version 5.4
*
* Class NotFoundException
*
* @category RetailCrm
* @package RetailCrm
*/
class NotFoundException extends \DomainException
{
}

View File

@ -1,203 +0,0 @@
<?php
/**
* PHP version 5.4
*
* HTTP client
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Http;
use RetailCrm\Exception\CurlException;
use RetailCrm\Exception\InvalidJsonException;
use RetailCrm\Exception\LimitException;
use RetailCrm\Exception\NotFoundException;
use RetailCrm\Response\ApiResponse;
/**
* PHP version 5.4
*
* HTTP client
*
* @category RetailCrm
* @package RetailCrm
*/
class Client
{
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
protected $url;
protected $defaultParameters;
protected $options;
/**
* Client constructor.
*
* @param string $url api url
* @param array $defaultParameters array of parameters
* @param bool $debug debug mode
*
* @throws \InvalidArgumentException
*/
public function __construct($url, array $defaultParameters = [], $debug = false)
{
if (false === stripos($url, 'https://') && false === $debug) {
throw new \InvalidArgumentException(
'API schema requires HTTPS protocol'
);
}
$this->url = $url;
$this->defaultParameters = $defaultParameters;
$this->options = new RequestOptions();
}
/**
* Make HTTP request
*
* @param string $path request url
* @param string $method (default: 'GET')
* @param array $parameters (default: array())
* @param bool $fullPath (default: false)
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*
* @throws \InvalidArgumentException
* @throws CurlException
* @throws InvalidJsonException
*
* @return ApiResponse
*/
public function makeRawRequest(
$path,
$method,
array $parameters = [],
$fullPath = false
) {
$allowedMethods = [self::METHOD_GET, self::METHOD_POST];
if (!in_array($method, $allowedMethods, false)) {
throw new \InvalidArgumentException(
sprintf(
'Method "%s" is not valid. Allowed methods are %s',
$method,
implode(', ', $allowedMethods)
)
);
}
$parameters = array_merge($this->defaultParameters, $parameters);
$url = $fullPath ? $path : $this->url . $path;
if (self::METHOD_GET === $method && count($parameters)) {
$url .= '?' . http_build_query($parameters, '', '&');
}
if (self::METHOD_POST === $method && '/files/upload' == $path) {
$url .= '?apiKey=' . $parameters['apiKey'];
}
$curlHandler = curl_init();
curl_setopt($curlHandler, CURLOPT_URL, $url);
curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curlHandler, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curlHandler, CURLOPT_FAILONERROR, false);
curl_setopt($curlHandler, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curlHandler, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curlHandler, CURLOPT_TIMEOUT, $this->options->getClientTimeout());
curl_setopt($curlHandler, CURLOPT_CONNECTTIMEOUT, $this->options->getClientTimeout());
if ($this->options->getHeaders()) {
curl_setopt($curlHandler, CURLOPT_HTTPHEADER, $this->options->getHttpHeaders());
}
if (self::METHOD_POST === $method) {
curl_setopt($curlHandler, CURLOPT_POST, true);
if ('/files/upload' == $path) {
curl_setopt($curlHandler, CURLOPT_POSTFIELDS, file_get_contents($parameters['file']));
} else {
curl_setopt($curlHandler, CURLOPT_POSTFIELDS, $parameters);
}
}
list($statusCode, $responseBody) = $this->checkResponse($curlHandler, $method);
return new ApiResponse($statusCode, $responseBody);
}
/**
* Make HTTP request and deserialize JSON body (throws exception otherwise)
*
* @param string $path request url
* @param string $method (default: 'GET')
* @param array $parameters (default: array())
* @param bool $fullPath (default: false)
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*
* @throws \InvalidArgumentException
* @throws CurlException
* @throws InvalidJsonException
*
* @return ApiResponse
*/
public function makeRequest(
$path,
$method,
array $parameters = [],
$fullPath = false
) {
return $this->makeRawRequest($path, $method, $parameters, $fullPath)->asJsonResponse();
}
/**
* Set request options
*
* @param RequestOptions $options
*/
public function setOptions(RequestOptions $options)
{
$this->options = $options;
}
/**
* @param $curlHandler
* @param $method
*
* @return array
*/
private function checkResponse($curlHandler, $method)
{
$responseBody = curl_exec($curlHandler);
$statusCode = curl_getinfo($curlHandler, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($curlHandler, CURLINFO_CONTENT_TYPE);
if (503 === $statusCode) {
throw new LimitException("Service temporary unavailable");
}
if (
(404 === $statusCode && false !== stripos($responseBody, 'Account does not exist'))
|| ('GET' !== $method && 405 === $statusCode && false !== stripos($contentType, 'text/html'))
) {
throw new NotFoundException("Account does not exist");
}
$errno = curl_errno($curlHandler);
$error = curl_error($curlHandler);
curl_close($curlHandler);
if ($errno) {
throw new CurlException($error, $errno);
}
return [$statusCode, $responseBody];
}
}

View File

@ -1,95 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Request options
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Http;
/**
* PHP version 5.4
*
* Request options class
*
* @category RetailCrm
* @package RetailCrm
*/
class RequestOptions
{
/** @var array */
private $headers;
/** @var int */
private $clientTimeout;
/**
* Init request options.
*
* @param array $headers
* @param int $clientTimeout
*/
public function __construct($headers = array(), $clientTimeout = 30)
{
$this->headers = $headers;
$this->clientTimeout = $clientTimeout;
}
/**
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* @return array
*/
public function getHttpHeaders()
{
$headers = [];
foreach ($this->headers as $header => $value) {
$headers[] = sprintf("%s: %s", $header, $value);
}
return $headers;
}
/**
* @param array $headers
*
* @return self
*/
public function setHeaders($headers)
{
$this->headers = $headers;
return $this;
}
/**
* @return int
*/
public function getClientTimeout()
{
return $this->clientTimeout;
}
/**
* @param int $clientTimeout
*
* @return self
*/
public function setClientTimeout($clientTimeout)
{
$this->clientTimeout = $clientTimeout;
return $this;
}
}

View File

@ -1,206 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Customers
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* Customers class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Customers
{
/**
* Returns filtered customers list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers',
"GET",
$parameters
);
}
/**
* Create a customer
*
* @param array $customer customer data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCreate(array $customer, $site = null)
{
if (! count($customer)) {
throw new \InvalidArgumentException(
'Parameter `customer` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/create',
"POST",
$this->fillSite($site, ['customer' => json_encode($customer)])
);
}
/**
* Save customer IDs' (id and externalId) association in the CRM
*
* @param array $ids ids mapping
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersFixExternalIds(array $ids)
{
if (! count($ids)) {
throw new \InvalidArgumentException(
'Method parameter must contains at least one IDs pair'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/fix-external-ids',
"POST",
['customers' => json_encode($ids)]
);
}
/**
* Upload array of the customers
*
* @param array $customers array of customers
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersUpload(array $customers, $site = null)
{
if (! count($customers)) {
throw new \InvalidArgumentException(
'Parameter `customers` must contains array of the customers'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/upload',
"POST",
$this->fillSite($site, ['customers' => json_encode($customers)])
);
}
/**
* Get customer by id or externalId
*
* @param string $id customer identificator
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersGet($id, $by = 'externalId', $site = null)
{
$this->checkIdParameter($by);
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers/$id",
"GET",
$this->fillSite($site, ['by' => $by])
);
}
/**
* Edit a customer
*
* @param array $customer customer data
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersEdit(array $customer, $by = 'externalId', $site = null)
{
if (!count($customer)) {
throw new \InvalidArgumentException(
'Parameter `customer` must contains a data'
);
}
$this->checkIdParameter($by);
if (!array_key_exists($by, $customer)) {
throw new \InvalidArgumentException(
sprintf('Customer array must contain the "%s" parameter.', $by)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/customers/%s/edit', $customer[$by]),
"POST",
$this->fillSite(
$site,
['customer' => json_encode($customer), 'by' => $by]
)
);
}
}

View File

@ -1,269 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Orders
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* Orders class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Orders
{
/**
* Returns filtered orders list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders',
"GET",
$parameters
);
}
/**
* Create an order
*
* @param array $order order data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersCreate(array $order, $site = null)
{
if (!count($order)) {
throw new \InvalidArgumentException(
'Parameter `order` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/create',
"POST",
$this->fillSite($site, ['order' => json_encode($order)])
);
}
/**
* Save order IDs' (id and externalId) association into CRM
*
* @param array $ids order identificators
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersFixExternalIds(array $ids)
{
if (! count($ids)) {
throw new \InvalidArgumentException(
'Method parameter must contains at least one IDs pair'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/fix-external-ids',
"POST",
['orders' => json_encode($ids)
]
);
}
/**
* Returns statuses of the orders
*
* @param array $ids (default: array())
* @param array $externalIds (default: array())
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersStatuses(array $ids = [], array $externalIds = [])
{
$parameters = [];
if (count($ids)) {
$parameters['ids'] = $ids;
}
if (count($externalIds)) {
$parameters['externalIds'] = $externalIds;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/statuses',
"GET",
$parameters
);
}
/**
* Upload array of the orders
*
* @param array $orders array of orders
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersUpload(array $orders, $site = null)
{
if (!count($orders)) {
throw new \InvalidArgumentException(
'Parameter `orders` must contains array of the orders'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/upload',
"POST",
$this->fillSite($site, ['orders' => json_encode($orders)])
);
}
/**
* Get order by id or externalId
*
* @param string $id order identificator
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersGet($id, $by = 'externalId', $site = null)
{
$this->checkIdParameter($by);
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/orders/$id",
"GET",
$this->fillSite($site, ['by' => $by])
);
}
/**
* Edit an order
*
* @param array $order order data
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersEdit(array $order, $by = 'externalId', $site = null)
{
if (!count($order)) {
throw new \InvalidArgumentException(
'Parameter `order` must contains a data'
);
}
$this->checkIdParameter($by);
if (!array_key_exists($by, $order)) {
throw new \InvalidArgumentException(
sprintf('Order array must contain the "%s" parameter.', $by)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/orders/%s/edit', $order[$by]),
"POST",
$this->fillSite(
$site,
['order' => json_encode($order), 'by' => $by]
)
);
}
/**
* Get orders history
* @param array $filter
* @param null $page
* @param null $limit
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersHistory(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/history',
"GET",
$parameters
);
}
}

View File

@ -1,197 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Packs
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* Packs class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Packs
{
/**
* Get orders assembly list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPacksList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/packs',
"GET",
$parameters
);
}
/**
* Create orders assembly
*
* @param array $pack pack data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPacksCreate(array $pack, $site = null)
{
if (!count($pack)) {
throw new \InvalidArgumentException(
'Parameter `pack` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/packs/create',
"POST",
$this->fillSite($site, ['pack' => json_encode($pack)])
);
}
/**
* Get orders assembly history
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPacksHistory(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/packs/history',
"GET",
$parameters
);
}
/**
* Get orders assembly by id
*
* @param string $id pack identificator
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPacksGet($id)
{
if (empty($id)) {
throw new \InvalidArgumentException('Parameter `id` must be set');
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/orders/packs/$id",
"GET"
);
}
/**
* Delete orders assembly by id
*
* @param string $id pack identificator
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPacksDelete($id)
{
if (empty($id)) {
throw new \InvalidArgumentException('Parameter `id` must be set');
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/orders/packs/%s/delete', $id),
"POST"
);
}
/**
* Edit orders assembly
*
* @param array $pack pack data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPacksEdit(array $pack, $site = null)
{
if (!count($pack) || empty($pack['id'])) {
throw new \InvalidArgumentException(
'Parameter `pack` must contains a data & pack `id` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/orders/packs/%s/edit', $pack['id']),
"POST",
$this->fillSite($site, ['pack' => json_encode($pack)])
);
}
}

View File

@ -1,515 +0,0 @@
<?php
/**
* PHP version 5.4
*
* References
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* References class
*
* @category RetailCrm
* @package RetailCrm
*/
trait References
{
/**
* Returns available county list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function countriesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/countries',
"GET"
);
}
/**
* Returns deliveryServices list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryServicesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/delivery-services',
"GET"
);
}
/**
* Edit deliveryService
*
* @param array $data delivery service data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryServicesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/delivery-services/%s/edit', $data['code']),
"POST",
['deliveryService' => json_encode($data)]
);
}
/**
* Returns deliveryTypes list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryTypesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/delivery-types',
"GET"
);
}
/**
* Edit deliveryType
*
* @param array $data delivery type data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryTypesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/delivery-types/%s/edit', $data['code']),
"POST",
['deliveryType' => json_encode($data)]
);
}
/**
* Returns orderMethods list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function orderMethodsList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/order-methods',
"GET"
);
}
/**
* Edit orderMethod
*
* @param array $data order method data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function orderMethodsEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/order-methods/%s/edit', $data['code']),
"POST",
['orderMethod' => json_encode($data)]
);
}
/**
* Returns orderTypes list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function orderTypesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/order-types',
"GET"
);
}
/**
* Edit orderType
*
* @param array $data order type data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function orderTypesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/order-types/%s/edit', $data['code']),
"POST",
['orderType' => json_encode($data)]
);
}
/**
* Returns paymentStatuses list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentStatusesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/payment-statuses',
"GET"
);
}
/**
* Edit paymentStatus
*
* @param array $data payment status data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentStatusesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/payment-statuses/%s/edit', $data['code']),
"POST",
['paymentStatus' => json_encode($data)]
);
}
/**
* Returns paymentTypes list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentTypesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/payment-types',
"GET"
);
}
/**
* Edit paymentType
*
* @param array $data payment type data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentTypesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/payment-types/%s/edit', $data['code']),
"POST",
['paymentType' => json_encode($data)]
);
}
/**
* Returns productStatuses list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function productStatusesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/product-statuses',
"GET"
);
}
/**
* Edit productStatus
*
* @param array $data product status data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function productStatusesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/product-statuses/%s/edit', $data['code']),
"POST",
['productStatus' => json_encode($data)]
);
}
/**
* Returns sites list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function sitesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/sites',
"GET"
);
}
/**
* Edit site
*
* @param array $data site data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function sitesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/sites/%s/edit', $data['code']),
"POST",
['site' => json_encode($data)]
);
}
/**
* Returns statusGroups list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function statusGroupsList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/status-groups',
"GET"
);
}
/**
* Returns statuses list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function statusesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/statuses',
"GET"
);
}
/**
* Edit order status
*
* @param array $data status data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function statusesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/statuses/%s/edit', $data['code']),
"POST",
['status' => json_encode($data)]
);
}
/**
* Returns stores list
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storesList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/stores',
"GET"
);
}
/**
* Edit store
*
* @param array $data site data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
if (!array_key_exists('name', $data)) {
throw new \InvalidArgumentException(
'Data must contain "name" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/stores/%s/edit', $data['code']),
"POST",
['store' => json_encode($data)]
);
}
}

View File

@ -1,41 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Statistic
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* Statistic class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Statistic
{
/**
* Update CRM basic statistic
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function statisticUpdate()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/statistic/update',
"GET"
);
}
}

View File

@ -1,86 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Stores
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* Stores class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Stores
{
/**
* Get purchace prices & stock balance
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeInventories(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/store/inventories',
"GET",
$parameters
);
}
/**
* Upload store inventories
*
* @param array $offers offers data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeInventoriesUpload(array $offers, $site = null)
{
if (!count($offers)) {
throw new \InvalidArgumentException(
'Parameter `offers` must contains array of the offers'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/store/inventories/upload',
"POST",
$this->fillSite($site, ['offers' => json_encode($offers)])
);
}
}

View File

@ -1,168 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Telephony
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V3;
/**
* PHP version 5.4
*
* Telephony class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Telephony
{
/**
* Get telephony settings
*
* @param string $code
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function telephonySettingsGet($code)
{
if (empty($code)) {
throw new \InvalidArgumentException('Parameter `code` must be set');
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/telephony/setting/$code",
"GET"
);
}
/**
* Call event
*
* @param string $phone phone number
* @param string $type call type
* @param array $codes
* @param array $userIds
* @param string $callExternalId
* @param string $hangupStatus
* @param string $externalPhone
* @param array $webAnalyticsData
* @param string $site (default: null)
*
* @return \RetailCrm\Response\ApiResponse
* @internal param string $code additional phone code
* @internal param string $status call status
*/
public function telephonyCallEvent(
$phone,
$type,
$codes,
$userIds = [],
$hangupStatus = null,
$externalPhone = null,
$callExternalId = null,
$webAnalyticsData = [],
$site = null
) {
if (!isset($phone)) {
throw new \InvalidArgumentException('Phone number must be set');
}
if (!isset($type)) {
throw new \InvalidArgumentException('Type must be set (in|out|hangup)');
}
if (empty($codes)) {
throw new \InvalidArgumentException('Codes array must be set');
}
$parameters['phone'] = $phone;
$parameters['type'] = $type;
$parameters['codes'] = $codes;
if (!empty($userIds)) {
$parameters['userIds'] = $userIds;
}
$parameters['hangupStatus'] = $hangupStatus;
$parameters['callExternalId'] = $callExternalId;
$parameters['externalPhone'] = $externalPhone;
$parameters['webAnalyticsData'] = $webAnalyticsData;
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/telephony/call/event',
"POST",
['event' => json_encode($this->fillSite($site, $parameters))]
);
}
/**
* Upload calls
*
* @param array $calls calls data
*
* @param bool $autoFillSite fill site code from API client in provided calls
*
* @return \RetailCrm\Response\ApiResponse
*/
public function telephonyCallsUpload(array $calls, $autoFillSite = false)
{
if (!count($calls)) {
throw new \InvalidArgumentException(
'Parameter `calls` must contains array of the calls'
);
}
if ($autoFillSite) {
foreach ($calls as $key => $call) {
$calls[$key] = $this->fillSite(null, $call);
}
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/telephony/calls/upload',
"POST",
['calls' => json_encode($calls)]
);
}
/**
* Get call manager
*
* @param string $phone phone number
* @param bool $details detailed information
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function telephonyCallManager($phone, $details)
{
if (!isset($phone)) {
throw new \InvalidArgumentException('Phone number must be set');
}
$parameters['phone'] = $phone;
$parameters['details'] = isset($details) ? $details : 0;
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/telephony/manager',
"GET",
$parameters
);
}
}

View File

@ -1,57 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Customers
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\Customers as Previous;
/**
* PHP version 5.4
*
* Customers class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Customers
{
use Previous;
/**
* Get customers history
* @param array $filter
* @param null $page
* @param null $limit
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersHistory(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/history',
"GET",
$parameters
);
}
}

View File

@ -1,79 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Delivery
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
/**
* PHP version 5.4
*
* Delivery class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Delivery
{
/**
* Get delivery settings
*
* @param string $code
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliverySettingsGet($code)
{
if (empty($code)) {
throw new \InvalidArgumentException('Parameter `code` must be set');
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/delivery/generic/setting/$code",
"GET"
);
}
/**
* Delivery tracking update
*
* @param string $code
* @param array $statusUpdate
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryTracking($code, array $statusUpdate)
{
if (empty($code)) {
throw new \InvalidArgumentException('Parameter `code` must be set');
}
if (!count($statusUpdate)) {
throw new \InvalidArgumentException(
'Parameter `statusUpdate` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/delivery/generic/%s/tracking', $code),
"POST",
['statusUpdate' => json_encode($statusUpdate)]
);
}
}

View File

@ -1,51 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Marketplace
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
/**
* PHP version 5.4
*
* Marketplace class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Marketplace
{
/**
* Edit marketplace configuration
*
* @param array $configuration
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function marketplaceSettingsEdit(array $configuration)
{
if (!count($configuration) || empty($configuration['code'])) {
throw new \InvalidArgumentException(
'Parameter `configuration` must contains a data & configuration `code` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/marketplace/external/setting/%s/edit', $configuration['code']),
"POST",
['configuration' => json_encode($configuration)]
);
}
}

View File

@ -1,279 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Orders
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\Orders as Previous;
/**
* PHP version 5.4
*
* Orders class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Orders
{
use Previous;
/**
* Returns filtered orders list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders',
"GET",
$parameters
);
}
/**
* Create an order
*
* @param array $order order data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersCreate(array $order, $site = null)
{
if (!count($order)) {
throw new \InvalidArgumentException(
'Parameter `order` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/create',
"POST",
$this->fillSite($site, ['order' => json_encode($order)])
);
}
/**
* Save order IDs' (id and externalId) association into CRM
*
* @param array $ids order identificators
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersFixExternalIds(array $ids)
{
if (! count($ids)) {
throw new \InvalidArgumentException(
'Method parameter must contains at least one IDs pair'
);
}
/** @noinspection PhpUndefinedMethodInspection */
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/fix-external-ids',
"POST",
['orders' => json_encode($ids)
]
);
}
/**
* Returns statuses of the orders
*
* @param array $ids (default: array())
* @param array $externalIds (default: array())
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersStatuses(array $ids = [], array $externalIds = [])
{
$parameters = [];
if (count($ids)) {
$parameters['ids'] = $ids;
}
if (count($externalIds)) {
$parameters['externalIds'] = $externalIds;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/statuses',
"GET",
$parameters
);
}
/**
* Upload array of the orders
*
* @param array $orders array of orders
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersUpload(array $orders, $site = null)
{
if (!count($orders)) {
throw new \InvalidArgumentException(
'Parameter `orders` must contains array of the orders'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/upload',
"POST",
$this->fillSite($site, ['orders' => json_encode($orders)])
);
}
/**
* Get order by id or externalId
*
* @param string $id order identificator
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersGet($id, $by = 'externalId', $site = null)
{
$this->checkIdParameter($by);
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/orders/$id",
"GET",
$this->fillSite($site, ['by' => $by])
);
}
/**
* Edit an order
*
* @param array $order order data
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersEdit(array $order, $by = 'externalId', $site = null)
{
if (!count($order)) {
throw new \InvalidArgumentException(
'Parameter `order` must contains a data'
);
}
$this->checkIdParameter($by);
if (!array_key_exists($by, $order)) {
throw new \InvalidArgumentException(
sprintf('Order array must contain the "%s" parameter.', $by)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/orders/%s/edit', $order[$by]),
"POST",
$this->fillSite(
$site,
['order' => json_encode($order), 'by' => $by]
)
);
}
/**
* Get orders history
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersHistory(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/history',
"GET",
$parameters
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Packs
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\Packs as Previous;
/**
* PHP version 5.4
*
* Packs class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Packs
{
use Previous;
}

View File

@ -1,77 +0,0 @@
<?php
/**
* PHP version 5.4
*
* References
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\References as Previous;
/**
* PHP version 5.4
*
* References class
*
* @category RetailCrm
* @package RetailCrm
*/
trait References
{
use Previous;
/**
* Get prices types
*
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function pricesTypes()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/price-types',
"GET"
);
}
/**
* Edit price type
*
* @param array $data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function pricesTypesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
if (!array_key_exists('name', $data)) {
throw new \InvalidArgumentException(
'Data must contain "name" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/price-types/%s/edit', $data['code']),
"POST",
['priceType' => json_encode($data)]
);
}
}

View File

@ -1,183 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Settings
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
/**
* PHP version 5.4
*
* Settings class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Settings
{
/**
* Edit store configuration
*
* @param array $configuration
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeSettingsEdit(array $configuration)
{
if (!count($configuration) || empty($configuration['code'])) {
throw new \InvalidArgumentException(
'Parameter `configuration` must contains a data & configuration `code` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/store/setting/%s/edit', $configuration['code']),
"POST",
['configuration' => json_encode($configuration)]
);
}
/**
* Edit telephony settings
*
* @param string $code symbolic code
* @param string $clientId client id
* @param boolean $active telephony activity
* @param mixed $name service name
* @param mixed $makeCallUrl service init url
* @param mixed $image service logo url(svg file)
*
* @param array $additionalCodes
* @param array $externalPhones
* @param bool $allowEdit
* @param bool $inputEventSupported
* @param bool $outputEventSupported
* @param bool $hangupEventSupported
* @param bool $changeUserStatusUrl
*
* @return \RetailCrm\Response\ApiResponse
*/
public function telephonySettingsEdit(
$code,
$clientId,
$active = false,
$name = false,
$makeCallUrl = false,
$image = false,
$additionalCodes = [],
$externalPhones = [],
$allowEdit = false,
$inputEventSupported = false,
$outputEventSupported = false,
$hangupEventSupported = false,
$changeUserStatusUrl = false
) {
if (!isset($code)) {
throw new \InvalidArgumentException('Code must be set');
}
$parameters['code'] = $code;
if (!isset($clientId)) {
throw new \InvalidArgumentException('client id must be set');
}
$parameters['clientId'] = $clientId;
if (!isset($active)) {
$parameters['active'] = false;
} else {
$parameters['active'] = $active;
}
if (!isset($name)) {
throw new \InvalidArgumentException('name must be set');
}
if (isset($name)) {
$parameters['name'] = $name;
}
if (isset($makeCallUrl)) {
$parameters['makeCallUrl'] = $makeCallUrl;
}
if (isset($image)) {
$parameters['image'] = $image;
}
if (isset($additionalCodes)) {
$parameters['additionalCodes'] = $additionalCodes;
}
if (isset($externalPhones)) {
$parameters['externalPhones'] = $externalPhones;
}
if (isset($allowEdit)) {
$parameters['allowEdit'] = $allowEdit;
}
if (isset($inputEventSupported)) {
$parameters['inputEventSupported'] = $inputEventSupported;
}
if (isset($outputEventSupported)) {
$parameters['outputEventSupported'] = $outputEventSupported;
}
if (isset($hangupEventSupported)) {
$parameters['hangupEventSupported'] = $hangupEventSupported;
}
if (isset($changeUserStatusUrl)) {
$parameters['changeUserStatusUrl'] = $changeUserStatusUrl;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/telephony/setting/$code/edit",
"POST",
['configuration' => json_encode($parameters)]
);
}
/**
* Edit delivery configuration
*
* @param array $configuration
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliverySettingsEdit(array $configuration)
{
if (!count($configuration) || empty($configuration['code'])) {
throw new \InvalidArgumentException(
'Parameter `configuration` must contains a data & configuration `code` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/delivery/generic/setting/%s/edit', $configuration['code']),
"POST",
['configuration' => json_encode($configuration)]
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Statistic
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\Statistic as Previous;
/**
* PHP version 5.4
*
* Statistic class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Statistic
{
use Previous;
}

View File

@ -1,116 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Stores
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\Stores as Previous;
/**
* PHP version 5.4
*
* Stores class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Stores
{
use Previous;
/**
* Get store settings
*
* @param string $code get settings code
*
* @return \RetailCrm\Response\ApiResponse
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeSettingsGet($code)
{
if (empty($code)) {
throw new \InvalidArgumentException('Parameter `code` must be set');
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/store/setting/$code",
"GET"
);
}
/**
* Upload store prices
*
* @param array $prices prices data
* @param string $site default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storePricesUpload(array $prices, $site = null)
{
if (!count($prices)) {
throw new \InvalidArgumentException(
'Parameter `prices` must contains array of the prices'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/store/prices/upload',
"POST",
$this->fillSite($site, ['prices' => json_encode($prices)])
);
}
/**
* Get products
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeProducts(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/store/products',
"GET",
$parameters
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Telephony
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
use RetailCrm\Methods\V3\Telephony as Previous;
/**
* PHP version 5.4
*
* Telephony class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Telephony
{
use Previous;
}

View File

@ -1,105 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Users
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V4;
/**
* PHP version 5.4
*
* Users class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Users
{
/**
* Returns users list
*
* @param array $filter
* @param null $page
* @param null $limit
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function usersList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int)$page;
}
if (null !== $limit) {
$parameters['limit'] = (int)$limit;
}
/** @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/users',
"GET",
$parameters
);
}
/**
* Get user groups
*
* @param null $page
* @param null $limit
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function usersGroups($page = null, $limit = null)
{
$parameters = [];
if (null !== $page) {
$parameters['page'] = (int)$page;
}
if (null !== $limit) {
$parameters['limit'] = (int)$limit;
}
/** @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/user-groups',
"GET",
$parameters
);
}
/**
* Returns user data
*
* @param integer $id user ID
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function usersGet($id)
{
/** @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest("/users/$id", "GET");
}
}

View File

@ -1,216 +0,0 @@
<?php
/**
* PHP version 5.4
*
* CostsTrait
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* CostsTrait class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Costs
{
/**
* Returns filtered costs list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/costs',
"GET",
$parameters
);
}
/**
* Create a cost
*
* @param array $cost cost data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsCreate(array $cost, $site = null)
{
if (!count($cost)) {
throw new \InvalidArgumentException(
'Parameter `cost` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/costs/create',
"POST",
$this->fillSite($site, ['cost' => json_encode($cost)])
);
}
/**
* Delete costs set
*
* @param array $ids costs identifiers
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsDelete(array $ids)
{
if (!count($ids)) {
throw new \InvalidArgumentException(
'Parameter `ids` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/costs/delete',
"POST",
['ids' => json_encode($ids)]
);
}
/**
* Upload costs
*
* @param array $costs costs array
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsUpload($costs)
{
if (!count($costs)) {
throw new \InvalidArgumentException(
'Parameter `costs` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/costs/upload',
"POST",
['costs' => json_encode($costs)]
);
}
/**
* Get cost by id
*
* @param string $id customer identificator
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsGet($id)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/costs/$id",
"GET"
);
}
/**
* Edit a cost
*
* @param array $cost cost data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsEdit(array $cost, $site = null)
{
if (!count($cost)) {
throw new \InvalidArgumentException(
'Parameter `cost` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/costs/%s/edit', $cost['id']),
"POST",
$this->fillSite(
$site,
['cost' => json_encode($cost)]
)
);
}
/**
* Delete cost by id
*
* @param string $id cost identifier
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costsDeleteById($id)
{
if (!empty($id)) {
throw new \InvalidArgumentException(
'Parameter `id` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/costs/%s/delete', $id),
"POST"
);
}
}

View File

@ -1,261 +0,0 @@
<?php
/**
* PHP version 5.4
*
* CustomFields
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* CustomFields class
*
* @category RetailCrm
* @package RetailCrm
*/
trait CustomFields
{
/**
* Get custom fields list
*
* @param array $filter
* @param null $limit
* @param null $page
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customFieldsList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/custom-fields',
"GET",
$parameters
);
}
/**
* Create custom field
*
* @param $entity
* @param $customField
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customFieldsCreate($entity, $customField)
{
if (!count($customField) ||
empty($customField['code']) ||
empty($customField['name']) ||
empty($customField['type'])
) {
throw new \InvalidArgumentException(
'Parameter `customField` must contain a data & fields `code`, `name` & `type` must be set'
);
}
if (empty($entity) || !in_array($entity, ['customer', 'order', 'customer_corporate', 'company'])) {
throw new \InvalidArgumentException(
sprintf(
'Parameter `entity` must contain a data & value must be %s',
'`order`, `customer`, `customer_corporate` or `company`'
)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/custom-fields/$entity/create",
"POST",
['customField' => json_encode($customField)]
);
}
/**
* Edit custom field
*
* @param $entity
* @param $customField
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customFieldsEdit($entity, $customField)
{
if (!count($customField) || empty($customField['code'])) {
throw new \InvalidArgumentException(
'Parameter `customField` must contain a data & fields `code` must be set'
);
}
if (empty($entity) || !in_array($entity, ['customer', 'order', 'customer_corporate', 'company'])) {
throw new \InvalidArgumentException(
sprintf(
'Parameter `entity` must contain a data & value must be %s',
'`order`, `customer`, `customer_corporate` or `company`'
)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/custom-fields/$entity/{$customField['code']}/edit",
"POST",
['customField' => json_encode($customField)]
);
}
/**
* Get custom field
*
* @param $entity
* @param $code
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customFieldsGet($entity, $code)
{
if (empty($code)) {
throw new \InvalidArgumentException(
'Parameter `code` must be not empty'
);
}
if (empty($entity) || !in_array($entity, ['customer', 'order', 'customer_corporate', 'company'])) {
throw new \InvalidArgumentException(
sprintf(
'Parameter `entity` must contain a data & value must be %s',
'`order`, `customer`, `customer_corporate` or `company`'
)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/custom-fields/$entity/$code",
"GET"
);
}
/**
* Get custom dictionaries list
*
* @param array $filter
* @param null $limit
* @param null $page
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customDictionariesList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/custom-fields/dictionaries',
"GET",
$parameters
);
}
/**
* Create custom dictionary
*
* @param $customDictionary
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customDictionariesCreate($customDictionary)
{
if (!count($customDictionary) ||
empty($customDictionary['code']) ||
empty($customDictionary['elements'])
) {
throw new \InvalidArgumentException(
'Parameter `dictionary` must contain a data & fields `code` & `elemets` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/custom-fields/dictionaries/create",
"POST",
['customDictionary' => json_encode($customDictionary)]
);
}
/**
* Edit custom dictionary
*
* @param $customDictionary
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customDictionariesEdit($customDictionary)
{
if (!count($customDictionary) ||
empty($customDictionary['code']) ||
empty($customDictionary['elements'])
) {
throw new \InvalidArgumentException(
'Parameter `dictionary` must contain a data & fields `code` & `elemets` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/custom-fields/dictionaries/{$customDictionary['code']}/edit",
"POST",
['customDictionary' => json_encode($customDictionary)]
);
}
/**
* Get custom dictionary
*
* @param $code
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customDictionariesGet($code)
{
if (empty($code)) {
throw new \InvalidArgumentException(
'Parameter `code` must be not empty'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/custom-fields/dictionaries/$code",
"GET"
);
}
}

View File

@ -1,144 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Customers
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Customers as Previous;
/**
* PHP version 5.4
*
* Customers class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Customers
{
use Previous;
/**
* Combine customers
*
* @param array $customers
* @param array $resultCustomer
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCombine(array $customers, $resultCustomer)
{
if (!count($customers) || !count($resultCustomer)) {
throw new \InvalidArgumentException(
'Parameters `customers` & `resultCustomer` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/combine',
"POST",
[
'customers' => json_encode($customers),
'resultCustomer' => json_encode($resultCustomer)
]
);
}
/**
* Returns filtered customers notes list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersNotesList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/notes',
"GET",
$parameters
);
}
/**
* Create customer note
*
* @param array $note (default: array())
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersNotesCreate($note, $site = null)
{
if (empty($note['customer']['id']) && empty($note['customer']['externalId'])) {
throw new \InvalidArgumentException(
'Customer identifier must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers/notes/create',
"POST",
$this->fillSite($site, ['note' => json_encode($note)])
);
}
/**
* Delete customer note
*
* @param integer $id
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersNotesDelete($id)
{
if (empty($id)) {
throw new \InvalidArgumentException(
'Note id must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers/notes/$id/delete",
"POST"
);
}
}

View File

@ -1,656 +0,0 @@
<?php
/**
* PHP version 5.4
*
* CustomersCorporate
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* CustomersCorporate class
*
* @category RetailCrm
* @package RetailCrm
*/
trait CustomersCorporate
{
/**
* Returns filtered corporate customers list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate',
"GET",
$parameters
);
}
/**
* Create a corporate customer
*
* @param array $customerCorporate corporate customer data
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateCreate(array $customerCorporate, $site = null)
{
if (! count($customerCorporate)) {
throw new \InvalidArgumentException(
'Parameter `customerCorporate` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate/create',
"POST",
$this->fillSite($site, ['customerCorporate' => json_encode($customerCorporate)])
);
}
/**
* Save corporate customer IDs' (id and externalId) association in the CRM
*
* @param array $ids ids mapping
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateFixExternalIds(array $ids)
{
if (! count($ids)) {
throw new \InvalidArgumentException(
'Method parameter must contains at least one IDs pair'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate/fix-external-ids',
"POST",
['customersCorporate' => json_encode($ids)]
);
}
/**
* Get corporate customers history
* @param array $filter
* @param null $page
* @param null $limit
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateHistory(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate/history',
"GET",
$parameters
);
}
/**
* Returns filtered corporate customers notes list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateNotesList(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate/notes',
"GET",
$parameters
);
}
/**
* Create corporate customer note
*
* @param array $note (default: array())
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateNotesCreate($note, $site = null)
{
if (empty($note['customer']['id']) && empty($note['customer']['externalId'])) {
throw new \InvalidArgumentException(
'Customer identifier must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate/notes/create',
"POST",
$this->fillSite($site, ['note' => json_encode($note)])
);
}
/**
* Delete corporate customer note
*
* @param integer $id
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateNotesDelete($id)
{
if (empty($id)) {
throw new \InvalidArgumentException(
'Note id must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/notes/$id/delete",
"POST"
);
}
/**
* Upload array of the corporate customers
*
* @param array $customersCorporate array of corporate customers
* @param string $site (default: null)
*
* @return \RetailCrm\Response\ApiResponse
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @throws \InvalidArgumentException
*/
public function customersCorporateUpload(array $customersCorporate, $site = null)
{
if (!count($customersCorporate)) {
throw new \InvalidArgumentException(
'Parameter `customersCorporate` must contains array of the corporate customers'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/customers-corporate/upload',
"POST",
$this->fillSite($site, ['customersCorporate' => json_encode($customersCorporate)])
);
}
/**
* Get corporate customer by id or externalId
*
* @param string $id corporate customer identifier
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateGet($id, $by = 'externalId', $site = null)
{
$this->checkIdParameter($by);
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id",
"GET",
$this->fillSite($site, ['by' => $by])
);
}
/**
* Get corporate customer addresses by id or externalId
*
* @param string $id corporate customer identifier
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateAddresses(
$id,
array $filter = [],
$page = null,
$limit = null,
$by = 'externalId',
$site = null
) {
$this->checkIdParameter($by);
$parameters = ['by' => $by];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id/addresses",
"GET",
$this->fillSite($site, $parameters)
);
}
/**
* Create corporate customer address
*
* @param string $id corporate customer identifier
* @param array $address (default: array())
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateAddressesCreate($id, array $address = [], $by = 'externalId', $site = null)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id/addresses/create",
"POST",
$this->fillSite($site, ['address' => json_encode($address), 'by' => $by])
);
}
/**
* Edit corporate customer address
*
* @param string $customerId corporate customer identifier
* @param string $addressId corporate customer identifier
* @param array $address (default: array())
* @param string $customerBy (default: 'externalId')
* @param string $addressBy (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateAddressesEdit(
$customerId,
$addressId,
array $address = [],
$customerBy = 'externalId',
$addressBy = 'externalId',
$site = null
) {
$addressFiltered = array_filter($address);
if ((count(array_keys($addressFiltered)) <= 1)
&& (!isset($addressFiltered['text'])
|| (isset($addressFiltered['text']) && empty($addressFiltered['text']))
)
) {
throw new \InvalidArgumentException(
'Parameter `address` must contain address text or all other address field'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$customerId/addresses/$addressId/edit",
"POST",
$this->fillSite($site, [
'address' => json_encode($address),
'by' => $customerBy,
'entityBy' => $addressBy
])
);
}
/**
* Get corporate customer companies by id or externalId
*
* @param string $id corporate customer identifier
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateCompanies(
$id,
array $filter = [],
$page = null,
$limit = null,
$by = 'externalId',
$site = null
) {
$this->checkIdParameter($by);
$parameters = ['by' => $by];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id/companies",
"GET",
$this->fillSite($site, $parameters)
);
}
/**
* Create corporate customer company
*
* @param string $id corporate customer identifier
* @param array $company (default: array())
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateCompaniesCreate($id, array $company = [], $by = 'externalId', $site = null)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id/companies/create",
"POST",
$this->fillSite($site, ['company' => json_encode($company), 'by' => $by])
);
}
/**
* Edit corporate customer company
*
* @param string $customerId corporate customer identifier
* @param string $companyId corporate customer identifier
* @param array $company (default: array())
* @param string $customerBy (default: 'externalId')
* @param string $companyBy (default: 'externalId')
* @param string $site (default: null)
*
* @return \RetailCrm\Response\ApiResponse
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
*/
public function customersCorporateCompaniesEdit(
$customerId,
$companyId,
array $company = [],
$customerBy = 'externalId',
$companyBy = 'externalId',
$site = null
) {
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$customerId/companies/$companyId/edit",
"POST",
$this->fillSite($site, [
'company' => json_encode($company),
'by' => $customerBy,
'entityBy' => $companyBy
])
);
}
/**
* Get corporate customer contacts by id or externalId
*
* @param string $id corporate customer identifier
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateContacts(
$id,
array $filter = [],
$page = null,
$limit = null,
$by = 'externalId',
$site = null
) {
$this->checkIdParameter($by);
$parameters = ['by' => $by];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id/contacts",
"GET",
$this->fillSite($site, $parameters)
);
}
/**
* Create corporate customer contact
*
* @param string $id corporate customer identifier
* @param array $contact (default: array())
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @return \RetailCrm\Response\ApiResponse
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @throws \InvalidArgumentException
*/
public function customersCorporateContactsCreate($id, array $contact = [], $by = 'externalId', $site = null)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$id/contacts/create",
"POST",
$this->fillSite($site, ['contact' => json_encode($contact), 'by' => $by])
);
}
/**
* Edit corporate customer contact
*
* @param string $customerId corporate customer identifier
* @param string $contactId corporate customer identifier
* @param array $contact (default: array())
* @param string $customerBy (default: 'externalId')
* @param string $contactBy (default: 'externalId')
* @param string $site (default: null)
*
* @return \RetailCrm\Response\ApiResponse
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
*/
public function customersCorporateContactsEdit(
$customerId,
$contactId,
array $contact = [],
$customerBy = 'externalId',
$contactBy = 'externalId',
$site = null
) {
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/customers-corporate/$customerId/contacts/$contactId/edit",
"POST",
$this->fillSite($site, [
'contact' => json_encode($contact),
'by' => $customerBy,
'entityBy' => $contactBy
])
);
}
/**
* Edit a corporate customer
*
* @param array $customerCorporate corporate customer data
* @param string $by (default: 'externalId')
* @param string $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function customersCorporateEdit(array $customerCorporate, $by = 'externalId', $site = null)
{
if (!count($customerCorporate)) {
throw new \InvalidArgumentException(
'Parameter `customerCorporate` must contains a data'
);
}
$this->checkIdParameter($by);
if (!array_key_exists($by, $customerCorporate)) {
throw new \InvalidArgumentException(
sprintf('Corporate customer array must contain the "%s" parameter.', $by)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/customers-corporate/%s/edit', $customerCorporate[$by]),
"POST",
$this->fillSite(
$site,
['customerCorporate' => json_encode($customerCorporate), 'by' => $by]
)
);
}
}

View File

@ -1,178 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Delivery
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Delivery as Previous;
/**
* PHP version 5.4
*
* Delivery class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Delivery
{
use Previous;
/**
* Get delivery settings
*
* @param string $code delivery code
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return void
*/
public function deliverySettingsGet($code)
{
throw new \InvalidArgumentException("This method is not available, setting code: $code is unnecessary");
}
/**
* Get delivery list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryShipmentsList(
array $filter = [],
$page = null,
$limit = null
) {
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/delivery/shipments',
"GET",
$parameters
);
}
/**
* Create delivery shipment
*
* @param array $shipment (default: array())
* @param string $deliveryType (default: string)
* @param null $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryShipmentsCreate(
array $shipment,
$deliveryType,
$site = null
) {
if (!count($shipment)) {
throw new \InvalidArgumentException(
'Parameter `shipment` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/delivery/shipments/create',
"POST",
$this->fillSite(
$site,
[
'deliveryShipment' => json_encode($shipment),
'deliveryType' => $deliveryType
]
)
);
}
/**
* Get shipment
*
* @param string $id shipment id
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryShipmentGet($id)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf("/delivery/shipments/%s", $id),
"GET"
);
}
/**
* Edit delivery shipment
*
* @param array $shipment (default: array())
* @param null $site (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function deliveryShipmentsEdit(array $shipment, $site = null)
{
if (!count($shipment)) {
throw new \InvalidArgumentException(
'Parameter `shipment` must contains a data'
);
}
if (empty($shipment['id'])) {
throw new \InvalidArgumentException(
'Parameter `shipment` must contains an `id` field'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf("/delivery/shipments/%s/edit", $shipment['id']),
"POST",
$this->fillSite(
$site,
[
'deliveryShipment' => json_encode($shipment)
]
)
);
}
}

View File

@ -1,209 +0,0 @@
<?php
/**
* PHP version 5.4
*
* CostsTrait
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* Files trait
*
* @category RetailCrm
* @package RetailCrm
*/
trait Files
{
/**
* Returns filtered files list
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function filesList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/files',
"GET",
$parameters
);
}
/**
* Upload file
*
* @param string $file filepath
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function fileUpload($file)
{
if (!file_exists($file)) {
throw new \InvalidArgumentException("File doesn't exist");
}
if (filesize($file) == 0) {
throw new \InvalidArgumentException("Empty file provided");
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/files/upload',
"POST",
["file" => $file]
);
}
/**
* Get file by id
*
* @param string $id file ID
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function fileGet($id)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/files/$id",
"GET"
);
}
/**
* Delete file by id
*
* @param string $id file ID
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function fileDelete($id)
{
if (empty($id)) {
throw new \InvalidArgumentException(
'Parameter `id` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/files/%s/delete', $id),
"POST"
);
}
/**
* Download file by id
*
* @param string $id file ID
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function fileDownload($id)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRawRequest(
sprintf('/files/%s/download', $id),
"GET"
);
}
/**
* Edit file data
*
* @param int $id file ID
* @param array $file file data
*
* $file = [
* 'filename' => 'Test file',
* 'attachment' => [
* [
* 'customer' => [
* 'id' => 1
* ],
* 'order' => [
* 'id' => 1
* ]
* ]
* ]
* ];
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function fileEdit($id, array $file)
{
if (empty($id)) {
throw new \InvalidArgumentException(
'Parameter `id` can`t be blank'
);
}
if (empty($file)) {
throw new \InvalidArgumentException(
'Parameter `file` must contains a data'
);
}
$allowedFields = ['filename', 'attachment'];
foreach (array_keys($file) as $field) {
if (!in_array($field, $allowedFields)) {
throw new \InvalidArgumentException(
'Invalid structure of `file` parameter'
);
}
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/files/%s/edit', $id),
"POST",
['file' => json_encode($file), 'id' => $id]
);
}
}

View File

@ -1,95 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Orders
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* Orders class
*
* @category RetailCrm
* @package RetailCrm
*/
trait IntegrationPayments
{
/**
* Create Invoice
*
* @param array $createInvoice
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentCreateInvoice($createInvoice)
{
if (!count($createInvoice)) {
throw new \InvalidArgumentException(
'Parameters `createInvoice` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/payment/create-invoice',
'POST',
[
'createInvoice' => json_encode($createInvoice)
]
);
}
/**
* Update Invoice
*
* @param array $updateInvoice
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentUpdateInvoice($updateInvoice)
{
if (!count($updateInvoice)) {
throw new \InvalidArgumentException(
'Parameters `updateInvoice` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/payment/update-invoice',
'POST',
[
'updateInvoice' => json_encode($updateInvoice)
]
);
}
/**
* Check Invoice
*
* @param array $check
* @return \RetailCrm\Response\ApiResponse
*/
public function paymentCheckInvoice($check)
{
if (!count($check)) {
throw new \InvalidArgumentException(
'Parameters `check` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/payment/check',
'POST',
[
'check' => json_encode($check)
]
);
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Marketplace
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* Module class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Module
{
/**
* Get module configuration
*
* @param string $code
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function integrationModulesGet($code)
{
if (empty($code)) {
throw new \InvalidArgumentException(
'Parameter `code` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/integration-modules/%s', $code),
"GET"
);
}
/**
* Edit module configuration
*
* @param array $configuration
*
* @throws \RetailCrm\Exception\InvalidJsonException
* @throws \RetailCrm\Exception\CurlException
* @throws \InvalidArgumentException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function integrationModulesEdit(array $configuration)
{
if (!count($configuration) || empty($configuration['code'])) {
throw new \InvalidArgumentException(
'Parameter `configuration` must contains a data & configuration `code` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/integration-modules/%s/edit', $configuration['code']),
"POST",
['integrationModule' => json_encode($configuration)]
);
}
}

View File

@ -1,153 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Orders
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Orders as Previous;
/**
* PHP version 5.4
*
* Orders class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Orders
{
use Previous;
/**
* Combine orders
*
* @param array $order orgin order
* @param array $resultOrder result order
* @param string $technique combining technique
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersCombine($order, $resultOrder, $technique = 'ours')
{
$techniques = ['ours', 'summ', 'theirs', 'merge'];
if (!count($order) || !count($resultOrder)) {
throw new \InvalidArgumentException(
'Parameters `order` & `resultOrder` must contains a data'
);
}
if (!in_array($technique, $techniques)) {
throw new \InvalidArgumentException(
'Parameter `technique` must be on of ours|summ|theirs'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/combine',
"POST",
[
'technique' => $technique,
'order' => json_encode($order),
'resultOrder' => json_encode($resultOrder)
]
);
}
/**
* Create an order payment
*
* @param array $payment order data
* @param null $site site code
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPaymentCreate(array $payment, $site = null)
{
if (!count($payment)) {
throw new \InvalidArgumentException(
'Parameter `payment` must contains a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/orders/payments/create',
"POST",
$this->fillSite(
$site,
['payment' => json_encode($payment)]
)
);
}
/**
* Edit an order payment
*
* @param array $payment order data
* @param string $by by key
* @param null $site site code
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPaymentEdit(array $payment, $by = 'id', $site = null)
{
if (!count($payment)) {
throw new \InvalidArgumentException(
'Parameter `payment` must contains a data'
);
}
$this->checkIdParameter($by);
if (!array_key_exists($by, $payment)) {
throw new \InvalidArgumentException(
sprintf('Order array must contain the "%s" parameter.', $by)
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/orders/payments/%s/edit', $payment[$by]),
"POST",
$this->fillSite(
$site,
['payment' => json_encode($payment), 'by' => $by]
)
);
}
/**
* Edit an order payment
*
* @param string $id payment id
*
* @return \RetailCrm\Response\ApiResponse
*/
public function ordersPaymentDelete($id)
{
if (!$id) {
throw new \InvalidArgumentException(
'Parameter `id` must be set'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/orders/payments/%s/delete', $id),
"POST"
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Packs
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Packs as Previous;
/**
* PHP version 5.4
*
* Packs class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Packs
{
use Previous;
}

View File

@ -1,287 +0,0 @@
<?php
/**
* PHP version 5.4
*
* References
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\References as Previous;
/**
* PHP version 5.4
*
* References class
*
* @category RetailCrm
* @package RetailCrm
*/
trait References
{
use Previous;
/**
* Get costs groups
*
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costGroups()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/cost-groups',
"GET"
);
}
/**
* Edit costs groups
*
* @param array $data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costGroupsEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
if (!array_key_exists('name', $data)) {
throw new \InvalidArgumentException(
'Data must contain "name" parameter.'
);
}
if (!array_key_exists('color', $data)) {
throw new \InvalidArgumentException(
'Data must contain "color" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/cost-groups/%s/edit', $data['code']),
"POST",
['costGroup' => json_encode($data)]
);
}
/**
* Get costs items
*
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costItems()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/cost-items',
"GET"
);
}
/**
* Edit costs items
*
* @param array $data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function costItemsEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
if (!array_key_exists('name', $data)) {
throw new \InvalidArgumentException(
'Data must contain "name" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/cost-items/%s/edit', $data['code']),
"POST",
['costItem' => json_encode($data)]
);
}
/**
* Get legal entities
*
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function legalEntities()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/legal-entities',
"GET"
);
}
/**
* Edit legal entity
*
* @param array $data
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function legalEntitiesEdit(array $data)
{
if (!array_key_exists('code', $data)) {
throw new \InvalidArgumentException(
'Data must contain "code" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/legal-entities/%s/edit', $data['code']),
"POST",
['legalEntity' => json_encode($data)]
);
}
/**
* Get couriers
*
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function couriersList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/couriers',
"GET"
);
}
/**
* Create courier
*
* @param array $courier
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function couriersCreate(array $courier)
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/couriers/create',
"POST",
['courier' => json_encode($courier)]
);
}
/**
* Edit courier
*
* @param array $courier
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function couriersEdit(array $courier)
{
if (!array_key_exists('id', $courier)) {
throw new \InvalidArgumentException(
'Data must contain "id" parameter.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/couriers/%s/edit', $courier['id']),
"POST",
['courier' => json_encode($courier)]
);
}
/**
* Get units
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function unitsList()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/reference/units',
"GET"
);
}
/**
* Edit unit
*
* @param array $unit
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function unitsEdit(array $unit)
{
if (empty($unit['code']) || empty($unit['name']) || empty($unit['sym'])) {
throw new \InvalidArgumentException(
'`code`, `name` and `sym` parameters must not be empty.'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
sprintf('/reference/units/%s/edit', $unit['code']),
"POST",
['unit' => json_encode($unit)]
);
}
}

View File

@ -1,54 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Segments
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* Segments class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Segments
{
/**
* Get segments list
*
* @param array $filter
* @param null $limit
* @param null $page
*
* @return \RetailCrm\Response\ApiResponse
*/
public function segmentsList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/segments',
"GET",
$parameters
);
}
}

View File

@ -1,38 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Settings
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* Settings class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Settings
{
/**
* Get settings
*
* @return \RetailCrm\Response\ApiResponse
*/
public function settings()
{
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/settings',
"GET",
[]
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Statistic
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V3\Statistic as Previous;
/**
* PHP version 5.4
*
* Statistic class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Statistic
{
use Previous;
}

View File

@ -1,110 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Stores
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Stores as Previous;
/**
* PHP version 5.4
*
* Stores class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Stores
{
use Previous;
/**
* Get store settings
*
* @param string $code get settings code
*
* @return void
*
*/
public function storeSettingsGet($code)
{
throw new \InvalidArgumentException("This method is not available, setting code: $code is unnecessary");
}
/**
* Get products groups
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeProductsGroups(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/store/product-groups',
"GET",
$parameters
);
}
/**
* Get products properties
*
* @param array $filter (default: array())
* @param int $page (default: null)
* @param int $limit (default: null)
*
* @throws \InvalidArgumentException
* @throws \RetailCrm\Exception\CurlException
* @throws \RetailCrm\Exception\InvalidJsonException
*
* @return \RetailCrm\Response\ApiResponse
*/
public function storeProductsProperties(array $filter = [], $page = null, $limit = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/store/products/properties',
"GET",
$parameters
);
}
}

View File

@ -1,132 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Tasks
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
/**
* PHP version 5.4
*
* Tasks class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Tasks
{
/**
* Get tasks list
*
* @param array $filter
* @param null $limit
* @param null $page
*
* @return \RetailCrm\Response\ApiResponse
*/
public function tasksList(array $filter = [], $limit = null, $page = null)
{
$parameters = [];
if (count($filter)) {
$parameters['filter'] = $filter;
}
if (null !== $page) {
$parameters['page'] = (int) $page;
}
if (null !== $limit) {
$parameters['limit'] = (int) $limit;
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
'/tasks',
"GET",
$parameters
);
}
/**
* Create task
*
* @param array $task
* @param null $site
*
* @return \RetailCrm\Response\ApiResponse
*
*/
public function tasksCreate($task, $site = null)
{
if (!count($task)) {
throw new \InvalidArgumentException(
'Parameter `task` must contain a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/tasks/create",
"POST",
$this->fillSite(
$site,
['task' => json_encode($task)]
)
);
}
/**
* Edit task
*
* @param array $task
* @param null $site
*
* @return \RetailCrm\Response\ApiResponse
*
*/
public function tasksEdit($task, $site = null)
{
if (!count($task)) {
throw new \InvalidArgumentException(
'Parameter `task` must contain a data'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/tasks/{$task['id']}/edit",
"POST",
$this->fillSite(
$site,
['task' => json_encode($task)]
)
);
}
/**
* Get custom dictionary
*
* @param $id
*
* @return \RetailCrm\Response\ApiResponse
*/
public function tasksGet($id)
{
if (empty($id)) {
throw new \InvalidArgumentException(
'Parameter `id` must be not empty'
);
}
/* @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/tasks/$id",
"GET"
);
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Telephony
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Telephony as Previous;
/**
* PHP version 5.4
*
* Telephony class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Telephony
{
use Previous;
/**
* @param string $code integration code
*/
public function telephonySettingsGet($code)
{
throw new \InvalidArgumentException("This method is not available, setting code: $code is unnecessary");
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Users
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Methods\V5;
use RetailCrm\Methods\V4\Users as Previous;
/**
* PHP version 5.4
*
* Users class
*
* @category RetailCrm
* @package RetailCrm
*/
trait Users
{
use Previous;
/**
* Change user status
*
* @param integer $id user ID
* @param string $status user status
*
* @return \RetailCrm\Response\ApiResponse
*/
public function usersStatus($id, $status)
{
$statuses = ["free", "busy", "dinner", "break"];
if (empty($status) || !in_array($status, $statuses)) {
throw new \InvalidArgumentException(
'Parameter `status` must be not empty & must be equal one of these values: free|busy|dinner|break'
);
}
/** @noinspection PhpUndefinedMethodInspection */
return $this->client->makeRequest(
"/users/$id/status",
"POST",
['status' => $status]
);
}
}

View File

@ -1,222 +0,0 @@
<?php
/**
* PHP version 5.4
*
* Response from RetailCRM API
*
* @category RetailCrm
* @package RetailCrm
*/
namespace RetailCrm\Response;
use RetailCrm\Exception\InvalidJsonException;
/**
* PHP version 5.4
*
* Response from RetailCRM API
*
* @property mixed success
* @category RetailCrm
* @package RetailCrm
*/
class ApiResponse implements \ArrayAccess
{
// HTTP response status code
protected $statusCode;
// raw json response
protected $rawResponse;
// response assoc array
protected $response;
/**
* ApiResponse constructor.
*
* @param int $statusCode HTTP status code
* @param mixed $responseBody HTTP body
*
* @throws InvalidJsonException
*/
public function __construct($statusCode, $responseBody = null)
{
$this->statusCode = (int) $statusCode;
$this->rawResponse = $responseBody;
}
/**
* Deserialize JSON from raw response body
*
* @return $this
*/
public function asJsonResponse()
{
if (!empty($this->rawResponse)) {
$response = json_decode($this->rawResponse, true);
if (!$response && JSON_ERROR_NONE !== ($error = json_last_error())) {
throw new InvalidJsonException(
"Invalid JSON in the API response body. Error code #$error",
$error
);
}
$this->response = $response;
}
return $this;
}
/**
* Return HTTP response status code
*
* @return int
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* Return HTTP response
*
* @return array
*/
public function getResponse()
{
return $this->response;
}
/**
* Return HTTP raw response body
*
* @return string
*/
public function getResponseBody()
{
return $this->rawResponse;
}
/**
* HTTP request was successful
*
* @return bool
*/
public function isSuccessful()
{
return $this->statusCode < 400;
}
/**
* Allow to access for the property throw class method
*
* @param string $name method name
* @param mixed $arguments method parameters
*
* @throws \InvalidArgumentException
*
* @return mixed
*/
public function __call($name, $arguments)
{
// convert getSomeProperty to someProperty
$propertyName = strtolower(substr($name, 3, 1)) . substr($name, 4);
if (!isset($this->response[$propertyName])) {
throw new \InvalidArgumentException("Method \"$name\" not found");
}
return $this->response[$propertyName];
}
/**
* Allow to access for the property throw object property
*
* @param string $name property name
*
* @throws \InvalidArgumentException
*
* @return mixed
*/
public function __get($name)
{
if (!isset($this->response[$name])) {
throw new \InvalidArgumentException("Property \"$name\" not found");
}
return $this->response[$name];
}
/**
* Allow to check if the property exists through object property
*
* @param string $name property name
*
* @return bool
*/
public function __isset($name)
{
return isset($this->response[$name]);
}
/**
* Offset set
*
* @param mixed $offset offset
* @param mixed $value value
*
* @throws \BadMethodCallException
* @return void
*/
public function offsetSet($offset, $value)
{
throw new \BadMethodCallException('This activity not allowed');
}
/**
* Offset unset
*
* @param mixed $offset offset
*
* @throws \BadMethodCallException
* @return void
*/
public function offsetUnset($offset)
{
throw new \BadMethodCallException('This call not allowed');
}
/**
* Check offset
*
* @param mixed $offset offset
*
* @return bool
*/
public function offsetExists($offset)
{
return isset($this->response[$offset]);
}
/**
* Get offset
*
* @param mixed $offset offset
*
* @throws \InvalidArgumentException
*
* @return mixed
*/
public function offsetGet($offset)
{
if (!isset($this->response[$offset])) {
throw new \InvalidArgumentException("Property \"$offset\" not found");
}
return $this->response[$offset];
}
}

0
models/.gitkeep Normal file
View File

13
phpcs.xml.dist Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
<arg name="basepath" value="."/>
<arg name="cache" value=".php_cs.cache"/>
<arg name="colors"/>
<arg name="extensions" value="php"/>
<rule ref="PSR12"/>
<file>src/</file>
<file>tests/</file>
</ruleset>

21
phpdoc.dist.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdocumentor
configVersion="3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://www.phpdoc.org"
xsi:noNamespaceSchemaLocation="https://docs.phpdoc.org/latest/phpdoc.xsd"
>
<title>RetailCRM API Client</title>
<paths>
<output>docs/build/html</output>
<cache>docs/build/cache</cache>
</paths>
<version number="latest">
<api>
<visibility>public</visibility>
<source dsn=".">
<path>src</path>
</source>
</api>
</version>
</phpdocumentor>

47
phpmd.xml Normal file
View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="Ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>Ruleset</description>
<rule ref="rulesets/controversial.xml" />
<rule ref="rulesets/unusedcode.xml" />
<rule ref="rulesets/design.xml">
<exclude name="CouplingBetweenObjects" />
</rule>
<rule ref="rulesets/cleancode.xml">
<exclude name="StaticAccess" />
</rule>
<rule ref="rulesets/codesize.xml">
<exclude name="TooManyPublicMethods" />
<exclude name="TooManyFields" />
</rule>
<rule ref="rulesets/naming.xml">
<exclude name="ShortVariable" />
</rule>
<rule ref="rulesets/naming.xml/ShortVariable">
<properties>
<property name="minimum" value="2" />
</properties>
</rule>
<rule ref="rulesets/codesize.xml/TooManyPublicMethods">
<properties>
<property name="maxmethods" value="20" />
</properties>
</rule>
<rule ref="rulesets/codesize.xml/TooManyFields">
<properties>
<property name="maxfields" value="30" />
</properties>
</rule>
<rule ref="rulesets/design.xml/CouplingBetweenObjects">
<properties>
<property name="maximum" value="15" />
</properties>
</rule>
<exclude-pattern>tests/*</exclude-pattern>
</ruleset>

5
phpstan.neon Normal file
View File

@ -0,0 +1,5 @@
parameters:
level: max
paths:
- src
- tests

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.5/phpunit.xsd"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
colors="false"
bootstrap="tests/bootstrap.php"
backupStaticAttributes="false"
convertErrorsToExceptions="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false"
processIsolation="true"
@ -17,20 +16,27 @@
stopOnSkipped="false"
stopOnRisky="false"
>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>lib</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-clover" target="coverage.xml"/>
<log type="junit" target="test-report.xml"/>
</logging>
<coverage>
<include>
<directory>src</directory>
<directory>dev</directory>
</include>
<report>
<clover outputFile="coverage.xml"/>
</report>
</coverage>
<testsuites>
<testsuite name="Project Test Suite">
<file>tests/src/Command/ClearModelsCommandTest.php</file>
<file>tests/src/Command/GenerateModelsCommandTest.php</file>
<file>tests/src/Command/VerifyModelsCommandTest.php</file>
<directory>tests/src</directory>
</testsuite>
</testsuites>
<logging>
<junit outputFile="test-report.xml"/>
</logging>
<php>
<ini name="memory_limit" value="4G" />
</php>
</phpunit>

View File

@ -0,0 +1,492 @@
<?php
/**
* PHP version 7.3
*
* @category ClientBuilder
* @package RetailCrm\Api\Builder
*/
namespace RetailCrm\Api\Builder;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Log\LoggerInterface;
use RetailCrm\Api\Client;
use RetailCrm\Api\Component\Transformer\RequestTransformer;
use RetailCrm\Api\Component\Transformer\ResponseTransformer;
use RetailCrm\Api\Exception\Client\BuilderException;
use RetailCrm\Api\Factory\ApiExceptionFactory;
use RetailCrm\Api\Factory\RequestPipelineFactory;
use RetailCrm\Api\Factory\ResponsePipelineFactory;
use RetailCrm\Api\Interfaces\ApiExceptionFactoryAwareInterface;
use RetailCrm\Api\Interfaces\BuilderInterface;
use RetailCrm\Api\Interfaces\EventDispatcherAwareInterface;
use RetailCrm\Api\Interfaces\FormEncoderInterface;
use RetailCrm\Api\Interfaces\HandlerInterface;
use RetailCrm\Api\Interfaces\PsrFactoriesAwareInterface;
use RetailCrm\Api\Interfaces\RequestTransformerInterface;
use RetailCrm\Api\Interfaces\ResponseTransformerInterface;
use RetailCrm\Api\Interfaces\SerializerAwareInterface;
use RetailCrm\Api\Traits\EventDispatcherAwareTrait;
/**
* Class ClientBuilder
*
* @category ClientBuilder
* @package RetailCrm\Api\Builder
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
*/
class ClientBuilder implements BuilderInterface, EventDispatcherAwareInterface
{
use EventDispatcherAwareTrait;
/** @var string */
private $apiUrl;
/** @var HandlerInterface|null */
private $authenticator;
/** @var ClientInterface|null */
private $httpClient;
/** @var \Psr\Log\LoggerInterface|null */
private $debugLogger;
/** @var RequestTransformerInterface|null */
private $requestTransformer;
/** @var \RetailCrm\Api\Interfaces\ResponseTransformerInterface|null */
protected $responseTransformer;
/** @var \RetailCrm\Api\Interfaces\FormEncoderInterface|null */
private $formEncoder;
/** @var \Psr\Http\Message\StreamFactoryInterface|null */
private $streamFactory;
/** @var \Psr\Http\Message\RequestFactoryInterface|null */
private $requestFactory;
/** @var \Psr\Http\Message\UriFactoryInterface|null */
private $uriFactory;
/** @var \RetailCrm\Api\Factory\ApiExceptionFactory|null */
private $apiExceptionFactory;
/** @var \RetailCrm\Api\Interfaces\HandlerInterface[] */
private $requestHandlers = [];
/** @var \RetailCrm\Api\Interfaces\HandlerInterface[] */
private $responseHandlers = [];
/**
* API URL. Looks like this: "https://test.retailcrm.pro/"
*
* @param string $apiUrl
*
* @return ClientBuilder
*/
public function setApiUrl(string $apiUrl): ClientBuilder
{
$this->apiUrl = $apiUrl;
return $this;
}
/**
* Request authenticator to append into request transformer pipeline.
*
* Do not use it if you already added a proper authenticator in the pipeline manually.
* You can use this method to drop authenticator from client builder (use null).
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface|null $authenticator
*
* @return ClientBuilder
*/
public function setAuthenticatorHandler(?HandlerInterface $authenticator): ClientBuilder
{
$this->authenticator = $authenticator;
return $this;
}
/**
* Set your PSR-18 HTTP client.
*
* Service discovery will be used if no client has been provided.
*
* @param \Psr\Http\Client\ClientInterface|null $httpClient
*
* @return ClientBuilder
*/
public function setHttpClient(?ClientInterface $httpClient): ClientBuilder
{
$this->httpClient = $httpClient;
return $this;
}
/**
* Set debug logger.
*
* The provided logger will be used to record all requests and responses.
* This feature consumes a lot of resources and shouldn't be used in production.
*
* @param \Psr\Log\LoggerInterface|null $debugLogger
*
* @return ClientBuilder
*/
public function setDebugLogger(?LoggerInterface $debugLogger): ClientBuilder
{
$this->debugLogger = $debugLogger;
return $this;
}
/**
* Set request transformer into API client.
*
* You can use this method to set your request transformer which will execute the pipeline.
* The default request transformer doesn't do anything besides calling provided chain of handlers.
*
* @param \RetailCrm\Api\Interfaces\RequestTransformerInterface|null $requestTransformer
*
* @return ClientBuilder
*/
public function setRequestTransformer(?RequestTransformerInterface $requestTransformer): ClientBuilder
{
$this->requestTransformer = $requestTransformer;
return $this;
}
/**
* Set response transformer into API client.
*
* You can use this method to set your response transformer which will execute the pipeline.
* The default response transformer doesn't do anything besides calling provided chain of handlers.
* The serializer instance for the request pipeline can be inferred from the provided FormEncoder instance.
*
* @param \RetailCrm\Api\Interfaces\ResponseTransformerInterface|null $responseTransformer
*
* @return ClientBuilder
*/
public function setResponseTransformer(?ResponseTransformerInterface $responseTransformer): ClientBuilder
{
$this->responseTransformer = $responseTransformer;
return $this;
}
/**
* Set form encoder into API client.
*
* Form encoder is a vital part of the API client. Its purpose is to transform provided request models
* into form-data. The result will be used as a query or POST body (depends on request type).
*
* @param \RetailCrm\Api\Interfaces\FormEncoderInterface $formEncoder
*
* @return ClientBuilder
*/
public function setFormEncoder(FormEncoderInterface $formEncoder): ClientBuilder
{
$this->formEncoder = $formEncoder;
return $this;
}
/**
* Sets PSR-17 compatible stream factory. You can skip this step if you want to use service discovery.
*
* @param \Psr\Http\Message\StreamFactoryInterface|null $streamFactory
*
* @return ClientBuilder
*/
public function setStreamFactory(?StreamFactoryInterface $streamFactory): ClientBuilder
{
$this->streamFactory = $streamFactory;
return $this;
}
/**
* Sets PSR-17 compatible request factory. You can skip this step if you want to use service discovery.
*
* @param \Psr\Http\Message\RequestFactoryInterface|null $requestFactory
*
* @return ClientBuilder
*/
public function setRequestFactory(?RequestFactoryInterface $requestFactory): ClientBuilder
{
$this->requestFactory = $requestFactory;
return $this;
}
/**
* Sets PSR-17 compatible URI factory. You can skip this step if you want to use service discovery.
*
* @param \Psr\Http\Message\UriFactoryInterface|null $uriFactory
*
* @return ClientBuilder
*/
public function setUriFactory(?UriFactoryInterface $uriFactory): ClientBuilder
{
$this->uriFactory = $uriFactory;
return $this;
}
/**
* Appends an additional request handler into the request processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface $handler
*
* @return ClientBuilder
*/
public function appendRequestHandler(HandlerInterface $handler): ClientBuilder
{
$this->requestHandlers[] = $handler;
return $this;
}
/**
* Appends an additional response handler into the response processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface $handler
*
* @return ClientBuilder
*/
public function appendResponseHandler(HandlerInterface $handler): ClientBuilder
{
$this->responseHandlers[] = $handler;
return $this;
}
/**
* Appends additional request handlers into the request processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface[] $handlers
*
* @return ClientBuilder
*/
public function appendRequestHandlers(array $handlers): ClientBuilder
{
foreach ($handlers as $handler) {
$this->appendRequestHandler($handler);
}
return $this;
}
/**
* Appends additional response handlers into the response processing chain.
*
* @param \RetailCrm\Api\Interfaces\HandlerInterface[] $handlers
*
* @return ClientBuilder
*/
public function appendResponseHandlers(array $handlers): ClientBuilder
{
foreach ($handlers as $handler) {
$this->appendResponseHandler($handler);
}
return $this;
}
/**
* Builds client with provided dependencies.
*
* @inheritDoc
*/
public function build(): Client
{
$this->validateBuilder();
if (
null !== $this->authenticator &&
null !== $this->requestTransformer &&
null !== $this->requestTransformer->getHandler()
) {
$this->requestTransformer->getHandler()->append($this->authenticator);
}
if (null === $this->requestTransformer) {
$this->requestTransformer = $this->buildRequestTransformer();
}
if (null === $this->responseTransformer) {
$this->responseTransformer = $this->buildResponseTransformer();
}
$this->appendAdditionalRequestHandlers();
$this->appendAdditionalResponseHandlers();
return new Client(
$this->apiUrl,
$this->httpClient ?: Psr18ClientDiscovery::find(),
$this->requestTransformer, // @phpstan-ignore-line
$this->responseTransformer, // @phpstan-ignore-line
$this->streamFactory ?: Psr17FactoryDiscovery::findStreamFactory(),
$this->eventDispatcher,
$this->debugLogger
);
}
/**
* Check if builder is ready to build a Client instance.
*
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private function validateBuilder(): void
{
if (empty($this->apiUrl)) {
throw new BuilderException('apiUrl must not be empty', ['apiUrl']);
}
if (empty($this->authenticator) && empty($this->requestTransformer)) {
throw new BuilderException(
'Authenticator or RequestTransformer must be present',
['authenticator', 'requestTransformer']
);
}
}
/**
* Appends additional request handlers into the request and response processor chain (if needed).
*
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private function appendAdditionalRequestHandlers(): void
{
if (
null !== $this->requestTransformer &&
null !== $this->requestTransformer->getHandler() &&
count($this->requestHandlers) > 0
) {
foreach ($this->requestHandlers as $handler) {
if ($handler instanceof PsrFactoriesAwareInterface) {
$handler->setRequestFactory($this->requestFactory ?: Psr17FactoryDiscovery::findRequestFactory());
$handler->setStreamFactory($this->streamFactory ?: Psr17FactoryDiscovery::findStreamFactory());
$handler->setUriFactory($this->uriFactory ?: Psr17FactoryDiscovery::findUriFactory());
}
}
$this->requestTransformer->getHandler()->append(static::buildHandlersChain($this->requestHandlers));
}
}
/**
* Appends additional response handlers into the request and response processor chain (if needed).
*
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
private function appendAdditionalResponseHandlers(): void
{
if (
null !== $this->responseTransformer &&
null !== $this->responseTransformer->getHandler() &&
count($this->responseHandlers) > 0
) {
foreach ($this->responseHandlers as $handler) {
if ($handler instanceof SerializerAwareInterface && null !== $this->formEncoder) {
$handler->setSerializer($this->formEncoder->getSerializer());
}
if ($handler instanceof ApiExceptionFactoryAwareInterface && null !== $this->apiExceptionFactory) {
$handler->setApiExceptionFactory($this->apiExceptionFactory);
}
if ($handler instanceof EventDispatcherAwareInterface) {
$handler->setEventDispatcher($this->eventDispatcher);
}
}
$this->responseTransformer->getHandler()->append(static::buildHandlersChain($this->responseHandlers));
}
}
/**
* Builds RequestTransformer with default pipeline and authenticator.
*
* @return \RetailCrm\Api\Component\Transformer\RequestTransformer
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private function buildRequestTransformer(): RequestTransformer
{
if (null === $this->formEncoder) {
throw new BuilderException(
"You must provide a FormEncoder instance in order to delegate " .
"RequestTransformer instantiation to the ClientBuilder."
);
}
if (null === $this->authenticator) {
throw new BuilderException(
"You must provide an authenticator handler instance in order to delegate " .
"RequestTransformer instantiation to the ClientBuilder."
);
}
return new RequestTransformer(
RequestPipelineFactory::createDefaultPipeline(
$this->formEncoder,
$this->uriFactory ?: Psr17FactoryDiscovery::findUriFactory(),
$this->requestFactory ?: Psr17FactoryDiscovery::findRequestFactory(),
$this->streamFactory ?: Psr17FactoryDiscovery::findStreamFactory(),
$this->authenticator
)
);
}
/**
* Builds ResponseTransformer.
*
* @return \RetailCrm\Api\Component\Transformer\ResponseTransformer
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private function buildResponseTransformer(): ResponseTransformer
{
if (null === $this->formEncoder) {
throw new BuilderException(
"You must provide a FormEncoder instance in order to delegate " .
"ResponseTransformer instantiation to the ClientBuilder."
);
}
if (null === $this->apiExceptionFactory) {
$this->apiExceptionFactory = new ApiExceptionFactory();
}
return new ResponseTransformer(ResponsePipelineFactory::createDefaultPipeline(
$this->formEncoder->getSerializer(),
$this->apiExceptionFactory,
$this->eventDispatcher
));
}
/**
* Connect all handlers in the array into chain, return first handler.
*
* @param HandlerInterface[] $handlers
*
* @return \RetailCrm\Api\Interfaces\HandlerInterface
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private static function buildHandlersChain(array $handlers): HandlerInterface
{
if (empty($handlers)) {
throw new BuilderException('Supplied handlers chain must contain at least one handler');
}
if (1 === count($handlers)) {
return $handlers[0];
}
for ($i = 0, $iMax = count($handlers) - 1; $i < $iMax; $i++) {
$handlers[$i]->setNext($handlers[$i + 1]);
}
return $handlers[0];
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* PHP version 7.3
*
* @category FilesystemCacheBuilder
* @package RetailCrm\Api\Builder
*/
namespace RetailCrm\Api\Builder;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use RetailCrm\Api\Enum\CacheDirectories;
use RetailCrm\Api\Exception\Client\BuilderException;
use RetailCrm\Api\Interfaces\BuilderInterface;
/**
* Class FilesystemCacheBuilder
*
* @category FilesystemCacheBuilder
* @package RetailCrm\Api\Builder
*/
class FilesystemCacheBuilder implements BuilderInterface
{
/** @var string */
private $cacheDir;
/**
* @param string $cacheDir
*
* @return FilesystemCacheBuilder
*/
public function setCacheDir(string $cacheDir): FilesystemCacheBuilder
{
$this->cacheDir = $cacheDir;
return $this;
}
/**
* Builds and returns filesystem cache.
*
* @return CacheItemPoolInterface
* @inheritDoc
*/
public function build(): CacheItemPoolInterface
{
if (empty($this->cacheDir)) {
return new FilesystemAdapter(CacheDirectories::DIR_NAME);
}
$cacheDir = static::getCacheDirPath($this->cacheDir);
$this->createDir($cacheDir);
return new FilesystemAdapter('', 0, $cacheDir);
}
/**
* @param string $dir
*
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private function createDir(string $dir): void
{
if (is_dir($dir)) {
return;
}
if (false === mkdir($dir, 0777, true) && false === is_dir($dir)) {
throw new BuilderException(sprintf('Could not create directory "%s".', $dir));
}
}
/**
* Returns target cache dir for cache.
*
* @param string $cacheDir
*
* @return string
*/
private static function getCacheDirPath(string $cacheDir): string
{
if ('' !== $cacheDir && DIRECTORY_SEPARATOR !== $cacheDir[strlen($cacheDir) - 1]) {
$cacheDir .= DIRECTORY_SEPARATOR;
}
return $cacheDir . CacheDirectories::MAIN_DIR . DIRECTORY_SEPARATOR;
}
}

View File

@ -0,0 +1,120 @@
<?php
/**
* PHP version 7.3
*
* @category FormEncoderBuilder
* @package RetailCrm\Api\Builder
*/
namespace RetailCrm\Api\Builder;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\PsrCachedReader;
use Psr\Cache\CacheItemPoolInterface;
use RetailCrm\Api\Component\FormData\FormEncoder;
use RetailCrm\Api\Factory\SerializerFactory;
use RetailCrm\Api\Interfaces\BuilderInterface;
use RetailCrm\Api\Interfaces\FormEncoderInterface;
/**
* Class FormEncoderBuilder
*
* @category FormEncoderBuilder
* @package RetailCrm\Api\Builder
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class FormEncoderBuilder implements BuilderInterface
{
/** @var CacheItemPoolInterface|null */
private $cache;
/** @var \Doctrine\Common\Annotations\Reader */
private $annotationReader;
/** @var \RetailCrm\Api\Builder\FilesystemCacheBuilder */
private $fsCacheBuilder;
/**
* FormEncoderBuilder constructor.
*/
public function __construct()
{
$this->fsCacheBuilder = new FilesystemCacheBuilder();
}
/**
* Sets cache directory.
*
* This directory will be used by FormEncoder component and underlying serializer to store annotations cache.
* Annotations parsing consumes a lot of resources, which is the reason why you should cache results.
*
* @param string $cacheDir
*
* @return FormEncoderBuilder
*/
public function setCacheDir(string $cacheDir): FormEncoderBuilder
{
$this->fsCacheBuilder->setCacheDir($cacheDir);
return $this;
}
/**
* Sets cache implementation.
*
* This cache implementation will be used by FormEncoder component and underlying serializer to store
* annotations cache. Any cache from `symfony/cache` should work just fine.
*
* @param \Psr\Cache\CacheItemPoolInterface $cache
*
* @return FormEncoderBuilder
*/
public function setCache(CacheItemPoolInterface $cache): FormEncoderBuilder
{
$this->cache = $cache;
return $this;
}
/**
* Builds FormEncoder.
*
* **Note:** Cache won't be set into provided serializer instance. It only works for instance built by
* this builder. You must manually inject the proper cache into the custom JMS Serializer instance.
*
* @inheritDoc
*/
public function build(): FormEncoderInterface
{
$this->buildCache();
$this->buildAnnotationReader();
$serializer = SerializerFactory::create();
return new FormEncoder($serializer, $this->annotationReader);
}
/**
* Builds cache if needed.
*
* @throws \RetailCrm\Api\Exception\Client\BuilderException
*/
private function buildCache(): void
{
if (null === $this->cache) {
$this->cache = $this->fsCacheBuilder->build();
}
}
/**
* Builds annotation reader.
*/
private function buildAnnotationReader(): void
{
$this->annotationReader = new AnnotationReader();
if (null !== $this->cache) {
$this->annotationReader = new PsrCachedReader(new AnnotationReader(), $this->cache);
}
}
}

343
src/Client.php Normal file
View File

@ -0,0 +1,343 @@
<?php
/**
* PHP version 7.3
*
* @category Client
* @package RetailCrm\Api
*/
namespace RetailCrm\Api;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Log\LoggerInterface;
use RetailCrm\Api\Component\Utils;
use RetailCrm\Api\Interfaces\RequestTransformerInterface;
use RetailCrm\Api\Interfaces\ResponseTransformerInterface;
use RetailCrm\Api\ResourceGroup\Api;
use RetailCrm\Api\ResourceGroup\Costs;
use RetailCrm\Api\ResourceGroup\Customers;
use RetailCrm\Api\ResourceGroup\CustomersCorporate;
use RetailCrm\Api\ResourceGroup\CustomFields;
use RetailCrm\Api\ResourceGroup\Delivery;
use RetailCrm\Api\ResourceGroup\Files;
use RetailCrm\Api\ResourceGroup\Integration;
use RetailCrm\Api\ResourceGroup\Inventories;
use RetailCrm\Api\ResourceGroup\Loyalty;
use RetailCrm\Api\ResourceGroup\Orders;
use RetailCrm\Api\ResourceGroup\Packs;
use RetailCrm\Api\ResourceGroup\Payments;
use RetailCrm\Api\ResourceGroup\References;
use RetailCrm\Api\ResourceGroup\Segments;
use RetailCrm\Api\ResourceGroup\Settings;
use RetailCrm\Api\ResourceGroup\Statistics;
use RetailCrm\Api\ResourceGroup\Store;
use RetailCrm\Api\ResourceGroup\Tasks;
use RetailCrm\Api\ResourceGroup\Telephony;
use RetailCrm\Api\ResourceGroup\Users;
use RetailCrm\Api\ResourceGroup\Verification;
/**
* Class Client
*
* Do not instantiate API client directly! Use `ClientFactory`, `SimpleClientFactory` or `ClientBuilder`.
*
* @category Client
* @package RetailCrm\Api
* @see \RetailCrm\Api\Factory\ClientFactory
* @see \RetailCrm\Api\Factory\SimpleClientFactory
* @see \RetailCrm\Api\Builder\ClientBuilder
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Client
{
/** @var \RetailCrm\Api\ResourceGroup\Api */
public $api;
/** @var \RetailCrm\Api\ResourceGroup\Costs */
public $costs;
/** @var \RetailCrm\Api\ResourceGroup\CustomFields */
public $customFields;
/** @var \RetailCrm\Api\ResourceGroup\Customers */
public $customers;
/** @var \RetailCrm\Api\ResourceGroup\CustomersCorporate */
public $customersCorporate;
/** @var \RetailCrm\Api\ResourceGroup\Delivery */
public $delivery;
/** @var \RetailCrm\Api\ResourceGroup\Files */
public $files;
/** @var \RetailCrm\Api\ResourceGroup\Integration */
public $integration;
/** @var \RetailCrm\Api\ResourceGroup\Loyalty */
public $loyalty;
/** @var \RetailCrm\Api\ResourceGroup\Orders */
public $orders;
/** @var \RetailCrm\Api\ResourceGroup\Packs */
public $packs;
/** @var \RetailCrm\Api\ResourceGroup\Payments */
public $payments;
/** @var \RetailCrm\Api\ResourceGroup\References */
public $references;
/** @var \RetailCrm\Api\ResourceGroup\Segments */
public $segments;
/** @var \RetailCrm\Api\ResourceGroup\Settings */
public $settings;
/** @var \RetailCrm\Api\ResourceGroup\Store */
public $store;
/** @var \RetailCrm\Api\ResourceGroup\Tasks */
public $tasks;
/** @var \RetailCrm\Api\ResourceGroup\Telephony */
public $telephony;
/** @var \RetailCrm\Api\ResourceGroup\Users */
public $users;
/** @var \RetailCrm\Api\ResourceGroup\Verification */
public $verification;
/** @var \RetailCrm\Api\ResourceGroup\Statistics */
public $statistics;
/** @var StreamFactoryInterface */
private $streamFactory;
/**
* Client constructor.
*
* @param string $apiUrl
* @param \Psr\Http\Client\ClientInterface $httpClient
* @param \RetailCrm\Api\Interfaces\RequestTransformerInterface $requestTransformer
* @param \RetailCrm\Api\Interfaces\ResponseTransformerInterface $responseTransformer
* @param \Psr\Http\Message\StreamFactoryInterface $streamFactory
* @param \Psr\EventDispatcher\EventDispatcherInterface|null $eventDispatcher
* @param \Psr\Log\LoggerInterface|null $logger
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @todo Maybe initialize children groups using different method?
*/
public function __construct(
string $apiUrl,
ClientInterface $httpClient,
RequestTransformerInterface $requestTransformer,
ResponseTransformerInterface $responseTransformer,
StreamFactoryInterface $streamFactory,
?EventDispatcherInterface $eventDispatcher = null,
?LoggerInterface $logger = null
) {
$url = static::getBaseUrl($apiUrl);
$this->streamFactory = $streamFactory;
$this->api = new Api(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->costs = new Costs(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->customFields = new CustomFields(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->customers = new Customers(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->customersCorporate = new CustomersCorporate(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->delivery = new Delivery(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->files = new Files(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->integration = new Integration(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->loyalty = new Loyalty(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->orders = new Orders(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->packs = new Packs(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->payments = new Payments(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->references = new References(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->segments = new Segments(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->settings = new Settings(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->store = new Store(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->tasks = new Tasks(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->telephony = new Telephony(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->users = new Users(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->verification = new Verification(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
$this->statistics = new Statistics(
$url,
$httpClient,
$requestTransformer,
$responseTransformer,
$eventDispatcher,
$logger
);
}
/**
* Returns PSR-17 stream factory.
*
* StreamFactory can be used to create a PSR-7 StreamInterface from various sources.
*
* @return \Psr\Http\Message\StreamFactoryInterface
*/
public function getStreamFactory(): StreamFactoryInterface
{
return $this->streamFactory;
}
/**
* Parses provided URL, builds API url with version out of it.
*
* @param string $url
*
* @return string
*/
private static function getBaseUrl(string $url): string
{
return Utils::removeTrailingSlash($url) . '/api/v5';
}
}

View File

@ -0,0 +1,54 @@
<?php
/**
* PHP version 7.3
*
* @category AbstractModelsProcessorCommand
* @package RetailCrm\Api\Command
*/
namespace RetailCrm\Api\Command;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class AbstractModelsProcessorCommand
*
* @category AbstractModelsProcessorCommand
* @package RetailCrm\Api\Command
* @internal
*/
abstract class AbstractModelsProcessorCommand extends Command
{
/**
* Returns true if "-v" was provided.
*
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return bool
*/
protected static function isVerbose(OutputInterface $output): bool
{
return $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE;
}
/**
* Create directory
*
* @param string $dir
*
* @throws RuntimeException
*/
protected static function createDir(string $dir): void
{
if (is_dir($dir)) {
return;
}
if (false === mkdir($dir, 0777, true) && false === is_dir($dir)) {
throw new RuntimeException(sprintf('Could not create directory "%s".', $dir));
}
}
}

View File

@ -0,0 +1,86 @@
<?php
/**
* PHP version 7.3
*
* @category ClearModelsCommand
* @package RetailCrm\Api\Command
*/
namespace RetailCrm\Api\Command;
use RetailCrm\Api\Component\Utils;
use RetailCrm\Api\Component\PhpFilesIterator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class ClearModelsCommand
*
* @category ClearModelsCommand
* @package RetailCrm\Api\Command
* @internal
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
class ClearModelsCommand extends AbstractModelsProcessorCommand
{
/**
* Sets description and help for a command.
*/
protected function configure(): void
{
$this->setName('models:clear')
->setDescription('Removes all generated models, leaves only empty directory behind.')
->setHelp('Use this command if you want to remove existing model cache.');
}
/**
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$target = Utils::getModelsCacheDirectory();
if (!is_dir($target)) {
$output->writeln('<error>The target directory does not exist.</error>');
$output->writeln('<info>You can safely use "bin/console models:generate" to generate models.</info>');
return -1;
}
$output->writeln(sprintf('Cleaning up <fg=magenta>"%s"</>...', $target));
if (self::isVerbose($output)) {
$output->writeln('');
}
$checksumFile = implode(DIRECTORY_SEPARATOR, [$target, 'checksum.json']);
$models = new PhpFilesIterator($target);
foreach ($models as $model) {
if (file_exists($model['file'])) {
unlink($model['file']);
if (self::isVerbose($output)) {
$output->writeln(sprintf('- Removed <fg=magenta>"%s"</>', $model['file']));
}
}
}
if (file_exists($checksumFile)) {
unlink($checksumFile);
}
if (self::isVerbose($output)) {
$output->writeln('');
}
$output->writeln('<fg=black;bg=green> ✓ Done!</>');
return 0;
}
}

View File

@ -0,0 +1,171 @@
<?php
/**
* PHP version 7.3
*
* @category CompilerPromptCommand
* @package RetailCrm\Api\Command
*/
namespace RetailCrm\Api\Command;
use JsonException;
use RetailCrm\Api\Component\ComposerLocator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CompilerPromptCommand
*
* @category CompilerPromptCommand
* @package RetailCrm\Api\Command
*/
class CompilerPromptCommand extends Command
{
private const PACKAGE_NAME = 'retailcrm/api-client-php';
/**
* Sets description and help for a command.
*/
protected function configure(): void
{
$this->setName('compiler:prompt')
->setDescription('Enable or disable composer compiler prompt.')
->setHelp('Use this command to suppress the compiler message and enable automatic compilation.')
->addOption(
'deactivate',
'd',
InputOption::VALUE_OPTIONAL,
'Hide compiler prompt and run compiler task automatically. This mode is used by default.',
false
)
->addOption(
'activate',
'a',
InputOption::VALUE_OPTIONAL,
'Show compiler prompt and only run compiler task if user allows it.',
false
);
}
/**
* Execute the command.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$composerJson = ComposerLocator::findComposerJson();
if ('' === $composerJson) {
$output->writeln('<fg=black;bg=red> ❌ Cannot find composer.json</>');
return -1;
}
try {
$json = json_decode((string) file_get_contents($composerJson), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
$output->writeln(sprintf('<fg=black;bg=red> ❌ Invalid JSON: %s</>', $exception->getMessage()));
return -1;
}
$activatePrompt = false !== $input->getOption('activate');
if ($activatePrompt) {
static::activatePrompt($json);
} else {
static::deactivatePrompt($json);
}
try {
$result = json_encode($json, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
} catch (JsonException $exception) {
$output->writeln(sprintf('<fg=black;bg=red> ❌ Cannot encode JSON: %s</>', $exception->getMessage()));
return -1;
}
if (false === file_put_contents($composerJson, $result)) {
$output->writeln('<fg=black;bg=red> ❌ Cannot write to file.</>');
return -1;
}
$output->writeln(sprintf(
'<fg=black;bg=green> ✓ Done, generator prompt is now %s.</>',
$activatePrompt ? 'enabled' : 'disabled'
));
return 0;
}
/**
* Activate prompt in the provided composer.json
*
* @param array<string, mixed> $composerJson
*/
private static function activatePrompt(array &$composerJson): void
{
if (!array_key_exists('extra', $composerJson)) {
$composerJson['extra'] = [];
}
if (
array_key_exists('compile-whitelist', $composerJson['extra']) &&
is_array($composerJson['extra']['compile-whitelist']) &&
in_array(static::PACKAGE_NAME, $composerJson['extra']['compile-whitelist'], true)
) {
$composerJson['extra']['compile-whitelist'] = array_filter(
$composerJson['extra']['compile-whitelist'],
static function ($value) {
return static::PACKAGE_NAME !== $value;
}
);
}
if (
empty($composerJson['extra']['compile-whitelist']) &&
array_key_exists('compile-mode', $composerJson['extra']) &&
'whitelist' === $composerJson['extra']['compile-mode']
) {
unset($composerJson['extra']['compile-whitelist'], $composerJson['extra']['compile-mode']);
}
if (1 === count($composerJson['extra'])) {
unset($composerJson['extra']);
}
}
/**
* Deactivate prompt in the provided composer.json
*
* @param array<string, mixed> $composerJson
*
* @SuppressWarnings(PHPMD.ElseExpression)
*/
private static function deactivatePrompt(array &$composerJson): void
{
if (!array_key_exists('extra', $composerJson)) {
$composerJson['extra'] = [];
}
if (
array_key_exists('compile-whitelist', $composerJson['extra']) &&
is_array($composerJson['extra']['compile-whitelist']) &&
!in_array(static::PACKAGE_NAME, $composerJson['extra']['compile-whitelist'], true)
) {
$composerJson['extra']['compile-whitelist'][] = static::PACKAGE_NAME;
} else {
$composerJson['extra']['compile-whitelist'] = [static::PACKAGE_NAME];
}
$composerJson['extra']['compile-mode'] = 'whitelist';
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* PHP version 7.3
*
* @category GenerateModelsCommand
* @package RetailCrm\Api\Command
*/
namespace RetailCrm\Api\Command;
use RetailCrm\Api\Component\ModelsGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class GenerateModelsCommand
*
* @category GenerateModelsCommand
* @package RetailCrm\Api\Command
* @internal
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
* @see There is no need to refactor generator into separate service. Its logic won't be used anywhere else.
*/
class GenerateModelsCommand extends AbstractModelsProcessorCommand
{
/**
* Sets description and help for a command.
*/
protected function configure(): void
{
$this->setName('models:generate')
->setDescription('Converts all JMS models to static (de)serialization code.')
->setHelp('Use this command after making any changes to the models.')
->addOption(
'all',
'a',
InputOption::VALUE_OPTIONAL,
'Generate cache for all models instead of changed models only.',
false
);
}
/**
* Execute the command.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$verbose = static::isVerbose($output);
$generator = new ModelsGenerator(false !== $input->getOption('all'));
$output->writeln('Preparing a list of models to generate cache files...');
$output->writeln(
'<options=bold>Note:</> Request models will be omitted ' .
'because they are being handled by FormEncoder.'
);
$output->writeln('');
$generator->loadModelsList();
if ($verbose) {
foreach ($generator->getModels() as $model) {
$output->writeln(sprintf('- Added <fg=magenta>%s</>', $model));
}
}
if ($verbose && count($generator->getModels()) > 0) {
$output->writeln('');
}
if (count($generator->getModels()) === 0) {
$output->writeln('<info>No changes were found; skipping generation...</info>');
$output->writeln('');
}
$generator->generate();
$output->writeln(sprintf(
'<fg=black;bg=green> ✓ Done, generated code for %d models.</>',
count($generator->getModels())
));
return 0;
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* PHP version 7.3
*
* @category VerifyModelsCommand
* @package RetailCrm\Api\Command
*/
namespace RetailCrm\Api\Command;
use RetailCrm\Api\Component\Serializer\ModelsChecksumGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Class VerifyModelsCommand
*
* @category VerifyModelsCommand
* @package RetailCrm\Api\Command
* @internal
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
class VerifyModelsCommand extends AbstractModelsProcessorCommand
{
/**
* Sets description and help for a command.
*/
protected function configure(): void
{
$this->setName('models:verify')
->setDescription('Verify models cache. This command will fail if model cache is not up-to-date.')
->setHelp('Use this command if you want to check existing model cache.');
}
/**
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return int
* @throws \JsonException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
if (ModelsChecksumGenerator::verifyChecksum()) {
$io->success("Models are up to date.");
return 0;
}
$io->error("Outdated models! Run \"models:generate\" command to fix that.");
return -1;
}
}

View File

@ -0,0 +1,83 @@
<?php
/**
* PHP version 7.3
*
* @category ComposerLocator
* @package RetailCrm\Api\Component
*/
namespace RetailCrm\Api\Component;
/**
* Class ComposerLocator
*
* @category ComposerLocator
* @package RetailCrm\Api\Component
*/
class ComposerLocator
{
/**
* Locate Composer autoloader.
*
* @return string
*/
public static function findAutoloader(): string
{
$counter = 0;
$dir = static::getBaseDirectory();
for (;;) {
if (file_exists($dir . '/autoload.php')) {
return $dir . '/autoload.php';
}
if (file_exists($dir . '/vendor/autoload.php')) {
return $dir . '/vendor/autoload.php';
}
$counter++;
$dir = dirname($dir);
if (5 < $counter) {
break;
}
}
return '';
}
/**
* Locate `composer.json`.
*
* @return string
*/
public static function findComposerJson(): string
{
$counter = 0;
$dir = static::getBaseDirectory();
for (;;) {
if (file_exists($dir . '/composer.json')) {
return $dir . '/composer.json';
}
$counter++;
$dir = dirname($dir);
if (5 < $counter) {
break;
}
}
return '';
}
/**
* @return string
*/
private static function getBaseDirectory(): string
{
return (string) realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..']));
}
}

View File

@ -0,0 +1,130 @@
<?php
/**
* PHP version 7.3
*
* @category FilesIteratorChecksumGenerator
* @package RetailCrm\Api\Component
*/
namespace RetailCrm\Api\Component;
use Iterator;
/**
* Class FilesIteratorChecksumGenerator
*
* @category FilesIteratorChecksumGenerator
* @package RetailCrm\Api\Component
* @internal
*/
class FilesIteratorChecksumGenerator
{
/** @var Iterator<mixed> */
private $iterator;
/** @var callable */
private $fileNameAccessor;
/** @var callable */
private $keyTransformer;
/** @var callable */
private $hashFunc;
/**
* FilesIteratorChecksumGenerator constructor.
*
* @param Iterator<mixed> $iterator
*/
public function __construct(Iterator $iterator)
{
$this->iterator = $iterator;
}
/**
* @return callable
*/
public function getFileNameAccessor(): callable
{
return $this->fileNameAccessor ?? static function ($fileName) {
return (string) $fileName;
};
}
/**
* @param callable $fileNameAccessor
*
* @return FilesIteratorChecksumGenerator
*/
public function setFileNameAccessor(callable $fileNameAccessor): FilesIteratorChecksumGenerator
{
$this->fileNameAccessor = $fileNameAccessor;
return $this;
}
/**
* @return callable
*/
public function getKeyTransformer(): callable
{
return $this->keyTransformer ?? static function ($fileName) {
return (string) $fileName;
};
}
/**
* This callable will be used to provide key for the hashes array.
*
* @param callable $keyTransformer
*
* @return FilesIteratorChecksumGenerator
*/
public function setKeyTransformer(callable $keyTransformer): FilesIteratorChecksumGenerator
{
$this->keyTransformer = $keyTransformer;
return $this;
}
/**
* @return callable
*/
public function getHashFunc(): callable
{
return $this->hashFunc ?? static function ($fileName) {
$contents = preg_replace('/\r|\n|\r\n/m', "\n", (string) file_get_contents($fileName));
return md5($contents ?? '');
};
}
/**
* This function will receive file name and should return file hash.
*
* @param callable $hashFunc
*
* @return FilesIteratorChecksumGenerator
*/
public function setHashFunc(callable $hashFunc): FilesIteratorChecksumGenerator
{
$this->hashFunc = $hashFunc;
return $this;
}
/**
* Generate hash string from the contents of a directory.
*
* @return array<string, string>
*/
public function generateHashes(): array
{
$hashes = [];
foreach ($this->iterator as $item) {
$fileName = $this->getFileNameAccessor()($item);
$hashes[(string) $this->getKeyTransformer()($item)] = $this->getHashFunc()($fileName);
}
return $hashes;
}
}

View File

@ -0,0 +1,131 @@
<?php
/**
* PHP version 7.3
*
* @category FormEncoder
* @package RetailCrm\Api\Component\FormData
*/
namespace RetailCrm\Api\Component\FormData;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\Reader;
use Liip\Serializer\SerializerInterface;
use ReflectionClass;
use ReflectionException;
use RetailCrm\Api\Component\FormData\Mapping\PostSerialize;
use RetailCrm\Api\Component\FormData\Strategy\StrategyFactory;
use RetailCrm\Api\Interfaces\FormEncoderInterface;
/**
* Class FormEncoder
*
* FormEncoder is a vital part of the library. Our API expects form-data for all requests, but some fields may contain
* JSON data (for example, `/api/v5/customers/create` method works like that). FormEncoder is our custom serializer that
* converts request instances to form-data and uses Liip serializer under the hood to fill some fields with JSON data.
*
* @see https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5#post--api-v5-customers-create
*
* @category FormEncoder
* @package RetailCrm\Api\Component\FormData
*/
class FormEncoder implements FormEncoderInterface
{
/** @var \Doctrine\Common\Annotations\Reader */
private $annotationReader;
/** @var \Liip\Serializer\SerializerInterface */
private $serializer;
/**
* FormEncoder constructor.
*
* @param \Liip\Serializer\SerializerInterface $serializer
* @param \Doctrine\Common\Annotations\Reader|null $annotationReader
*/
public function __construct(SerializerInterface $serializer, ?Reader $annotationReader = null)
{
$this->serializer = $serializer;
$this->annotationReader = $annotationReader ?: new AnnotationReader();
}
/**
* Encodes provided object into a form data
*
* @param mixed $object
* @param string $type
*
* @return string
* @throws \ReflectionException
*/
public function encode($object, string $type = ''): string
{
return http_build_query($this->encodeArray($object, $type));
}
/**
* Encodes provided object into an array
*
* @param mixed $object
* @param string $type
*
* @return array<mixed>
* @throws \ReflectionException
*/
public function encodeArray($object, string $type = ''): array
{
$type = empty($type) ? gettype($object) : $type;
$result = (array) StrategyFactory::encodeStrategyByType(
$type,
$object,
$this->annotationReader,
$this->serializer
)->encode($object, null);
return $this->processPostSerialize($object, $result);
}
/**
* Returns underlying serializer instance.
*
* @return \Liip\Serializer\SerializerInterface
*/
public function getSerializer(): SerializerInterface
{
return $this->serializer;
}
/**
* Process post deserialize callback
*
* @param mixed $object
* @param mixed[] $result
*
* @return mixed[]
* @throws ReflectionException
*/
private function processPostSerialize($object, array $result): array
{
$class = get_class($object);
if (false !== $object) {
try {
$reflection = new ReflectionClass($class);
} catch (ReflectionException $e) {
return $result;
}
foreach ($reflection->getMethods() as $method) {
$postDeserialize = $this->annotationReader
->getMethodAnnotation($method, PostSerialize::class);
if ($postDeserialize instanceof PostSerialize) {
return $method->invokeArgs($object, [$result]);
}
}
}
return $result;
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* PHP version 7.3
*
* @category Accessor
* @package RetailCrm\Api\Component\FormData\Mapping
*/
namespace RetailCrm\Api\Component\FormData\Mapping;
use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Annotation\Target;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
/**
* Class Accessor
*
* @category Accessor
* @package RetailCrm\Api\Component\FormData\Mapping
*
* @Annotation
* @Attributes(
* @Attribute("getter", required=false, type="string"),
* @Attribute("setter", required=false, type="string")
* )
* @Target({"PROPERTY","ANNOTATION"})
*/
final class Accessor
{
/**
* Property getter
*
* @var string
*/
public $getter;
/**
* Property setter
*
* @var string
*/
public $setter;
}

View File

@ -0,0 +1,26 @@
<?php
/**
* PHP version 7.3
*
* @category JsonField
* @package RetailCrm\Api\Component\FormData\Mapping
*/
namespace RetailCrm\Api\Component\FormData\Mapping;
use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Class JsonField
*
* @category JsonField
* @package RetailCrm\Api\Component\FormData\Mapping
*
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
final class JsonField
{
}

View File

@ -0,0 +1,26 @@
<?php
/**
* PHP version 7.3
*
* @category NoTransform
* @package RetailCrm\Api\Component\FormData\Mapping
*/
namespace RetailCrm\Api\Component\FormData\Mapping;
use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Class NoTransform
*
* @category NoTransform
* @package RetailCrm\Api\Component\FormData\Mapping
*
* @Annotation
* @Target({"PROPERTY","ANNOTATION"})
*/
final class NoTransform
{
}

Some files were not shown because too many files have changed in this diff Show More