636 lines
20 KiB
PHP
636 lines
20 KiB
PHP
<?php
|
|
|
|
namespace LDC\LDCCRM\Models;
|
|
|
|
use LDC\LDCCRM\Enums\ApiError;
|
|
use LDC\LDCCRM\Enums\ApiStatus;
|
|
use LDC\LDCCRM\Enums\Country;
|
|
use LDC\LDCCRM\Exceptions\ApiErrorException;
|
|
use LDC\LDCCRM\Utils;
|
|
use TypeError;
|
|
|
|
/**
|
|
* This class represents the customer info with
|
|
* the account validation status. You can use it as
|
|
* a standalone class but beware of catching exceptions
|
|
*
|
|
* Be warned that on server side data is normalized
|
|
* - firstname and lastname are 'capitalized'
|
|
* - fields are trimmed
|
|
* - non numeric chars are removed from postcode
|
|
*
|
|
* @property string|null $hashcode The crm hashcode this is a readonly field
|
|
* @property string|null $email Must be a rfc valid email on a valid domain name
|
|
* @property string|null $password Required only on create, must have upper and lowers chars,
|
|
* at least 1 number, at leat 1 upper char, at least 1 lower char and
|
|
* at leat 1 special char
|
|
* @property string|null $firstname The account first name
|
|
* @property string|null $lastname The account last name
|
|
* @property string|null $birth_date The account birth date 'yyyy-mm-dd'
|
|
* @property string|null $phone The account phone
|
|
* @property string|null $address The account address (street number, name, build, flat number)
|
|
* @property string|null $address2 The account phone (address complement)
|
|
* @property string|null $city The account city name
|
|
* @property string|null $postcode The account city postcode
|
|
* @property \LDC\LDCCRM\Enums\Country $country The account country residence
|
|
* @property boolean $opt_in true if the account is opt in
|
|
* @property boolean $status true if the account is activated
|
|
* @property string|null $last_connection the last time user was connected 'yyyy-mm-dd HH:ii:ss'
|
|
*
|
|
* @example examples/CustomerExample.php
|
|
*
|
|
* @category Api Models
|
|
* @package LDCCrmClient
|
|
* @subpackage models
|
|
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
|
|
*/
|
|
class Customer extends ClientModel
|
|
{
|
|
/**
|
|
* The user hashcode, this is link with the customer and the brand it is related to
|
|
* It is used to identify the customer itself.
|
|
*
|
|
* Validate: UUID, in customer_infos
|
|
*
|
|
* Required
|
|
* Min length 36
|
|
* Max length 36
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $hashcode = null;
|
|
|
|
/**
|
|
* An RFC Valid Email
|
|
*
|
|
* Validate: rfc,dns,spoof,filter
|
|
*
|
|
* Required
|
|
* Min length N/A
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $email = null;
|
|
|
|
/**
|
|
* The user password
|
|
* This is set only when creating a user
|
|
* Otherwise it has to be set to null
|
|
*
|
|
* Validate: case_diff,numbers,letters,symbols,min:8
|
|
*
|
|
* Min length 8
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $password = null;
|
|
|
|
/**
|
|
* First name
|
|
*
|
|
* Required
|
|
* Min length 2
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $firstname = null;
|
|
|
|
/**
|
|
* Last name
|
|
*
|
|
* Required
|
|
* Min length 2
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $lastname = null;
|
|
|
|
/**
|
|
* Birth date
|
|
*
|
|
* Validate: date_format:Y-m-d
|
|
*
|
|
* Required
|
|
* Field Type Date
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $birth_date = null;
|
|
|
|
/**
|
|
* The user phone
|
|
*
|
|
* Validate: sometimes,string
|
|
*
|
|
* Required
|
|
* Min length NA
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $phone = null;
|
|
|
|
/**
|
|
* The user address (street number, name, etc)
|
|
*
|
|
* Validate: sometimes,string
|
|
*
|
|
* Min length NA
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $address = null;
|
|
|
|
/**
|
|
* The user address 2nd part (street number, name, more, etc)
|
|
*
|
|
* Validate: sometimes,string
|
|
*
|
|
* Min length NA
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $address2 = null;
|
|
|
|
/**
|
|
* The user address city
|
|
*
|
|
* Validate: sometimes,string
|
|
*
|
|
* Min length NA
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $city = null;
|
|
|
|
/**
|
|
* The user address postcode
|
|
*
|
|
* Validate: string
|
|
*
|
|
* Required
|
|
* Min length NA
|
|
* Max length 255
|
|
* Field Type varchar
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $postcode = null;
|
|
|
|
/**
|
|
* The user address postcode
|
|
*
|
|
* Validate: sometimes, in Country::toValues()
|
|
*
|
|
* Required
|
|
* Field Type tinyInt
|
|
*
|
|
* @var \LDC\LDCCRM\Enums\Country
|
|
*/
|
|
private ?int $country = null;
|
|
|
|
/**
|
|
* The user optin choice
|
|
*
|
|
* Validate: boolean
|
|
*
|
|
* Required
|
|
* Field Type TinyInt
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private bool $opt_in = false;
|
|
|
|
/**
|
|
* Is the account activated ?
|
|
*
|
|
* This field is read only
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private bool $status = false;
|
|
|
|
/**
|
|
* The last time the account was connected
|
|
*
|
|
* This field is read only
|
|
*
|
|
* @var string|null
|
|
*/
|
|
private ?string $last_connection = null;
|
|
|
|
/**
|
|
* The crm customer fields
|
|
*
|
|
* @var array
|
|
*/
|
|
protected array $modelFields = [
|
|
'hashcode',
|
|
'email',
|
|
'firstname',
|
|
'password',
|
|
'lastname',
|
|
'birth_date',
|
|
'phone',
|
|
'address',
|
|
'address2',
|
|
'city',
|
|
'postcode',
|
|
'country',
|
|
'opt_in',
|
|
'status',
|
|
'last_connection',
|
|
];
|
|
|
|
/**
|
|
* The crm customer fields
|
|
* the should not be visible
|
|
*
|
|
* @var array
|
|
*/
|
|
protected array $invisibleFields = [
|
|
'password',
|
|
];
|
|
|
|
/**
|
|
* Validation rules
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static array $validation = [
|
|
'hashcode' => 'required|uuid',
|
|
'email' => 'required|email',
|
|
'firstname' => 'required|string|min:1|max:255',
|
|
'lastname' => 'required|string|min:1|max:255',
|
|
'password' => 'required|min:8|max:255|mixedCase|containDigit|containSymbol',
|
|
'birth_date' => 'required|date_format:Y-m-d',
|
|
'phone' => 'sometimes|nullable|string|max:255',
|
|
'address' => 'sometimes|string|max:255',
|
|
'address2' => 'sometimes|string|max:255',
|
|
'city' => 'sometimes|string|min:2|max:255',
|
|
'postcode' => 'sometimes|string|max:255',
|
|
'country' => ['sometimes'],
|
|
'opt_in' => 'required|bool',
|
|
'status' => 'required|bool',
|
|
'last_connection' => 'sometimes|date',
|
|
];
|
|
|
|
/**
|
|
* Customer
|
|
*
|
|
* @param array $properties
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
*/
|
|
public function __construct(#[\SensitiveParameter] array $properties = [])
|
|
{
|
|
parent::__construct();
|
|
$this->fill($properties);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Generic accessor
|
|
*
|
|
* @param string $field
|
|
* @return mixed|null Returns null if the property was not found any way.
|
|
*/
|
|
public function __get(string $field)
|
|
{
|
|
$props = \array_diff(
|
|
$this->modelFields,
|
|
$this->invisibleFields
|
|
);
|
|
if (in_array($field, $props)) {
|
|
if ($field === 'country' and !\is_object($this->{$field})) {
|
|
return Country::make($this->{$field});
|
|
}
|
|
}
|
|
return parent::__get($field);
|
|
}
|
|
|
|
/**
|
|
* Set prop on the model.
|
|
*
|
|
* @param string $field
|
|
* @param mixed $value
|
|
* @return void
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
|
|
*/
|
|
public function __set(string $field, #[\SensitiveParameter] mixed $value): void
|
|
{
|
|
if ($field === 'country') {
|
|
$value = (\is_object($value) and
|
|
get_class($value) === Country::class) ? $value->value() : $value;
|
|
}
|
|
parent::__set($field, $value);
|
|
}
|
|
|
|
/**
|
|
* Fill values into model using validation rules.
|
|
*
|
|
* @param array $properties
|
|
* @return void
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
*/
|
|
public function fill(#[\SensitiveParameter] array $properties)
|
|
{
|
|
if (isset($properties['country'])) {
|
|
try {
|
|
$properties['country'] = (\is_object($properties['country']) and
|
|
get_class($properties['country']) === Country::class) ?
|
|
$properties['country']->value : (ctype_digit($properties['country']) ?
|
|
(int)$properties['country'] : $properties['country']);
|
|
} catch (TypeError | \BadMethodCallException $e) {
|
|
// * Cast anyway
|
|
$properties['country'] = (int)$properties['country'];
|
|
}
|
|
}
|
|
parent::fill($properties);
|
|
}
|
|
|
|
/**
|
|
* Validate all model properties
|
|
*
|
|
* @param array $ignore
|
|
* @return array the validated values
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
*/
|
|
protected function validateAllValues(array $ignore = []): array
|
|
{
|
|
$out = [];
|
|
if (!\in_array('country', $ignore)) {
|
|
try {
|
|
$this->validateValue('country', Country::make(Utils::getRawProperty($this, 'country')));
|
|
} catch (\TypeError | \BadMethodCallException $e) {
|
|
// Use this to throw validation error.
|
|
$this->validateValue('country', Utils::getRawProperty($this, 'country'));
|
|
}
|
|
$out['country'] = Utils::getRawProperty($this, 'country');
|
|
}
|
|
return array_merge($out, parent::validateAllValues(array_merge(['country'], $ignore)));
|
|
}
|
|
|
|
// * PUBLIC METHODS
|
|
|
|
/**
|
|
* Find a customer using its hashcode in the crm
|
|
*
|
|
* @param string $hashcode
|
|
* @return self|null Return null if the customer was not found
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public static function find(#[\SensitiveParameter] string $hashcode): ?self
|
|
{
|
|
static::setUpApiInterface();
|
|
$request = Utils::getResquest('apiV4.customers.show', [
|
|
'serviceCustomer' => $hashcode
|
|
]);
|
|
$customer = null;
|
|
try {
|
|
$response = static::$apiInterface->call($request);
|
|
$customer = new self();
|
|
// ! Use fillRaw here because we cant validate comming
|
|
// ! from CRM since it has crappy data comming from the old crm (jellix).
|
|
$customer->fillRaw($response->data);
|
|
Utils::setRawProperty($customer, 'isNew', false);
|
|
} catch (ApiErrorException $e) {
|
|
if (!$e->getApiError()->equals(ApiError::modelNotFound)) {
|
|
throw $e;
|
|
}
|
|
}
|
|
return $customer;
|
|
}
|
|
|
|
/**
|
|
* Find a customer using its email in the crm
|
|
* This is an alias of Customer::find()
|
|
*
|
|
* @param string $email
|
|
* @return self|null
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public static function findWithEmail(#[\SensitiveParameter] string $email): ?self
|
|
{
|
|
return static::find($email);
|
|
}
|
|
|
|
/**
|
|
* Save the customer on CRM.
|
|
*
|
|
* @return boolean True if save was successfull.
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - validationFailed, If provided data is wrong(CRM side).
|
|
* - modelNotFound, If this customer hashcode could not be found (new customers only).
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public function save(): bool
|
|
{
|
|
$saved = false;
|
|
if ($this->isNew) {
|
|
$saved = $this->store();
|
|
$this->isNew = !$saved;
|
|
$this->updatedValues = [];
|
|
if ($saved) {
|
|
Utils::setRawProperty($this, 'password', null);
|
|
}
|
|
return $saved;
|
|
} else {
|
|
if (empty($this->updatedValues)) {
|
|
return true;
|
|
}
|
|
if ($saved = $this->update()) {
|
|
$this->updatedValues = [];
|
|
Utils::setRawProperty($this, 'password', null);
|
|
}
|
|
}
|
|
return $saved;
|
|
}
|
|
|
|
/**
|
|
* Calls api store customer method
|
|
*
|
|
* @return boolean True if save was successfull.
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - validationFailed, If provided data is wrong(CRM side).
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
private function store(): bool
|
|
{
|
|
$data = $this->validateAllValues(['hashcode', 'last_connection', 'status']);
|
|
$data['password'] = $this->password;
|
|
|
|
static::setUpApiInterface();
|
|
$request = Utils::getResquest('apiV4.customers.store', [], $data);
|
|
|
|
$response = static::$apiInterface->call($request);
|
|
if ($status = $response->status->equals(ApiStatus::OK)) {
|
|
$this->updatedValues = [];
|
|
$this->fill($response->data);
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Calls api update customer method
|
|
*
|
|
* @return boolean True if save was successfull.
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - validationFailed, If provided data is wrong(CRM side).
|
|
* - modelNotFound, If this customer hashcode could not be found.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
private function update(): bool
|
|
{
|
|
$this->validateValue('hashcode', $this->hashcode);
|
|
static::setUpApiInterface();
|
|
$request = Utils::getResquest('apiV4.customers.update', [
|
|
'serviceCustomer' => $this->hashcode
|
|
], $this->updatedValues);
|
|
|
|
$response = static::$apiInterface->call($request);
|
|
if ($status = $response->status->equals(ApiStatus::OK)) {
|
|
$this->updatedValues = [];
|
|
$this->fill($response->data);
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Removes the customer from CRM.
|
|
*
|
|
* @return boolean true if succeeded
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - modelNotFound, If this customer hashcode could not be found.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public function delete(): bool
|
|
{
|
|
$this->validateValue('hashcode', $this->hashcode);
|
|
static::setUpApiInterface();
|
|
$request = Utils::getResquest('apiV4.customers.destroy', [
|
|
'serviceCustomer' => $this->hashcode
|
|
]);
|
|
|
|
$response = static::$apiInterface->call($request);
|
|
return $response->status->equals(ApiStatus::OK);
|
|
}
|
|
|
|
/**
|
|
* Active the customer on CRM.
|
|
*
|
|
* @return boolean true if succeeded
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's hashcode could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - modelNotFound, If this customer hashcode could not be found.
|
|
* - customerAlreadyActive, It the customer is already activated.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public function activate(): bool
|
|
{
|
|
$this->validateValue('hashcode', $this->hashcode);
|
|
static::setUpApiInterface();
|
|
$request = Utils::getResquest('apiV4.customers.activate', [
|
|
'serviceCustomer' => $this->hashcode
|
|
]);
|
|
|
|
$response = static::$apiInterface->call($request);
|
|
$this->__set('status', true);
|
|
return $response->status->equals(ApiStatus::OK);
|
|
}
|
|
|
|
/**
|
|
* Toggle the account optin value in the crm
|
|
*
|
|
* @return boolean true if succeeded
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - modelNotFound, If this customer hashcode could not be found.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public function toggleOptin(): bool
|
|
{
|
|
$this->__set('opt_in', !$this->opt_in);
|
|
return $this->update();
|
|
}
|
|
|
|
/**
|
|
* Test customer credentials
|
|
*
|
|
* @param string $email
|
|
* @param string $password
|
|
* @return boolean
|
|
* @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's email could not be validated.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
|
|
* @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
|
|
* Possibles are : - authFailed, If credentials are not valid.
|
|
* - customerNotActivated, If the account is not activated.
|
|
* @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
|
*/
|
|
public static function login(#[\SensitiveParameter] string $email, #[\SensitiveParameter] string $password): bool
|
|
{
|
|
self::validateValue('email', $email);
|
|
static::setUpApiInterface();
|
|
$request = Utils::getResquest('apiV4.customers.authenticate', [], [
|
|
'email' => $email,
|
|
'password' => $password
|
|
]);
|
|
$response = static::$apiInterface->call($request);
|
|
return $response->status->equals(ApiStatus::OK);
|
|
}
|
|
|
|
/**
|
|
* Serialize this object to an array
|
|
*
|
|
* @return array
|
|
*/
|
|
public function jsonSerialize()
|
|
{
|
|
return [
|
|
'hashcode' => $this->hashcode,
|
|
'email' => $this->email,
|
|
'firstname' => $this->firstname,
|
|
'lastname' => $this->lastname,
|
|
'birth_date' => $this->birth_date,
|
|
'phone' => $this->phone,
|
|
'address' => $this->address,
|
|
'address2' => $this->address2,
|
|
'city' => $this->city,
|
|
'postcode' => $this->postcode,
|
|
'country' => $this->country,
|
|
'opt_in' => $this->opt_in,
|
|
'status' => $this->status,
|
|
'last_connection' => $this->last_connection
|
|
];
|
|
}
|
|
}
|