ldc-crm-client/src/Models/Customer.php

607 lines
17 KiB
PHP
Raw Normal View History

<?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
*
* @property string $hashcode The crm hashcode this is a readonly field
* @property string $email Must be a rfc valid email on a valid domain name
* @property string $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 $firstname The account first name
* @property string $lastname The account last name
* @property string $birth_date The account birth date 'yyyy-mm-dd'
* @property string $phone The account phone
* @property string $address The account address (street number, name, build, flat number)
* @property string $address2 The account phone (address complement)
* @property string $city The account city name
* @property string $postcode The account city postcode
* @property \LDC\LDCCRM\Enums\Country $country The account country residence
* @property bool $opt_in true if the account is opt in
* @property bool $status true if the account is activated
* @property string $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
*/
class Customer extends CrmClientModel
{
/**
* Is true if this model was not filled from crm
*
* @var boolean
*/
private $isNew = true;
/**
* 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
*/
private $hashcode;
/**
* An RFC Valid Email
*
* Validate: rfc,dns,spoof,filter
*
* Required
* Min length N/A
* Max length 255
* Field Type varchar
*
* @var string
*/
private $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
*/
private $password = null;
/**
* First name
*
* Required
* Min length 2
* Max length 255
* Field Type varchar
*
* @var string
*/
private $firstname = null;
/**
* Last name
*
* Required
* Min length 2
* Max length 255
* Field Type varchar
*
* @var string
*/
private $lastname = null;
/**
* Birth date
*
* Validate: date_format:Y-m-d
*
* Required
* Field Type Date
* @var string
*/
private $birth_date = null;
/**
* The user phone
*
* Validate: sometimes,string
*
* Required
* Min length NA
* Max length 255
* Field Type varchar
*
* @var string
*/
private $phone = null;
/**
* The user address (street number, name, etc)
*
2021-08-16 13:23:31 +02:00
* Validate: sometimes,string
*
* Min length NA
* Max length 255
* Field Type varchar
*
* @var string
*/
private $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
*/
private $address2 = null;
/**
* The user address city
*
2021-08-16 13:23:31 +02:00
* Validate: sometimes,string
*
* Min length NA
* Max length 255
* Field Type varchar
*
* @var string
*/
private $city = null;
/**
* The user address postcode
*
* Validate: string
*
* Required
* Min length NA
* Max length 255
* Field Type varchar
*
* @var string
*/
private $postcode = null;
/**
* The user address postcode
*
* Validate: sometimes, in Country::toValues()
*
* Required
* TODO: change this crm field to TinyInt when CRMV1 is dropped
* Field Type varchar (for now)
*
* @var \LDC\LDCCRM\Enums\Country
*/
private $country = null;
/**
* The user optin choice
*
* Validate: boolean
*
* Required
* Field Type TinyInt
*
* @var bool
*/
private $opt_in = false;
/**
* Is the account activated ?
*
* This field is read only
*
* @var bool
*/
private $status = false;
/**
* The last time the account was connected
*
* This field is read only
*
* @var string
*/
private $last_connection = null;
/**
* The crm customer fields
*
* @var array
*/
protected $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 $invisibleFields = [
'password'
];
/**
* Validation rules
*
* @var array
*/
protected static $validation = [
'hashcode' => 'required|uuid',
'email' => 'required|email',
'firstname' => 'required|string|min:2|max:255',
'lastname' => 'required|string|min:2|max:255',
'password' => 'required|min:8|max:255',
'birth_date' => 'required|date_format:Y-m-d',
'phone' => 'sometimes|string|min:10|max:255',
2021-08-16 13:23:31 +02:00
'address' => 'sometimes|string|max:255',
'address2' => 'sometimes|string|max:255',
'city' => 'required|string|max:255',
'postcode' => 'required|string|max:255',
'country' => ['sometimes'],
'opt_in' => 'required|bool',
'status' => 'required|bool',
'last_connection' => 'sometimes|date'
];
public function __construct(array $properties = [])
{
parent::__construct();
if (!is_array(self::$validation['password'])) {
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
self::$validation['country'][] = function ($attribute, $value, $fail) {
if (!is_object($value) or !\in_array($value, Country::toArray())) {
$type = \gettype($value);
$fail("Country ($type)$value is not in Country enum list");
}
};
self::$validation['password'] = array_merge(explode('|', self::$validation['password']), [
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed,Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if (!\preg_match('/[a-z]/', $value) or !\preg_match('/[A-Z]/', $value)) {
$fail('The password must contain uppercase and lowercase chars.');
}
if (!\preg_match('/[0-9]/', $value)) {
$fail('The password must contain numbers.');
}
if (!\preg_match('/[^\w]/', $value)) {
$fail('The password must contain symbols.');
}
}
]);
}
$this->fill($properties);
return $this;
}
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
*
* @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) === Country::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['country'])) {
try {
$properties['country'] = (\is_int($properties['country']) or \ctype_digit($properties['country'])) ?
Country::make((int)$properties['country']) : $properties['country'];
} catch (TypeError $e) {
// cast anyway
$properties['country'] = (int)$properties['country'];
}
}
parent::fill($properties);
}
/**
* Validate all model properties
*
* @return array the validated values
*/
protected function validateAllValues(array $ignore = []): array
{
$out = [];
if (!\in_array('country', $ignore)) {
try {
$this->validateValue('country', Country::make(self::getRawProperty($this, 'country')));
} catch (TypeError $e) {
// this to throw validation error if failure way
$this->validateValue('country', self::getRawProperty($this, 'country'));
}
$out['country'] = self::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
2021-08-12 14:49:27 +02:00
* @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
*/
public static function find(string $hashcode): ?self
{
static::setUpApiInterface(); // set apiInterface
2021-08-11 17:26:30 +02:00
$request = Utils::getResquest('apiV2.customers.show', [
'customer' => $hashcode
]);
$customer = null;
try {
$response = static::$apiInterface->call($request);
$customer = new self();
$customer->fillRaw($response->data);
static::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
2021-08-12 14:49:27 +02:00
* @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
*/
public static function findWithEmail(string $email): ?self
{
return static::find($email);
}
/**
* Save the actual account in the crm
*
* @return boolean true if succeeded
2021-08-12 14:49:27 +02:00
* @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
*/
public function save(): bool
{
$saved = false;
if ($this->isNew) {
$saved = $this->store();
$this->isNew = !$saved;
$this->updatedValues = [];
if ($saved) {
$this->setRawProperty($this, 'password', null);
}
return $saved;
}
if ($saved = $this->update()) {
$this->updatedValues = [];
$this->setRawProperty($this, 'password', null);
}
return $saved;
}
private function store(): bool
{
$data = $this->validateAllValues(['hashcode', 'last_connection', 'status']);
$data['password'] = $this->password;
static::setUpApiInterface(); // set apiInterface
2021-08-11 17:26:30 +02:00
$request = Utils::getResquest('apiV2.customers.store', [], $data);
$response = static::$apiInterface->call($request);
if ($status = $response->status->equals(ApiStatus::OK())) {
$this->updatedValues = [];
}
return $status;
}
private function update(): bool
{
$this->validateValue('hashcode', $this->hashcode);
static::setUpApiInterface(); // set apiInterface
2021-08-11 17:26:30 +02:00
$request = Utils::getResquest('apiV2.customers.update', [
'customer' => $this->hashcode
], $this->updatedValues);
$response = static::$apiInterface->call($request);
if ($status = $response->status->equals(ApiStatus::OK())) {
$this->updatedValues = [];
}
return $status;
}
/**
* Delete the actual account in the crm
*
* @return boolean true if succeeded
2021-08-12 14:49:27 +02:00
* @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
*/
public function delete(): bool
{
$this->validateValue('hashcode', $this->hashcode);
static::setUpApiInterface(); // set apiInterface
2021-08-11 17:26:30 +02:00
$request = Utils::getResquest('apiV2.customers.destroy', [
'customer' => $this->hashcode
]);
$response = static::$apiInterface->call($request);
return $response->status->equals(ApiStatus::OK());
}
/**
* Activate the account in the crm
*
* @return boolean true if succeeded
2021-08-12 14:49:27 +02:00
* @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
*/
public function activate(): bool
{
$this->validateValue('hashcode', $this->hashcode);
static::setUpApiInterface(); // set apiInterface
2021-08-11 17:26:30 +02:00
$request = Utils::getResquest('apiV2.customers.activate', [
'customer' => $this->hashcode
]);
$response = static::$apiInterface->call($request);
$this->status = true;
return $response->status->equals(ApiStatus::OK());
}
/**
* Toggle the account optin value in the crm
*
* @return boolean true if succeeded
2021-08-12 14:49:27 +02:00
* @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
*/
public function toggleOptin(): bool
{
$this->opt_in = !$this->opt_in;
return $this->update();
}
2021-08-12 08:34:15 +02:00
/**
* Test customer credentials
*
* @param string $email
* @param string $password
* @return boolean
2021-08-12 14:49:27 +02:00
* @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
2021-08-12 08:34:15 +02:00
*/
public static function login(string $email, string $password): bool
{
self::validateValue('email', $email);
static::setUpApiInterface(); // set apiInterface
$request = Utils::getResquest('apiV2.customers.authenticate', [], [
'email' => $email,
'password' => $password
]);
try {
$response = static::$apiInterface->call($request);
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::authFailed())) {
return false;
}
2021-08-16 13:22:34 +02:00
/**
* 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
];
2021-08-12 08:34:15 +02:00
}
}