This repository has been archived on 2024-10-16. You can view files and clone it, but cannot push or open issues or pull requests.
Files
ebala/mirzaev/ebala/system/models/account.php

470 lines
18 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace mirzaev\ebala\models;
// Project files
use mirzaev\ebala\models\traits\instance,
mirzaev\ebala\models\traits\status;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Встроенные библиотеки
use exception;
/**
* Модель аккаунта
*
* @package mirzaev\ebala\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class account extends core
{
use instance, status;
/**
* Коллекция
*/
final public const COLLECTION = 'account';
/**
* Инстанция документа в базе данных
*/
protected readonly _document $document;
/**
* Конструктор
*
* @param ?session $session Инстанция сессии
* @param ?string $authenticate Аутентифицировать аккаунт? Если да, то какой категории? ([worker|operator|market] из $_SERVER['INTERFACE'])
* @param array &$errors Реестр ошибок
*
* @return static Инстанция аккаунта
*/
public function __construct(?session $session = null, ?string $authenticate = null, array &$errors = [])
{
try {
if (isset($session)) {
// Получена инстанция сессии
if ($account = $session->account()) {
// Найден связанный с сессией аккаунт
// Инициализация инстанции документа аккаунта в базе данных
$this->document = $account->document;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
return $this;
} else {
// Не найден связанный с сессией аккаунт
if (
match ($authenticate) {
'worker', 'operator', 'market', 'administrator' => true,
default => false
}
) {
// Запрошена аутентификация
if (!empty($session->buffer['worker']['entry']['number'])) {
// Найден номер сотрудника в буфере сессии
if (!empty($session->buffer['worker']['entry']['password'])) {
// Найден пароль в буфере сессии
if (($account = self::read('d.number == "' . $session->buffer['worker']['entry']['number'] . '" && d.type == "worker"', amount: 1, errors: $errors)) instanceof _document) {
// Найден аккаунт сотрудника (игнорируются ошибки)
if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['worker']['entry']['password'])) {
// Аутентифицирован аккаунт (прошёл проверку пароль)
// Инициализация инстанции документа аккаунта в базе данных
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['number' => null, 'password' => null]]);
// Выход (успех)
return $this;
} else throw new exception('Неправильный пароль');
throw new exception('Неизвестная ошибка на этапе проверки пароля');
} else throw new exception('Не найден аккаунт');
} else throw new exception('Не найден пароль в буфере сессии');
} else if (!empty($session->buffer['operator']['entry']['_key'])) {
// Найден идентификатор оператора в буфере сессии
if (!empty($session->buffer['operator']['entry']['password'])) {
// Найден пароль в буфере сессии
if (($account = self::read('d._key == "' . $session->buffer['operator']['entry']['_key'] . '" && d.type == "operator"', amount: 1)) instanceof _document) {
// Найден аккаунт оператора (игнорируются ошибки)
if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['operator']['entry']['password'])) {
// Аутентифицирован аккаунт (прошёл проверку пароль)
// Инициализация инстанции документа аккаунта в базе данных
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['_key' => null, 'password' => null]]);
// Выход (успех)
return $this;
} else throw new exception('Неправильный пароль');
throw new exception('Неизвестная ошибка на этапе проверки пароля');
}
} else throw new exception('Не найден пароль в буфере сессии');
} else if (!empty($session->buffer['market']['entry']['id'])) {
// Найден идентификатор магазина в буфере сессии
if (!empty($session->buffer['market']['entry']['password'])) {
// Найден пароль в буфере сессии
if (($account = market::account(market::read('d.id == "' . $session->buffer['market']['entry']['id'] . '"', amount: 1)?->getId())) instanceof _document) {
// Найден аккаунт (игнорируются ошибки)
if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['market']['entry']['password'])) {
// Аутентифицирован аккаунт (прошёл проверку пароль)
// Инициализация инстанции документа аккаунта в базе данных
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['_key' => null, 'password' => null]]);
// Выход (успех)
return $this;
} else throw new exception('Неправильный пароль');
throw new exception('Неизвестная ошибка на этапе проверки пароля');
}
} else throw new exception('Не найден пароль в буфере сессии');
} else if (!empty($session->buffer['administrator']['entry'])) {
// Найден идентификатор администратора в буфере сессии
if (!empty($session->buffer['administrator']['entry']['password'])) {
// Найден пароль в буфере сессии
if (($account = self::read('d._key == "' . $session->buffer['administrator']['entry']['_key'] . '" && d.type == "administrator"', amount: 1)) instanceof _document) {
// Найден аккаунт администратора (игнорируются ошибки)
if (sodium_crypto_pwhash_str_verify($account->password, $session->buffer['administrator']['entry']['password'])) {
// Аутентифицирован аккаунт (прошёл проверку пароль)
// Инициализация инстанции документа аккаунта в базе данных
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['_key' => null, 'password' => null]]);
// Выход (успех)
return $this;
} else throw new exception('Неправильный пароль');
throw new exception('Неизвестная ошибка на этапе проверки пароля');
}
} else throw new exception('Не найден пароль в буфере сессии');
} else throw new exception('Не найдены данные первичной идентификации в буфере сессии');
}
}
} else throw new exception('Не найдена сессия');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
}
/**
* Найти связанного с аккаунтом сотрудника
*
* @param string $_id Идентификатор аккаунта связанного с сотрудником
* @param array &$errors Реестр ошибок
*
* @return ?_document Инстанция документа сотрудника в базе данных, если найдена
*/
public static function worker(string $_id, array &$errors = []): ?_document
{
try {
if (collection::init(static::$arangodb->session, worker::COLLECTION)) {
// Инициализирована коллекция
// Поиск сотрудника
$worker = collection::search(
static::$arangodb->session,
sprintf(
<<<'AQL'
FOR d IN %s
LET e = (
FOR e IN %s
FILTER e._from == '%s'
SORT e.created DESC, e._key DESC
LIMIT 1
RETURN e
)
FILTER d._id == e[0]._to && d.active == true
SORT d.created DESC, d._key DESC
LIMIT 1
RETURN d
AQL,
worker::COLLECTION,
static::COLLECTION . '_edge_worker',
$_id
)
);
// Возврат (успех)
return $worker instanceof _document ? $worker : throw new exception('Не удалось найти инстанцию сотрудника в базе данных');
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
/**
* Найти связанный с аккаунтом магазин
*
* @param string $_id Идентификатор аккаунта связанного с магазином
* @param array &$errors Реестр ошибок
*
* @return ?_document Инстанция документа магазина в базе данных, если найдена
*/
public static function market(string $_id, array &$errors = []): ?_document
{
try {
if (
collection::init(static::$arangodb->session, static::COLLECTION)
&& collection::init(static::$arangodb->session, market::COLLECTION)
&& collection::init(static::$arangodb->session, static::COLLECTION . '_edge_market', true)
) {
// Инициализированы коллекции
// Поиск магазина
$market = collection::search(
static::$arangodb->session,
sprintf(
<<<'AQL'
FOR d IN %s
LET e = (
FOR e IN %s
FILTER e._from == '%s'
SORT e.created DESC, e._key DESC
LIMIT 1
RETURN e
)
FILTER d._id == e[0]._to && d.active == true
SORT d.created DESC, d.id DESC
LIMIT 1
RETURN d
AQL,
market::COLLECTION,
static::COLLECTION . '_edge_market',
$_id
)
);
return $market instanceof _document ? $market : throw new exception('Не удалось найти инстанцию магазина в базе данных');
} else throw new exception('Не удалось инициализировать коллекции');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
/**
* Инициализировать связь аккаунта с сотрудником
*
* Ищет связь аккаунта с сотрудником, если не находит, то создаёт её
*
* @param string $account Идентификатор инстанции документа аккаунта в базе данных
* @param string $target Идентификатор инстанции документа цели в базе данны (подразумевается сотрудник или магазин)
* @param string $type Тип подключения (worker|market)
* @param array &$errors Реестр ошибок
*
* @return bool Связан аккаунт с сотрудником?
*/
public static function connect(string $account, string $target, string $type = 'worker', array &$errors = []): bool
{
try {
if (
collection::init(static::$arangodb->session, $type)
&& collection::init(static::$arangodb->session, self::COLLECTION)
&& collection::init(static::$arangodb->session, self::COLLECTION . "_edge_$type", true)
) {
// Инициализированы коллекции
if (
collection::search(static::$arangodb->session, sprintf(
<<<AQL
FOR d IN %s
FILTER d._from == '%s' && d._to == '%s'
SORT d.created DESC, d._key DESC
LIMIT 1
RETURN d
AQL,
self::COLLECTION . "_edge_$type",
$account,
$target
)) instanceof _document
|| document::write(static::$arangodb->session, self::COLLECTION . "_edge_$type", [
'_from' => $account,
'_to' => $target
])
) {
// Найдено, либо создано ребро: account -> $type
return true;
} else throw new exception("Не удалось создать ребро: account -> $type");
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return false;
}
/**
* Создать
*
* @param array $data Данные аккаунта
* @param array &$errors Реестр ошибок
*
* @return string|null Идентификатор документа, если он был создан
*/
public static function create(array $data = [], array &$errors = []): ?string
{
try {
if (collection::init(static::$arangodb->session, self::COLLECTION))
if ($id = document::write(static::$arangodb->session, self::COLLECTION, $data + ['active' => true])) return $id;
else throw new exception('Не удалось создать аккаунт');
else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в реестр ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
/**
* Записать
*
* Записывает свойство в инстанцию документа аккаунта из базы данных
*
* @param string $name Название
* @param mixed $value Содержимое
*
* @return void
*/
public function __set(string $name, mixed $value = null): void
{
$this->document->{$name} = $value;
}
/**
* Прочитать
*
* Читает свойство из инстанции документа аккаунта из базы данных
*
* @param string $name Название
*
* @return mixed Данные свойства инстанции аккаунта или инстанции документа аккаунта из базы данных
*/
public function __get(string $name): mixed
{
return $this->document->{$name};
}
/**
* Проверить инициализированность
*
* Проверяет инициализированность свойства в инстанции документа аккаунта из базы данных
*
* @param string $name Название
*
* @return bool Свойство инициализировано?
*/
public function __isset(string $name): bool
{
return isset($this->document->{$name});
}
/**
* Удалить
*
* Деинициализировать свойство в инстанции документа аккаунта из базы данных
*
* @param string $name Название
*
* @return void
*/
public function __unset(string $name): void
{
unset($this->document->{$name});
}
/**
* Выполнить метод
*
* Выполнить метод в инстанции документа аккаунта из базы данных
*
* @param string $name Название
* @param array $arguments Аргументы
*/
public function __call(string $name, array $arguments = [])
{
if (method_exists($this->document, $name)) return $this->document->{$name}($arguments);
}
}