More methods on CrmApiV2
This commit is contained in:
Jérémy Munsch 2021-08-11 17:26:30 +02:00
parent eb69ef30e9
commit 14f0bb83dd
11 changed files with 698 additions and 20 deletions

View file

@ -3,7 +3,7 @@
"type": "library",
"description": "LDC CRM Client V2",
"license": "MIT",
"version": "2.0.0",
"version": "2.0.1",
"authors": [
{
"name": "Jérémy Munsch",
@ -32,7 +32,8 @@
"require-dev": {
"phpunit/phpunit": "~9.0",
"squizlabs/php_codesniffer": "^3.5",
"orchestra/testbench": "^6.13"
"orchestra/testbench": "^6.13",
"slevomat/coding-standard": "^7.0"
},
"autoload": {
"psr-4": {

182
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "8ae44f3768b7ee6045ae61fb2655ea9c",
"content-hash": "89b14a69db5103b9d01833ed279ab009",
"packages": [
{
"name": "brick/math",
@ -4562,6 +4562,76 @@
}
],
"packages-dev": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.7.1",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "fe390591e0241955f22eb9ba327d137e501c771c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c",
"reference": "fe390591e0241955f22eb9ba327d137e501c771c",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"phpcompatibility/php-compatibility": "^9.0",
"sensiolabs/security-checker": "^4.1.0"
},
"type": "composer-plugin",
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
"time": "2020-12-07T18:04:37+00:00"
},
{
"name": "doctrine/instantiator",
"version": "1.4.0",
@ -5421,6 +5491,55 @@
},
"time": "2021-03-17T13:42:18+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "0.5.5",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "ea0b17460ec38e20d7eb64e7ec49b5d44af5d28c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ea0b17460ec38e20d7eb64e7ec49b5d44af5d28c",
"reference": "ea0b17460ec38e20d7eb64e7ec49b5d44af5d28c",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12.87",
"phpstan/phpstan-strict-rules": "^0.12.5",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.5-dev"
}
},
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/0.5.5"
},
"time": "2021-06-11T13:24:46+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.6",
@ -6807,6 +6926,67 @@
],
"time": "2020-09-28T06:39:44+00:00"
},
{
"name": "slevomat/coding-standard",
"version": "7.0.13",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "122a9bf9a4a2195f74100f47dfb8375982f43cc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/122a9bf9a4a2195f74100f47dfb8375982f43cc9",
"reference": "122a9bf9a4a2195f74100f47dfb8375982f43cc9",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"php": "^7.1 || ^8.0",
"phpstan/phpdoc-parser": "0.5.1 - 0.5.5",
"squizlabs/php_codesniffer": "^3.6.0"
},
"require-dev": {
"phing/phing": "2.16.4",
"php-parallel-lint/php-parallel-lint": "1.3.0",
"phpstan/phpstan": "0.12.93",
"phpstan/phpstan-deprecation-rules": "0.12.6",
"phpstan/phpstan-phpunit": "0.12.21",
"phpstan/phpstan-strict-rules": "0.12.10",
"phpunit/phpunit": "7.5.20|8.5.5|9.5.7"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "7.x-dev"
}
},
"autoload": {
"psr-4": {
"SlevomatCodingStandard\\": "SlevomatCodingStandard"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"support": {
"issues": "https://github.com/slevomat/coding-standard/issues",
"source": "https://github.com/slevomat/coding-standard/tree/7.0.13"
},
"funding": [
{
"url": "https://github.com/kukulich",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
"type": "tidelift"
}
],
"time": "2021-07-29T14:30:22+00:00"
},
{
"name": "spatie/backtrace",
"version": "1.2.0",

View file

@ -77,6 +77,7 @@
</rule>
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing" />
<rule ref="Squiz.WhiteSpace.SemicolonSpacing" />
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse" />
<!-- For Laravel migrations -->

View file

@ -68,6 +68,17 @@ class CrmApiV2
return Customer::find($identification);
}
/**
* Get all segmentation questions
*
* @param string $identification The customer hashcode or email
* @return Customer
*/
public function getQuestions(string $identification): ?Customer
{
return Customer::find($identification);
}
/**
* Set the single optin status for an email
*

View file

@ -0,0 +1,25 @@
<?php
namespace LDC\LDCCRM\Enums;
use Kwaadpepper\Enum\BaseEnum;
/**
* @method static self select()
* @method static self radio()
* @method static self checkbox()
*
* @package LDCCrmClient
* @subpackage enums
*/
class QuestionType extends BaseEnum
{
public static function values(): array
{
return [
'select' => 1,
'radio' => 2,
'checkbox' => 3
];
}
}

114
src/Models/Answer.php Normal file
View file

@ -0,0 +1,114 @@
<?php
namespace LDC\LDCCRM\Models;
use LDC\LDCCRM\Enums\ApiStatus;
use LDC\LDCCRM\Utils;
/**
* This class represents an answer
* It has a method to submit answers
*
* @property string $id The answer id
* @property string $question The actual question as a string
* @property \LDC\LDCCRM\Enums\QuestionType $type
* The question type to display,
* it also hints wether it can accept one or multiple responses,
* so only checkbox allows multiple responses
* @property \LDC\LDCCRM\Models\Answer[] $answers An array with answers
*
* @category Api Models
* @package LDCCrmClient
* @subpackage models
*/
class Answer extends CrmClientModel
{
/**
* Is true if this model was not filled from crm
*
* @var boolean
*/
private $isNew = true;
/**
* The question id
*
* Required
* Field Type bigInt
*
* @var int
*/
private $id;
/**
* The actual answer as a string
*
* Required
* Max length 255
* Field Type varchar
*
* @var string
*/
private $answer = null;
/**
* The crm customer fields
*
* @var array
*/
protected $modelFields = [
'id', 'answer'
];
/**
* Validation rules
*
* @var array
*/
protected static $validation = [
'id' => 'required|int',
'answer' => 'required|string|max:255'
];
public function __construct(array $properties = [])
{
parent::__construct();
$this->fill($properties);
return $this;
}
// PUBLIC METHODS
/**
* Get unanswered question for a specific customer
*
* @param \LDC\LDCCRM\Models\Customer $customer
* @return bool
*/
public static function setQuestionAnswers(string $hashcode, int $questionId, array $answersId): bool
{
self::assertValidHascode($hashcode);
self::assertValidPrimaryKey($questionId);
if (!count($answersId)) {
return true;
}
foreach ($answersId as $answerId) {
self::assertValidPrimaryKey($answerId);
}
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest(
'apiV2.customers.answers.store',
[
'customer' => $hashcode
],
[
'question' => $questionId,
'answers' => $answersId
]
);
$response = static::$apiInterface->call($request);
return $response->status->equals(ApiStatus::OK());
}
}

View file

@ -272,4 +272,40 @@ abstract class CrmClientModel
$refProp->setAccessible(true);
return $refProp->getValue($object);
}
/**
* Assert primaryKey is valid
*
* @param int $primaryKey
* @return void
* @throws LDC\LDCCRM\Exceptions\ValidationException
*/
protected static function assertValidPrimaryKey(int $primaryKey): void
{
$validator = Utils::makeValidator(
['id' => $primaryKey],
['id' => 'required|int']
);
if ($validator->fails()) {
throw new ValidationException(implode(' ', $validator->errors()->get('id')));
}
}
/**
* Assert hashcode is valid
*
* @param string|null $hashcode
* @return void
* @throws LDC\LDCCRM\Exceptions\ValidationException
*/
protected static function assertValidHascode(?string $hashcode): void
{
$validator = Utils::makeValidator(
['hashcode' => $hashcode],
['hashcode' => 'required|uuid']
);
if ($validator->fails()) {
throw new ValidationException(implode(' ', $validator->errors()->get('hashcode')));
}
}
}

View file

@ -401,7 +401,7 @@ class Customer extends CrmClientModel
public static function find(string $hashcode): ?self
{
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.customer.show', [
$request = Utils::getResquest('apiV2.customers.show', [
'customer' => $hashcode
]);
$customer = null;
@ -460,7 +460,7 @@ class Customer extends CrmClientModel
$data['password'] = $this->password;
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.customer.store', [], $data);
$request = Utils::getResquest('apiV2.customers.store', [], $data);
$response = static::$apiInterface->call($request);
if ($status = $response->status->equals(ApiStatus::OK())) {
@ -473,7 +473,7 @@ class Customer extends CrmClientModel
{
$this->validateValue('hashcode', $this->hashcode);
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.customer.update', [
$request = Utils::getResquest('apiV2.customers.update', [
'customer' => $this->hashcode
], $this->updatedValues);
@ -493,7 +493,7 @@ class Customer extends CrmClientModel
{
$this->validateValue('hashcode', $this->hashcode);
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.customer.destroy', [
$request = Utils::getResquest('apiV2.customers.destroy', [
'customer' => $this->hashcode
]);
@ -510,7 +510,7 @@ class Customer extends CrmClientModel
{
$this->validateValue('hashcode', $this->hashcode);
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.customer.activate', [
$request = Utils::getResquest('apiV2.customers.activate', [
'customer' => $this->hashcode
]);

282
src/Models/Question.php Normal file
View file

@ -0,0 +1,282 @@
<?php
namespace LDC\LDCCRM\Models;
use LDC\LDCCRM\Enums\QuestionType;
use LDC\LDCCRM\Utils;
use TypeError;
/**
* This class represents a question
* It has some static methods to retrieve questions and answers
*
* @property string $id The question id
* @property string $question The actual question as a string
* @property \LDC\LDCCRM\Enums\QuestionType $type
* The question type to display,
* it also hints wether it can accept one or multiple responses,
* so only checkbox allows multiple responses
* @property \LDC\LDCCRM\Models\Answer[] $answers An array with answers
*
* @category Api Models
* @package LDCCrmClient
* @subpackage models
*/
class Question extends CrmClientModel
{
/**
* Is true if this model was not filled from crm
*
* @var boolean
*/
private $isNew = true;
/**
* The question id
*
* Required
* Field Type bigInt
*
* @var int
*/
private $id;
/**
* The actual question as a string
*
* Required
* Max length 255
* Field Type varchar
*
* @var string
*/
private $question = null;
/**
* The question type
*
* Validate: in QuestionType::toValues()
*
* Required
* Field Type tinyInteger
*
* @var \LDC\LDCCRM\Enums\QuestionType
*/
private $type = null;
/**
* The question answers
*
* @var \LDC\LDCCRM\Models\Answer[]
*/
private $answers = [];
/**
* The crm customer fields
*
* @var array
*/
protected $modelFields = [
'id', 'question', 'type', 'answers'
];
/**
* Validation rules
*
* @var array
*/
protected static $validation = [
'id' => 'required|int',
'question' => 'required|string|max:255',
'type' => ['required'],
'answers' => 'required|array'
];
public function __construct(array $properties = [])
{
parent::__construct();
if (count(self::$validation['type']) < 2) {
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
self::$validation['type'][] = function ($attribute, $value, $fail) {
if (!is_object($value) or !\in_array($value, QuestionType::toArray())) {
$type = \gettype($value);
$fail("Question type ($type)$value is not in QuestionType enum list");
}
};
}
$this->fill($properties);
return $this;
}
public function __get(string $field)
{
$props = \array_diff(
$this->modelFields,
$this->invisibleFields
);
if (in_array($field, $props)) {
if ($field === 'type' and !\is_object($this->{$field})) {
return QuestionType::make($this->{$field});
}
}
return parent::__get($field);
}
/**
* Set prop
*
* @param string $field
* @param mixed $value
* @throws TypeError if the field has an incorrect value
* @return mixed|null The input value or null is this prop does not exists
*/
public function __set(string $field, $value)
{
$out = parent::__set($field, $value);
if (in_array($field, $this->modelFields)) {
$this->updatedValues[$field] = (\is_object($value) and
get_class($value) === QuestionType::class) ? $value->value : $value;
$this->setRawProperty($this, $field, $this->updatedValues[$field]);
}
return $out;
}
/**
* Fill data into this model
* using validation rules
*
* @param array $properties
* @return void
* @throws TypeError if any valid is not validated
*/
public function fill(array $properties)
{
if (isset($properties['type'])) {
try {
$properties['type'] = (\is_int($properties['type']) or \ctype_digit($properties['type'])) ?
QuestionType::make((int)$properties['type']) : $properties['type'];
} catch (TypeError $e) {
// cast anyway
$properties['type'] = (int)$properties['type'];
}
}
parent::fill($properties);
}
/**
* Validate all model properties
*
* @return array the validated values
*/
protected function validateAllValues(array $ignore = []): array
{
$out = [];
if (!\in_array('type', $ignore)) {
try {
$this->validateValue('type', QuestionType::make(self::getRawProperty($this, 'type')));
} catch (TypeError $e) {
// this to throw validation error if failure way
$this->validateValue('type', self::getRawProperty($this, 'type'));
}
$out['type'] = self::getRawProperty($this, 'type');
}
return array_merge($out, parent::validateAllValues(array_merge(['type'], $ignore)));
}
// PUBLIC METHODS
/**
* Get all questions only
*
* @param string $hashcode
* @return array
*
* with \LDC\LDCCRM\Models\Question[]
*/
public static function all(): array
{
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.questions.index');
$response = static::$apiInterface->call($request);
$out = [];
foreach ($response->data as $question) {
$answers = \array_map(function ($answer) {
return new Answer($answer);
}, $question['answers']);
$question['answers'] = $answers;
$out[] = new self($question);
}
return $out;
}
/**
* Get all the questions with customer answers
*
* @param \LDC\LDCCRM\Models\Customer $customer
* @return array
*
* [
* 0 => [
* 'question' => \LDC\LDCCRM\Models\Question,
* 'customerAnswers' => \LDC\LDCCRM\Models\Answer[]
* ]
* ]
*
* with \LDC\LDCCRM\Models\Question[]
* and the customer \LDC\LDCCRM\Models\Answer[]
*/
public static function allWithCustomerAnswers(Customer $customer): array
{
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.questions.indexWithResponse', [
'customer' => $customer->hashcode
]);
$response = static::$apiInterface->call($request);
$out = [];
foreach ($response->data as $question) {
$answers = \array_map(function ($answer) {
return new Answer($answer);
}, $question['answers']);
$customerAnswers = \array_map(function ($answer) {
return new Answer($answer);
}, $question['customerAnswers']);
unset($question['customerAnswers']);
$question['answers'] = $answers;
$out[] = [
'question' => new self($question),
'customerAnswers' => $customerAnswers,
];
}
return $out;
}
/**
* Get unanswered question for a specific customer
*
* @param \LDC\LDCCRM\Models\Customer $customer
* @return \LDC\LDCCRM\Models\Question[]
*/
public static function unansweredBy(Customer $customer): array
{
self::assertValidHascode($customer->hashcode);
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest(
'apiV2.questions.unAnsweredBy',
['customer' => $customer->hashcode]
);
$response = static::$apiInterface->call($request);
$out = [];
foreach ($response->data as $question) {
$answers = \array_map(function ($answer) {
return new Answer($answer);
}, $question['answers']);
$question['answers'] = $answers;
$out[] = new self($question);
}
return $out;
}
}

View file

@ -17,6 +17,7 @@ use RecursiveIteratorIterator;
* @property string $message The api message
* @property array $headers The http reponse headers
* @property array $body The http response body, this is parsed json
* @property ?array $data The reponse data
*
* @package LDCCrmClient
* @subpackage transport

View file

@ -18,40 +18,46 @@
"POST"
]
},
"apiV2.customer.store": {
"uri": "api/v2/customer",
"apiV2.customers.store": {
"uri": "api/v2/customers",
"methods": [
"POST"
]
},
"apiV2.customer.show": {
"uri": "api/v2/customer/{customer}",
"apiV2.customers.show": {
"uri": "api/v2/customers/{customer}",
"methods": [
"GET",
"HEAD"
]
},
"apiV2.customer.update": {
"uri": "api/v2/customer/{customer}",
"apiV2.customers.update": {
"uri": "api/v2/customers/{customer}",
"methods": [
"PUT",
"PATCH"
]
},
"apiV2.customer.destroy": {
"uri": "api/v2/customer/{customer}",
"apiV2.customers.destroy": {
"uri": "api/v2/customers/{customer}",
"methods": [
"DELETE"
]
},
"apiV2.customer.authenticate": {
"uri": "api/v2/customer/authenticate",
"apiV2.customers.authenticate": {
"uri": "api/v2/customers/authenticate",
"methods": [
"POST"
]
},
"apiV2.customer.activate": {
"uri": "api/v2/customer/{customer}/activate",
"apiV2.customers.activate": {
"uri": "api/v2/customers/{customer}/activate",
"methods": [
"POST"
]
},
"apiV2.customers.answers.store": {
"uri": "api/v2/customers/{customer}/answers",
"methods": [
"POST"
]
@ -67,5 +73,26 @@
"methods": [
"POST"
]
},
"apiV2.questions.index": {
"uri": "api/v2/questions",
"methods": [
"GET",
"HEAD"
]
},
"apiV2.questions.unAnsweredBy": {
"uri": "api/v2/questions/un-answered-by/{customer}",
"methods": [
"GET",
"HEAD"
]
},
"apiV2.questions.indexWithResponse": {
"uri": "api/v2/questions/with-answers/{customer}",
"methods": [
"GET",
"HEAD"
]
}
}