Transit into git.mirzaev.sexya

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2022-11-06 10:47:54 +10:00
parent 6b56e51dd7
commit cccbf53008
12 changed files with 2023 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor

49
composer.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "mirzaev/vk-arangodb",
"description": "Module for mirzaev/vk framework for storage data in ArangoDB",
"keywords": [
"ArangoDB",
"vk",
"module"
],
"type": "vk-module",
"license": "WTFPL",
"homepage": "https://git.mirzaev.sexy/mirzaev/vk-arangodb",
"authors": [
{
"name": "Arsen Mirzaev Tatyano-Muradovich",
"email": "arsen@mirzaev.sexy",
"homepage": "https://mirzaev.sexy",
"role": "Programmer"
}
],
"support": {
"email": "arsen@mirzaev.sexy",
"wiki": "https://git.mirzaev.sexy/mirzaev/vk-arangodb/wiki",
"issues": "https://git.mirzaev.sexy/mirzaev/vk-arangodb/issues"
},
"funding": [
{
"type": "funding",
"url": "https://fund.mirzaev.sexy"
}
],
"require": {
"php": "^8.1",
"mirzaev/vk": "~4.0.1",
"mirzaev/accounts": "1.2.x-dev",
"mirzaev/arangodb": "^1.0.0",
"triagens/arangodb": "~3.9.x-dev",
"guzzlehttp/guzzle": "~7.5"
},
"autoload": {
"psr-4": {
"mirzaev\\vk\\arangodb\\": "mirzaev/vk/arangodb/system"
}
},
"autoload-dev": {
"psr-4": {
"mirzaev\\vk\\arangodb\\tests\\": "mirzaev/vk/arangodb/tests"
}
}
}

1013
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

1
mirzaev/vk/arangodb/system/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
storage/*

View File

@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb;
// Файлы проекта
use mirzaev\arangodb\collection;
// Фреймворк ArangoDB
use mirzaev\arangodb\document;
use mirzaev\vk\arangodb\longpoll;
// Библиотека для работы с API-сервера ArangoDB
use ArangoDBClient\Connection as _connection;
use ArangoDBClient\Statement as _statement;
use ArangoDBClient\Document as _document;
/**
* Журнал
*
* Используется для хранения информации об изменениях других коллекций
* Связывается с отслеживаемой коллекцией ребром
*
* @param _connection $session Сессия соединения с базой данных
* @param _document $document Инстанция (static::COLLECTION_JOURNAL)
*/
class journal
{
/**
* Настройки архитектуры базы данных
*/
const COLLECTION_JOURNAL = 'journal';
/**
* Конструктор
*
* @param _connection $session Сессия соединения с базой данных
* @param _document $document Инстанция (static::COLLECTION_JOURNAL)
*/
public function __construct(
public _connection $session,
public _document $document
) {
}
/**
* Инициализация
*
* @param _connection $session Сессия соединения с базой данных
* @param string $document Идентификатор документа
* @param bool $create Создавать коллекции при их отсутствии
*
* @return static|null Объект с инстанцией журнала
*/
public static function init(_connection $session, string $document, bool $create = true): ?static
{
if ($create) {
// Запрошено создание коллекций в случае их отсутствия
// Инициализация коллекций
collection::init($session, static::COLLECTION_JOURNAL);
collection::init($session, longpoll::COLLECTION_ACCESSED, true);
}
if (empty($journal = static::search($session, $document))) {
// Не найден журнал (подразумевается, что его не существует)
if (empty($journal = document::write($session, static::COLLECTION_JOURNAL, [
'events' => [],
'expires' => strtotime('first day of next month 00:00')
]))) {
// Не удалось создать документ
return null;
}
if (empty(document::write($session, longpoll::COLLECTION_ACCESSED, ['_from' => $document, '_to' => $journal]))) {
// Не удалось создать ребро: {$document} -> ЖУРНАЛ
return null;
}
}
// Инициализация инстанции журнала и возврат
return new static($session, $journal);
}
/**
* Поиск журнала
*
* Находит актуальный документ журнала
*
* @param _connection $session Сессия соединения с базой данных
* @param string $document Идентификатор документа
*
* @return _document|null Инстанция (static::COLLECTION_JOURNAL) или его идентификатор
*/
public static function search(_connection $session, string $document): _document|null
{
// Инициализация ярлыка названия коллекции
$collection = static::COLLECTION_JOURNAL;
// Инициализация ярлыка названия ребра
$edge = longpoll::COLLECTION_ACCESSED;
// Инициализация даты для разделения по журналам
$time = time();
// Поиск журнала
$journal = (new _statement(
$session,
[
'query' => <<<AQL
FOR a IN $collection
LET b = (
FOR vertex, edge IN INBOUND a $edge
FILTER vertex._id == $document
LIMIT 1
RETURN vertex
)
FILTER a.expires >= $time
LIMIT 1
RETURN a
AQL,
"batchSize" => 1000,
"sanitize" => true
]
))->execute();
// Инициализация буфера вывода
$buffer = [];
foreach ($journal as $key => $value) {
// Перебор параметров для универсализации вывода
// Запись в буфер
$buffer[$key] = $value;
}
// Возврат (подразумевается, что в буфере только 1 значение - инстанция (static::COLLECTION_JOURNAL))
return $buffer[0] ?? null;
}
/**
* Запись в журнал
*
* @param string $event Событие
* @param array $data Дополнительная информация
*
* @return bool Статус обработки
*/
public function write(string $event, array $data = []): bool
{
// Запись в инстанцию документа
$this->document->events += [
[
'date' => time(),
'event' => $event,
'data' => $data
]
];
// Запись в базу данных
return document::update($this->session, $this->document);
}
}

View File

@ -0,0 +1,430 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb;
// Файлы проекта
use mirzaev\arangodb\connection;
use mirzaev\arangodb\collection;
use mirzaev\arangodb\terminal;
use mirzaev\arangodb\document;
use mirzaev\vk\arangodb\traits\HTTP\headers\content\disposition;
// Библиотека для работы с API-сервера ArangoDB
use ArangoDBClient\Document as _document;
// Библиотека браузера
use GuzzleHttp\Client as Guzzle;
// Встроенные библиотеки
use Exception;
/**
* LongPoll API ВКонтакте
*
* @todo
* 1. Проработать создание индексов в базе данных
*/
class longpoll
{
use disposition {
disposition::filename as disposition_filename;
}
const COLLECTION_USERS = 'user';
const COLLECTION_GROUPS = 'group';
const COLLECTION_CHATS = 'chat';
const COLLECTION_MESSAGES = 'message';
const COLLECTION_ACCESSED = 'accessed';
public static string $path_storage = __DIR__ . '/storage';
public static string $path_storage_accounts = '/accounts';
public static string $path_storage_accounts_images = '/images';
public static string $path_storage_accounts_videos = '/videos';
public static string $path_storage_accounts_audios = '/audios';
public static string $path_storage_vk = '/vk';
public static string $path_storage_vk_stickers = '/stickers';
protected bool $journal = true;
public function __construct(protected connection $connection)
{
}
/**
* Сохранить событие в базу данных
*
* @param array $updates События
*
* @return bool Статус сохранения
*/
public function save(array $update): bool
{
try {
// Динамический вызов метода-обработчика события
if ($this->{$update['type']}($update['object'], $update['group_id'], $update['event_id'])) {
// Удалось сохранить в базу данных
return true;
}
} catch (Exception $e) {
terminal::write($e->getMessage() . PHP_EOL . $e->getFile() . ':' . $e->getLine());
}
return false;
}
/**
* Событие: "message_new"
*
* Новое сообщение
*
* @param array $data Данные сообщения
* @param int $group Идентификатор группы
* @param string $event Идентификатор события
*
* @return bool
*/
public function message_new(array $data, int $group, string $event): bool
{
if ($this->connection->create) {
// Запрошено создание коллекций в случае их отсутствия
collection::init($this->connection->session, static::COLLECTION_USERS);
collection::init($this->connection->session, static::COLLECTION_MESSAGES);
}
if ($message = document::write($this->connection->session, static::COLLECTION_MESSAGES, static::messages($data, true))) {
// Записано сообщение
// Инициализация коллекции
$collection = static::COLLECTION_MESSAGES;
// Инициализация аккаунтов
$from = ('mirzaev\\vk\\arangodb\\vk\\' . static::type($data['message']['from_id']))::init($this->connection->session, $data['message']['from_id']);
$to = ('mirzaev\\vk\\arangodb\\vk\\' . static::type($data['message']['peer_id']))::init($this->connection->session, $data['message']['peer_id']);
if ($from instanceof _document && $to instanceof _document) {
// Инициализированы аккаунты
if ($this->connection->create) {
// Запрошено создание коллекций в случае их отсутствия
collection::init($this->connection->session, static::COLLECTION_ACCESSED, edge: true);
}
if (document::write($this->connection->session, static::COLLECTION_ACCESSED, [], ['_from' => $from->getId(), '_to' => $message])) {
// Записано ребро: АККАУНТ (отправитель) -> СООБЩЕНИЕ
}
if (document::write($this->connection->session, static::COLLECTION_ACCESSED, [], ['_from' => $message, '_to' => $to->getId()])) {
// Записно ребро: СООБЩЕНИЕ -> АККАУНТ (получатель)
}
}
// Журналирование
if ($this->journal && journal::init($this->connection->session, $message)->write('create', [
'account' => $from,
'changes' => [
'new' => collection::search($this->connection->session, <<<AQL
FOR a IN $collection
FILTER a._id == $message
LIMIT 1
RETURN a.data
AQL),
'old' => null
]
])) {
// Записано ребро: СООБЩЕНИЕ -> СООБЩЕНИЕ
}
return true;
}
throw new Exception('Не удалось сохранить сообщение в базу даннных', 500);
}
/**
* Обработка сообщений
*
* @param array $messages Сообщения или сообщение
* @param bool $download Активация скачивания файлов на сервер
* @param bool $clean Активация очистки массива от пустых данных сообщения
*
* @return array Обработанные сообщения (зависит от входных данных)
*
* @todo
* 1. Переделать $message['vk']['metadata']['action']['cover']
* 2. Переделать $message['vk']['metadata']['payload']
* 3. Узнать про Notify API и добавить message_tag
* 4. В будущем удалить $message['message']['body']
* 5. Переделать $message['vk']['metadata']['conversation']['members']['amount_test'] (или удалить)
* 6. Разобраться с "Мультидиалогом" для старых версий API и существует ли он в новых
*/
public static function messages(array $messages, bool $download = false, bool $clean = true): array
{
if (isset($messages['message'])) {
// Передано одно сообщение
// Инициализация
$buffer[] = &$messages;
}
foreach ($buffer ?? $messages as &$message) {
// Перебор сообщений
$message = [
'data' => [
'id' => [
'global' => $message['message']['id'],
'local' => $message['message']['conversation_message_id']
],
'text' => $message['message']['text'] ?? $message['message']['body'],
'forward' => static::messages($message['message']['fwd_messages']),
'reply' => static::messages($message['message']['fwd_messages']),
'attachments' => static::attachments($message['message']['attachments'], $download, $clean)
],
'metadata' => [
'date' => [
'create' => $message['message']['date'] ?? null,
'update' => $message['message']['update_time'] ?? null
],
'action' => [
'type' => $message['message']['action'] ?? null,
'target' => [
'id' => $message['message']['action']['member_id'] ?? null
],
'text' => $message['message']['action']['text'] ?? null,
'email ' => $message['message']['action']['email'] ?? null,
'cover ' => $message['message']['action']['photo'] ?? null
],
'hash' => $message['message']['random_id'] ?? null,
'type' => $message['message']['out'] ?? null,
'admin' => [
'id' => $message['message']['admin_author_id'] ?? null
],
'pinned' => [
'date' => $message['message']['pinned_at'] ?? null
],
'emoji' => $message['message']['emoji'] ?? null,
'readed' => $message['message']['read_state'] ?? null,
'listened' => $message['message']['was_listened'] ?? null,
'hidden' => $message['message']['is_hidden'] ?? null,
'cropped' => $message['message']['is_cropped'] ?? null,
'deleted' => $message['message']['deleted'] ?? null,
'conversation' =>
[
'id' => $message['message']['chat_id'] ?? null,
'admin' => [
'id' => $message['message']['admin_id'] ?? null
],
'title' => $message['message']['title'] ?? null,
'members' => [
'amount' => $message['message']['members_count'] ?? null,
'amount_test' => $message['message']['users_count'] ?? null
],
'active' => $message['message']['chat_active'] ?? null,
'settings' => [
'push' => $message['message']['push_settings'] ?? null
]
],
'important' => $message['message']['important'] ?? null,
'source' => [
'from' => $message['message']['ref'] ?? null,
'data' => $message['message']['ref_source'] ?? null
],
'payload' => $message['message']['payload'] ?? null,
'geo' => [
$message['message']['geo'] ?? null
],
'keyboard' => [
'block' => $message['client_info']['keyboard'] ?? null,
'inline' => $message['client_info']['inline_keyboard'] ?? null,
'buttons' => $message['client_info']['button_actions'] ?? null,
],
'carousel' => $message['client_info']['carousel'] ?? null,
'language' => $message['client_info']['lang_id'] ?? null,
]
];
}
// Очистка массива, если активировано
$clean and static::cleaner($messages);
return $messages;
}
/**
* Обработка вложений
*
* @param array $attachments Вложения
* @param bool $download Активация скачивания файлов на сервер
* @param bool $clean Активация очистки массива от пустых данных сообщения
*
* @return array Обработанные вложения
*/
public static function attachments(array $attachments, bool $download = false, bool $clean = false): array
{
foreach ($attachments as &$attachment) {
// Перебор вложений
if ($attachment['type'] === 'photo') {
// Изображение
$attachment = [
'data' => [
'date' => $attachment['photo']['date'] ?? null,
'id' => $attachment['photo']['id'] ?? null,
'album' => [
'id' => $attachment['photo']['album_id'] ?? null
],
'account' => [
'id' => $attachment['photo']['user_id'] ?? null,
'admin' => $attachment['photo']['owner_id'] ?? null
],
'tags' => $attachment['photo']['has_tags'] ?? null,
'access' => [
'key' => $attachment['photo']['access_key'] ?? null
],
'text' => $attachment['photo']['text'] ?? null,
'storage' => static::sizes($attachment['photo']['sizes'], $download) ?? null
],
'metadata' => [
'type' => $attachment['type'] ?? null
]
];
}
}
// Очистка массива, если активировано
$clean and static::cleaner($attachments);
return $attachments;
}
/**
* Сортировка размеров файла из вложения
*
* @param array $sizes Размеры файла согласно спецификации в API
* @param bool $download Активация скачивания файлов на сервер
* @param int|null $id Идентификатор аккаунта кому принадлежат изображения
*
* @return array Обработанные размеры
*
* @see https://vk.com/dev/photo_sizes
*/
public static function sizes(array $sizes, bool $download = false, int|null $id = null): array
{
foreach ($sizes as &$size) {
// Перебор размеров
if ($download) {
// Запрошено сохранение на сервер
// Инициализация
$browser = new Guzzle();
// Инициализация директории
if (!file_exists($path = static::$path_storage . (isset($id) ? static::$path_storage_accounts . PHP_EOL . $id : static::$path_storage_vk) . static::$path_storage_accounts_images . PHP_EOL . date('Y_m_d', time())))
if (!mkdir($path, 0755, true))
throw new Exception('Не удалось инициализировать директорию: ' . $path);
// Генерация временного файла с уникальным дескриптором
$file = tempnam($path, '');
// Сохранение в файл
$request = $browser->get($size['url'], ['sink' => $file]);
var_dump($request->getHeaders());
die;
// Чтение расширения файла
$ext = $request->getHeader('Mime-Type');
// Перезапись
rename($file, dirname($file) . PHP_EOL . (static::disposition_filename($request->getHeader('Content-Disposition')[0]) ?? 1));
}
// Инициализация
$size = [
'data' => [
'width' => [
'value' => $size['width'],
'unit' => 'px'
],
'height' => [
'value' => $size['height'],
'unit' => 'px'
],
'source' => [
'vk' => $size['url']
]
],
'metadata' => [
'type' => $size['type']
]
];
}
return $sizes;
}
// Инициализация
public static function cleaner(array &$target): bool
{
// Инициализация
$changes = false;
foreach ($target as $key => &$value) {
// Перебор элементов массива
if ($value === null || $value === []) {
// Пустое значение
// Удаление из массива по ключу
unset($target[$key]);
// Запись обозначения о том, что были произведены изменения
$changes = true;
} else if (is_array($value)) {
// Элемент является массивом
// Начало рекурсии (повторяется до тех пор пока производятся изменения за итерацию)
while (static::cleaner($value));
}
}
return $changes;
}
public function truncate()
{
collection::truncate($this->connection->session, static::COLLECTION_USERS);
collection::truncate($this->connection->session, static::COLLECTION_GROUPS);
collection::truncate($this->connection->session, static::COLLECTION_CHATS);
collection::truncate($this->connection->session, static::COLLECTION_MESSAGES);
collection::truncate($this->connection->session, static::COLLECTION_ACCESSED);
collection::truncate($this->connection->session, journal::COLLECTION_JOURNAL);
}
/**
* Определить тип аккаунта
*
* @param int $id Идентификатор
*
* @return string Возвращает 'user', если не прошли другие проверки
*/
public static function type(int $id): string
{
// Чат
if ($id - 2000000000 >= 0) return static::COLLECTION_CHATS;
// Группа
if (((string) $id)[0] === '-') return static::COLLECTION_GROUPS;
return static::COLLECTION_USERS;
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb;
class terminal
{
protected const PREFIX = 'arangodb';
/**
* Запись в вывод
*
* @param string $text Текст сообщения
*/
public static function write(string $text): void
{
echo '[Модуль][' . self::PREFIX . '] ' . $text . PHP_EOL;
}
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\traits\HTTP\headers\content;
/**
* Обработка заголовка: "Content-Disposition"
*
* @see https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Content-Disposition
*/
trait disposition
{
/**
* Поиск значения filename
*
* @param string $raw Необработанное значение заголовка
*/
private static function filename(string $raw): ?string
{
// Инициализация
$raw = trim($raw);
if (strpos($raw, ';') === false) {
// Разделитель параметров не найден (пустая строка или отсутствие параметров)
return null;
}
// Инициализация
$params = [];
$_params = explode(';', $raw);
$type = $_params[0];
unset($_params[0]);
if (str_contains($type, 'attachment')) {
// В заголовке указано, что это скачиваемый файл
foreach ($_params as $param) {
if (str_contains($param, '=')) {
// Параметр подходит по маске: "текст=текст"
// Чтение и запись в переменные
[$key, $value] = explode('=', $param, 2);
// Запись в буфер
$params[trim($key)] = trim($value);
}
}
// Инициализация (порядок связан с приоритетом)
$masks = ['filename*' => true, 'filename' => false];
$targets = [];
foreach ($masks as $mask => $utf8) {
// Перебор значений масок
if (isset($params[$mask])) {
// Параметр из буфера прошел проверку по маске
// Инициализация
$target = trim($params[$mask]);
// Надо разобраться с этим
if ($utf8 && strpos($target, "utf-8''") === 0 && $target = substr($target, strlen("utf-8''")))
$target = rawurldecode($target);
// Надо разобраться с этим
if (substr($target, 0, 1) === '"' && substr($target, -1, 1) === '"')
$target = substr($target, 1, -1);
$targets[$mask] = $target;
}
}
// Возврат по приоритету
return $targets[$masks[0]] ?? $targets[$masks[1]];
}
return null;
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\traits\HTTP\headers\content;
/**
* Обработка заголовка: "Content-Type"
*
* @see https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Content-Type
*/
trait type
{
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\vk;
use mirzaev\vk\arangodb\longpoll;
use mirzaev\vk\arangodb\journal;
use mirzaev\arangodb\collection;
use mirzaev\arangodb\document;
use ArangoDBClient\Document as _document;
use ArangoDBClient\Connection as _connection;
class chat
{
/**
* Инициализация
*
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал
* @param bool $create Создавать коллекции при их отсутствии
*
* @return ?_document Инстанция документа
*/
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{
// Инициализация коллекции
$collection = longpoll::COLLECTION_CHATS;
if ($create) {
// Запрошено создание коллекций в случае их отсутствия
// Создание коллекции
collection::init($session, $collection);
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.data.data.id == $id
LIMIT 1
RETURN x.data
AQL)) {
// Найден аккаунт
return $account;
}
if ($account = document::write($session, $collection, ['data' => ['id' => $id]])) {
// Аккаунт записан
// Журналирование
if ($journal && journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, <<<AQL
FOR a IN $collection
FILTER a._id == $account
LIMIT 1
RETURN a.data
AQL),
'old' => null
]
])) {
// Записаны данные в журнал
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.data.data.id == $id
LIMIT 1
RETURN x
AQL)) {
// Найден аккаунт
return $account;
}
}
return null;
}
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\vk;
use mirzaev\vk\arangodb\longpoll;
use mirzaev\vk\arangodb\journal;
use mirzaev\arangodb\collection;
use mirzaev\arangodb\document;
use ArangoDBClient\Document as _document;
use ArangoDBClient\Connection as _connection;
class group
{
/**
* Инициализация
*
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал
* @param bool $create Создавать коллекции при их отсутствии
*
* @return ?_document Инстанция документа
*/
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{
// Инициализация коллекции
$collection = longpoll::COLLECTION_GROUPS;
if ($create) {
// Запрошено создание коллекций в случае их отсутствия
// Создание коллекции
collection::init($session, $collection);
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.data.data.id == $id
LIMIT 1
RETURN x.data
AQL)) {
// Найден аккаунт
return $account;
}
if ($account = document::write($session, $collection, ['data' => ['id' => $id]])) {
// Аккаунт записан
// Журналирование
if ($journal && journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, <<<AQL
FOR a IN $collection
FILTER a._id == $account
LIMIT 1
RETURN a.data
AQL),
'old' => null
]
])) {
// Записаны данные в журнал
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.data.data.id == $id
LIMIT 1
RETURN x
AQL)) {
// Найден аккаунт
return $account;
}
}
return null;
}
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\vk;
use mirzaev\vk\arangodb\longpoll;
use mirzaev\vk\arangodb\journal;
use mirzaev\arangodb\collection;
use mirzaev\arangodb\document;
use ArangoDBClient\Document as _document;
use ArangoDBClient\Connection as _connection;
class user
{
/**
* Инициализация
*
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал
* @param bool $create Создавать коллекции при их отсутствии
*
* @return ?_document Инстанция документа
*/
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{
// Инициализация коллекции
$collection = longpoll::COLLECTION_USERS;
if ($create) {
// Запрошено создание коллекций в случае их отсутствия
// Создание коллекции
collection::init($session, $collection);
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.id == $id
LIMIT 1
RETURN x
AQL)) {
// Найден аккаунт
return $account;
}
if ($account = document::write($session, $collection, ['id' => $id])) {
// Аккаунт записан
// Журналирование
if ($journal && journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, <<<AQL
FOR a IN $collection
FILTER a._id == $account
LIMIT 1
RETURN a.data
AQL),
'old' => null
]
])) {
// Записаны данные в журнал
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.data.data.id == $id
LIMIT 1
RETURN x
AQL)) {
// Найден аккаунт
return $account;
}
}
return null;
}
}