created cart system

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2024-10-21 23:07:26 +03:00
parent ac7694f716
commit 8efce7d6e6
21 changed files with 549 additions and 165 deletions

View File

@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\arming_bot\controllers\core,
mirzaev\arming_bot\models\product,
mirzaev\arming_bot\models\cart as model;
// Framework for ArangoDB
use mirzaev\arangodb\document;
/**
* Controller of cart
*
* @package mirzaev\arming_bot\controllers
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class cart extends core
{
/**
* Registry of errors
*/
protected array $errors = [
'session' => [],
'account' => [],
'cart' => []
];
/**
* Write or delete from the cart
*
* @param array $parameters Parameters of the request (POST + GET)
*/
public function write(array $parameters = []): ?string
{
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// POST request
// The cart contains the product?
$status = false;
// Validating @todo add throwing errors
if (!empty($parameters['product']) && preg_match('/[\d]+/', urldecode($parameters['product']), $matches)) $product = (int) $matches[0];
if (isset($product)) {
// Received and validated identfier of the product
// Search for the product
$product = product::read(
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
sort: 'd.created DESC',
amount: 1,
parameters: ['identifier' => $product],
errors: $this->errors['cart']
);
if ($product instanceof product) {
// Initialized the product
// Initializing of the cart
$cart = $this->session->cart(errors: $this->errors['cart']);
if ($cart instanceof model) {
// Initialized the cart
if (0 < $amount = $cart->has($product, errors: $this->errors['cart'])) {
// The cart contains the product
// temporary
/* $cart->disconnect($product, errors: $this->errors['cart']); */
$cart->cancel($product, errors: $this->errors['cart']);
$status = false;
} else {
// The cart not contains the product
// temporary
$cart->connect($product, errors: $this->errors['cart']);
$status = true;
}
}
}
}
// Initializing a response headers
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Initializing of the output buffer
ob_start();
// Generating the reponse
echo json_encode(
[
'status' => $status ?? false,
'errors' => $this->errors
]
);
// Initializing a response headers
header('Content-Length: ' . ob_get_length());
// Sending and deinitializing of the output buffer
ob_end_flush();
flush();
// Exit (success)
return null;
}
// Exit (fail)
return null;
}
}

View File

@ -42,61 +42,11 @@ final class catalog extends core
*/ */
public function index(array $parameters = []): ?string public function index(array $parameters = []): ?string
{ {
// Validating // validating
if (!empty($parameters['category']) && preg_match('/[\d]+/', $parameters['category'], $matches)) $category = (int) $matches[0];
if (isset($category)) {
// Received and validated identifier of the category
// Initialize of category
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => $category], errors: $this->errors['catalog']);
if ($category instanceof category) {
// Found the category
// Write to the response buffer
$response['category'] = ['name' => $category->name ?? null];
// Search for categories that are descendants of $category
$entries = entry::search(
document: $category,
amount: 50,
errors: $this->errors['catalog']
);
/* var_dump($entries); die; */
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
$category = $product = [];
foreach ($entries as $entry) {
// Iterate over entries (descendands)
// Write entry to the buffer of entries (sort by $category and $product)
${$entry->_type}[] = $entry;
}
// Write to the buffer of global variables of view templater
$this->view->categories = $category ?? null;
// Write to the buffer of global variables of view templater
$this->view->products = $product ?? null;
/* var_dump($this->view->products); die; */
// Delete buffers
unset($category, $product);
}
} else if (!isset($parameters['category'])) {
// Not received identifie of the category
// Search for root ascendants categories
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']) ?? null;
}
// Validating
if (!empty($parameters['product']) && preg_match('/[\d]+/', $parameters['product'], $matches)) $product = (int) $matches[0]; if (!empty($parameters['product']) && preg_match('/[\d]+/', $parameters['product'], $matches)) $product = (int) $matches[0];
if (isset($product)) { if (isset($product)) {
// Received and validated identifier of the product // received and validated identifier of the product
// Search for the product data and write to the buffer of global variables of view templater // Search for the product data and write to the buffer of global variables of view templater
$this->view->product = product::read( $this->view->product = product::read(
@ -116,14 +66,6 @@ final class catalog extends core
// Initializing buffer of filters query (AQL) // Initializing buffer of filters query (AQL)
$_filters = 'd.deleted != true && d.hidden != true'; $_filters = 'd.deleted != true && d.hidden != true';
// Search among products in the $category
if (isset($this->view->products) && count($this->view->products) > 0) {
// Amount of rendered products is more than 0
// Write to the buffer of filters query (AQL)
$_filters .= ' && POSITION(["' . implode('", "', array_map(fn(_document $document): string => $document->getId(), $this->view->products)) . '"], d._id)';
}
// Validating // Validating
if (!empty($parameters['brand']) && preg_match('/[\w\s]+/u', urldecode($parameters['brand']), $matches)) $brand = $matches[0]; if (!empty($parameters['brand']) && preg_match('/[\w\s]+/u', urldecode($parameters['brand']), $matches)) $brand = $matches[0];
@ -264,18 +206,6 @@ final class catalog extends core
] ]
] ]
); );
// Search for products and write to the buffer of global variables of view templater
$this->view->products = product::read(
search: $text ?? null,
filter: $_filters,
sort: $_sort,
amount: 50, // @todo pagination
language: $this->language,
parameters: $_parameters,
return: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language})',
errors: $this->errors['catalog']
);
} else { } else {
// Not received or not validated filter by brand // Not received or not validated filter by brand
@ -302,6 +232,62 @@ final class catalog extends core
); );
} }
// Validating
if (!empty($parameters['category']) && preg_match('/[\d]+/', $parameters['category'], $matches)) $category = (int) $matches[0];
if (isset($category)) {
// Received and validated identifier of the category
// Initialize of category
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => $category], errors: $this->errors['catalog']);
if ($category instanceof category) {
// Found the category
// Write to the response buffer
$response['category'] = ['name' => $category->name ?? null];
// Search for categories that are descendants of $category
$entries = entry::search(
document: $category,
amount: 50,
errors: $this->errors['catalog']
);
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
$category = $product = [];
foreach ($entries as $entry) {
// Iterate over entries (descendands)
// Write entry to the buffer of entries (sort by $category and $product)
${$entry->_type}[] = $entry;
}
// Write to the buffer of global variables of view templater
$this->view->categories = $category;
// Write to the buffer of global variables of view templater
$this->view->products = $product;
if (isset($this->view->products) && count($this->view->products) > 0) {
// Amount of rendered products is more than 0
// Write to the buffer of filters query (AQL)
$_filters .= ' && POSITION(["' . implode('", "', array_map(fn(_document $document): string => $document->getId(), $this->view->products)) . '"], d._id)';
}
// Deleting buffers
unset($category, $product);
}
} else if (!isset($parameters['category'])) {
// Not received identifier of the category
// search for root ascendants categories
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']) ?? null;
}
// Search among products in the $category
if (isset($this->view->products) && count($this->view->products) > 0) { if (isset($this->view->products) && count($this->view->products) > 0) {
// Amount of rendered products is more than 0 // Amount of rendered products is more than 0
@ -314,6 +300,18 @@ final class catalog extends core
errors: $this->errors['catalog'] errors: $this->errors['catalog']
) )
]; ];
// Search for products and write to the buffer of global variables of view templater
$this->view->products = product::read(
search: $text ?? null,
filter: $_filters,
sort: $_sort,
amount: 50, // @todo pagination
language: $this->language,
parameters: $_parameters,
return: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language})',
errors: $this->errors['catalog']
);
} }
if (isset($menu)) { if (isset($menu)) {
@ -347,7 +345,9 @@ final class catalog extends core
} }
// Exit (success) // Exit (success)
return $this->view->render('catalog/page.html'); return $this->view->render('catalog/page.html', [
'h2' => $this->language === language::ru ? 'Каталог' : 'Catalog' // @see https://git.mirzaev.sexy/mirzaev/huesos/issues/1
]);
} else if ($_SERVER['REQUEST_METHOD'] === 'POST') { } else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// POST request // POST request

View File

@ -119,8 +119,8 @@ class core extends controller
$this->settings = settings::active(); $this->settings = settings::active();
// Initializing of language // Initializing of language
if ($this->account?->language) $this->language = $this->account->language ?? language::en->name; if ($this->account?->language) $this->language = $this->account->language ?? language::en;
else if ($this->settings?->language) $this->language = $this->settings->language ?? language::en->name; else if ($this->settings?->language) $this->language = $this->settings->language ?? language::en;
// Initializing of preprocessor of views // Initializing of preprocessor of views
$this->view = new templater( $this->view = new templater(

View File

@ -161,6 +161,8 @@ final class session extends core
* @param array $parameters Parameters of the request (POST + GET) * @param array $parameters Parameters of the request (POST + GET)
* *
* @return void * @return void
*
* @todo переделать под trait buffer
*/ */
public function write(array $parameters = []): void public function write(array $parameters = []): void
{ {

View File

@ -7,9 +7,10 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\status, mirzaev\arming_bot\models\traits\status,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
mirzaev\arming_bot\models\traits\buffer, mirzaev\arming_bot\models\traits\buffer,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language; mirzaev\arming_bot\models\enumerations\language;
// Framework for ArangoDB // Framework for ArangoDB
@ -33,9 +34,9 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class account extends core implements arangodb_document_interface final class account extends core implements document_interface, collection_interface
{ {
use status, arangodb_document_trait, buffer { use status, document_trait, buffer {
buffer::write as write; buffer::write as write;
} }
@ -77,20 +78,20 @@ final class account extends core implements arangodb_document_interface
// Initialized the account // Initialized the account
// Initializing the object // Initializing the object
$account = new account; $account = new static;
if (method_exists($account, '__document')) { if (method_exists($account, '__document')) {
// Object can implement a document from ArangoDB // Object can implement a document from ArangoDB
// Abstractioning of parameters // Abstractioning of parameters
$result->language = language::{$result->language} ?? 'en'; if (isset($result->language)) $result->language = language::{$result->language};
// Writing the instance of account document from ArangoDB to the implement object // Writing the instance of account document from ArangoDB to the implement object
$account->__document($result); $account->__document($result);
// Exit (success) // Exit (success)
return $account; return $account;
} } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB');
} else if ($registration) { } else if ($registration) {
// Not found the account and registration is requested // Not found the account and registration is requested

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
/**
* Model of cart
*
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class cart extends core implements document_interface, collection_interface
{
use document_trait;
/**
* Name of the collection in ArangoDB
*/
final public const string COLLECTION = 'cart';
}

View File

@ -6,8 +6,9 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for ArangoDB // Framework for ArangoDB
use mirzaev\arangodb\collection, use mirzaev\arangodb\collection,
@ -25,9 +26,9 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class category extends core implements arangodb_document_interface final class category extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB

View File

@ -6,8 +6,9 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for ArangoDB // Framework for ArangoDB
use mirzaev\arangodb\enumerations\collection\type; use mirzaev\arangodb\enumerations\collection\type;
@ -20,9 +21,9 @@ use mirzaev\arangodb\enumerations\collection\type;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class connect extends core implements arangodb_document_interface final class connect extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB

View File

@ -4,9 +4,6 @@ declare(strict_types=1);
namespace mirzaev\arming_bot\models; namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for PHP // Framework for PHP
use mirzaev\minimal\model; use mirzaev\minimal\model;
@ -28,7 +25,7 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
class core extends model implements collection_interface class core extends model
{ {
/** /**
* Postfix for name of models files * Postfix for name of models files

View File

@ -6,8 +6,9 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Library for ArangoDB // Library for ArangoDB
use ArangoDBClient\Document as _document; use ArangoDBClient\Document as _document;
@ -28,9 +29,9 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class entry extends core implements arangodb_document_interface final class entry extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB

View File

@ -6,9 +6,9 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
/** /**
* Model of menu * Model of menu
@ -18,9 +18,9 @@ use mirzaev\arming_bot\models\core,
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class menu extends core implements arangodb_document_interface final class menu extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB

View File

@ -7,12 +7,17 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\enumerations\language, mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait; mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for ArangoDB // Framework for ArangoDB
use mirzaev\arangodb\collection, use mirzaev\arangodb\collection,
mirzaev\arangodb\document; mirzaev\arangodb\document;
// Library for ArangoDB
use ArangoDBClient\Document as _document;
// Built-in libraries // Built-in libraries
use exception; use exception;
@ -24,9 +29,9 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class product extends core final class product extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB
@ -118,11 +123,11 @@ final class product extends core
* @param int $page Page * @param int $page Page
* @param int $amount Amount per page * @param int $amount Amount per page
* @param string|null $return Return (AQL) * @param string|null $return Return (AQL)
* @param language $language Language code (en, ru...) * @param language|null $language Language code (en, ru...)
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter] * @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
* @param array &$errors Registry of errors * @param array &$errors Registry of errors
* *
* @return array Found products (can be empty) * @return array|static Found products or instance of the product from ArangoDB (can be empty)
*/ */
public static function read( public static function read(
?string $search = null, ?string $search = null,
@ -131,22 +136,25 @@ final class product extends core
int $page = 1, int $page = 1,
int $amount = 100, int $amount = 100,
?string $return = 'DISTINCT d', ?string $return = 'DISTINCT d',
language $language = language::en, ?language $language = null,
array $parameters = [], array $parameters = [],
array &$errors = [] array &$errors = []
): array { ): array|static {
try { try {
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
// Initialized the collection // Initialized the collection
// Initializing of the language parameter
if ($language instanceof language) $parameters['language'] = $language->name;
// Initializing parameters for search // Initializing parameters for search
if ($search) $parameters += [ if ($search) $parameters += [
'search' => $search, 'search' => $search,
'analyzer' => 'text_' . $language->name 'analyzer' => 'text_' . $language->name ?? language::en->name
]; ];
// Reading products // Search for products
$documents = collection::execute( $result = collection::execute(
sprintf( sprintf(
<<<'AQL' <<<'AQL'
FOR d IN @@collection %s FOR d IN @@collection %s
@ -182,19 +190,31 @@ final class product extends core
), ),
[ [
'@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search', '@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search',
'language' => $language->name,
'offset' => --$page <= 0 ? $page = 0 : $page * $amount, 'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
'amount' => $amount, 'amount' => $amount,
] + $parameters, ] + $parameters,
errors: $errors errors: $errors
); );
if ($documents) { if ($result instanceof _document) {
// Found products // Found product
// Initializing the object
$product = new static;
if (method_exists($product, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of product document from ArangoDB to the implement object
$product->__document($result);
// Exit (success) // Exit (success)
return is_array($documents) ? $documents : [$documents]; return $product;
} else return []; } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB');
}
// Exit (success)
return $result ?? [];
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) { } catch (exception $e) {
// Writing to the registry of errors // Writing to the registry of errors

View File

@ -7,11 +7,13 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\account, use mirzaev\arming_bot\models\account,
mirzaev\arming_bot\models\connect, mirzaev\arming_bot\models\connect,
mirzaev\arming_bot\models\cart,
mirzaev\arming_bot\models\enumerations\session as verification, mirzaev\arming_bot\models\enumerations\session as verification,
mirzaev\arming_bot\models\traits\status, mirzaev\arming_bot\models\traits\status,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
mirzaev\arming_bot\models\traits\buffer, mirzaev\arming_bot\models\traits\buffer,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language; mirzaev\arming_bot\models\enumerations\language;
// Framework for ArangoDB // Framework for ArangoDB
@ -32,9 +34,9 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class session extends core implements arangodb_document_interface final class session extends core implements document_interface, collection_interface
{ {
use status, arangodb_document_trait, buffer { use status, document_trait, buffer {
buffer::write as write; buffer::write as write;
} }
@ -148,6 +150,7 @@ final class session extends core implements arangodb_document_interface
$result = collection::execute( $result = collection::execute(
<<<AQL <<<AQL
FOR v IN INBOUND @session GRAPH sessions FOR v IN INBOUND @session GRAPH sessions
FILTER IS_SAME_COLLECTION(account, v._id)
SORT v.created DESC SORT v.created DESC
LIMIT 1 LIMIT 1
RETURN v RETURN v
@ -159,7 +162,7 @@ final class session extends core implements arangodb_document_interface
); );
if ($result instanceof _document) { if ($result instanceof _document) {
// Found active settings // Found the account
// Initializing the object // Initializing the object
$account = new account; $account = new account;
@ -168,14 +171,14 @@ final class session extends core implements arangodb_document_interface
// Object can implement a document from ArangoDB // Object can implement a document from ArangoDB
// Abstractioning of parameters // Abstractioning of parameters
$result->language = language::{$result->language} ?? 'en'; if (isset($result->language)) $result->language = language::{$result->language};
// Writing the instance of account document from ArangoDB to the implement object // Writing the instance of account document from ArangoDB to the implement object
$account->__document($result); $account->__document($result);
// Exit (success) // Exit (success)
return $account; return $account;
} } else throw new exception('Class ' . account::class . ' does not implement a document from ArangoDB');
} else return null; } else return null;
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION); } else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION); } else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
@ -195,34 +198,97 @@ final class session extends core implements arangodb_document_interface
} }
/** /**
* Connect account to session * Search for a connected cart
* *
* @param account $account Account
* @param array &$errors Registry of errors * @param array &$errors Registry of errors
* *
* @return string|null The identifier of the created edge of the "connect" collection, if created * @return cart|null An object implements the instance of the cart document from ArangoDB, if found
*/ */
public function connect(account $account, array &$errors = []): ?string public function cart(array &$errors = []): ?cart
{ {
try { try {
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) { if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) {
if (collection::initialize(account::COLLECTION, account::TYPE, errors: $errors)) { if (collection::initialize(cart::COLLECTION, cart::TYPE, errors: $errors)) {
// Collections initialized // Initialized collections
// The instance of the session document from ArangoDB is initialized? // Search for connected cart
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized'); $result = collection::execute(
<<<AQL
// Writing document and exit (success) FOR v IN INBOUND @session GRAPH sessions
return document::write( FILTER IS_SAME_COLLECTION(cart, v._id) && v.active == true && v.ordered != true
connect::COLLECTION, SORT v.updated DESC, v.created DESC
LIMIT 1
RETURN v
AQL,
[ [
'_from' => $account->getId(), 'session' => $this->getId()
'_to' => $this->document->getId()
], ],
errors: $errors errors: $errors
); );
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
if ($result instanceof _document) {
// Found the cart
// Initializing the object
$cart = new cart;
if (method_exists($cart, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of cart document from ArangoDB to the implement object
$cart->__document($result);
// Exit (success)
return $cart;
}
} else {
// Not found the cart
// Initializing a new cart and write they into ArangoDB
$_id = document::write(
cart::COLLECTION,
[
'active' => true,
]
);
if ($result = collection::execute(
<<<'AQL'
FOR d IN @@collection
FILTER d._id == @_id && d.active == true
RETURN d
AQL,
[
'@collection' => cart::COLLECTION,
'_id' => $_id
],
errors: $errors
)) {
// Found the instance of just created new cart
// Initializing the object
$cart = new cart;
if (method_exists($cart, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of cart document from ArangoDB to the implement object
$cart->__document($result);
// Connecting the cart to the session
$connected = $this->connect($cart, $errors);
if ($connected) {
// The cart has been connected to the session
// Exit (success)
return $cart;
} else throw new exception('Failed to connect the cart to the session');
} else throw new exception('Class ' . cart::class . ' does not implement a document from ArangoDB');
} else throw new exception('Failed to create or find just created session');
}
} else throw new exception('Failed to initialize ' . cart::TYPE . ' collection: ' . cart::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION); } else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) { } catch (exception $e) {

View File

@ -6,15 +6,16 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface, mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language; mirzaev\arming_bot\models\enumerations\language;
// Framework for ArangoDB // Framework for ArangoDB
use mirzaev\arangodb\collection, use mirzaev\arangodb\collection,
mirzaev\arangodb\document; mirzaev\arangodb\document;
// Library для ArangoDB // Library for ArangoDB
use ArangoDBClient\Document as _document; use ArangoDBClient\Document as _document;
// Built-in libraries // Built-in libraries
@ -28,9 +29,9 @@ use exception;
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class settings extends core implements arangodb_document_interface final class settings extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB
@ -76,14 +77,14 @@ final class settings extends core implements arangodb_document_interface
// Object can implement a document from ArangoDB // Object can implement a document from ArangoDB
// Abstractioning of parameters // Abstractioning of parameters
$result->language = language::{$result->language} ?? 'en'; if (isset($result->language)) $result->language = language::{$result->language};
// Writing the instance of settings document from ArangoDB to the implement object // Writing the instance of settings document from ArangoDB to the implement object
$settings->__document($result); $settings->__document($result);
// Exit (success) // Exit (success)
return $settings; return $settings;
} } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB');
} else if ($create) { } else if ($create) {
// Not found active settings and requested their creating // Not found active settings and requested their creating

View File

@ -6,9 +6,10 @@ namespace mirzaev\arming_bot\models;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core, use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\enumerations\language, mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\traits\document as arangodb_document_trait, mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface; mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language;
// Framework for ArangoDB // Framework for ArangoDB
use mirzaev\arangodb\collection, use mirzaev\arangodb\collection,
@ -29,9 +30,9 @@ use exception,
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/ */
final class suspension extends core implements arangodb_document_interface final class suspension extends core implements document_interface, collection_interface
{ {
use arangodb_document_trait; use document_trait;
/** /**
* Name of the collection in ArangoDB * Name of the collection in ArangoDB
@ -81,7 +82,7 @@ final class suspension extends core implements arangodb_document_interface
// Exit (success) // Exit (success)
return $suspension; return $suspension;
} } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB');
} else return null; } else return null;
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) { } catch (exception $e) {

View File

@ -5,13 +5,17 @@ declare(strict_types=1);
namespace mirzaev\arming_bot\models\traits; namespace mirzaev\arming_bot\models\traits;
// Files of the project // Files of the project
use mirzaev\arming_bot\models\core; use mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\connect;
// Library для ArangoDB // Library для ArangoDB
use ArangoDBClient\Document as _document; use ArangoDBClient\Document as _document;
// Framework for ArangoDB // Framework for ArangoDB
use mirzaev\arangodb\connection as arangodb; use mirzaev\arangodb\connection as arangodb,
mirzaev\arangodb\document as framework_document,
mirzaev\arangodb\collection;
// Built-in libraries // Built-in libraries
use exception; use exception;
@ -19,6 +23,8 @@ use exception;
/** /**
* Trait for implementing a document instance from ArangoDB * Trait for implementing a document instance from ArangoDB
* *
* @uses document_interface
*
* @var protected readonly _document|null $document An instance of the ArangoDB document * @var protected readonly _document|null $document An instance of the ArangoDB document
* *
* @package mirzaev\arming_bot\models\traits * @package mirzaev\arming_bot\models\traits
@ -71,6 +77,52 @@ trait document
return $this->document ?? null; return $this->document ?? null;
} }
/**
* Connect
*
* @param collecton_interface $document Document
* @param array &$errors Registry of errors
*
* @return string|null The identifier of the created edge of the "connect" collection, if created
*/
public function connect(collection_interface $document, array &$errors = []): ?string
{
try {
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) {
if (collection::initialize($document::COLLECTION, $document::TYPE, errors: $errors)) {
// Initialized collections
if ($this->document instanceof _document) {
// Initialized instance of the document from ArangoDB
// Writing document and exit (success)
return framework_document::write(
connect::COLLECTION,
[
'_from' => $document->getId(),
'_to' => $this->document->getId()
],
errors: $errors
);
} else throw new exception('The instance of the document from ArangoDB is not initialized');
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/** /**
* Write * Write
* *

View File

@ -40,6 +40,8 @@ $router = new router;
$router $router
->write('/', 'catalog', 'index', 'GET') ->write('/', 'catalog', 'index', 'GET')
->write('/', 'catalog', 'index', 'POST') ->write('/', 'catalog', 'index', 'POST')
->write('/cart', 'cart', 'index', 'GET')
->write('/cart/write', 'cart', 'write', 'POST')
->write('/account/write', 'account', 'write', 'POST') ->write('/account/write', 'account', 'write', 'POST')
->write('/session/write', 'session', 'write', 'POST') ->write('/session/write', 'session', 'write', 'POST')
->write('/session/connect/telegram', 'session', 'telegram', 'POST') ->write('/session/connect/telegram', 'session', 'telegram', 'POST')

View File

@ -35,9 +35,90 @@ import("/js/core.js").then(() =>
*/ */
core.cart = class cart { core.cart = class cart {
/** /**
* Products in cart ["product/148181", "product/148181", "product/148181"...] * Write or delete product from the cart (interface)
*
* @param {HTMLElement} button Button of the product <a>
* @param {bool} force Ignore the damper? (false)
*
* @return {bool} True if an error occurs to continue the event execution
*/ */
static cart = []; static toggle(button, force = false) {
// Blocking the button
button.setAttribute("disabled", "true");
// Execute under damper
this.toggle_damper(button, force);
// Exit (success)
return false;
}
/**
* Write or delete product from the cart (damper)
*
* @param {HTMLElement} button Button of the product <a>
* @param {bool} force Ignore the damper? (false)
*
* @return {void}
*/
static toggle_damper = core.damper(
(...variables) => this.toggle_system(...variables),
800,
1,
);
/**
* Write or delete product from the cart (system)
*
* @param {HTMLElement} button Button of the product <a>
*
* @return {Promise} Request to the server
*
* @todo add unblocking button by timer + everywhere
*/
static async toggle_system(button) {
if (button instanceof HTMLElement) {
// Validated
// Initializing of identifier of the product
const identifier = button.getAttribute("data-product-identifier");
if (typeof identifier === "string" && identifier.length > 0) {
// Validated identifier
return await core.request(
"/cart/write",
`product=${identifier}`,
)
.then((json) => {
if (
json.errors !== null &&
typeof json.errors === "object" &&
json.errors.length > 0
) {
// Fail (received errors)
} else {
// Success (not received errors)
// Unblocking the button
button.removeAttribute("disabled");
if (json.status) {
// The product in the cart
// Writing style of added to the cart button
button.classList.add("cart");
} else {
// The product is not in the cart
// Deleting style of added to the cart button
button.classList.remove("cart");
}
}
});
}
}
}
}; };
} }
} }

View File

@ -797,7 +797,7 @@ import("/js/core.js").then(() =>
* *
* @return {Promise} Request to the server * @return {Promise} Request to the server
*/ */
static product_system(identifier) { static async product_system(identifier) {
if (typeof identifier === "string") { if (typeof identifier === "string") {
// Validated identifier // Validated identifier
@ -807,7 +807,7 @@ import("/js/core.js").then(() =>
// Write parameter to the buffer of URN parameters // Write parameter to the buffer of URN parameters
parameters.set("product", identifier); parameters.set("product", identifier);
return core.request("?" + parameters).then((json) => { return await core.request("?" + parameters).then((json) => {
if ( if (
json.errors !== null && json.errors !== null &&
typeof json.errors === "object" && typeof json.errors === "object" &&

View File

@ -137,6 +137,10 @@ main>section#products>div.column>article.product>a>p.title {
background-color: var(--tg-theme-secondary-bg-color); background-color: var(--tg-theme-secondary-bg-color);
} }
main>section#products>div.column>article.product>button.cart {
filter: hue-rotate(120deg);
}
main>section#products>div.column>article.product>button:last-of-type { main>section#products>div.column>article.product>button:last-of-type {
z-index: 100; z-index: 100;
height: 33px; height: 33px;

View File

@ -1,13 +1,17 @@
{% macro card(product) %} {% macro card(product) %}
{% set title = product.name[language] ~ ' ' ~ product.brand[language] ~ format_dimensions(product.dimensions.x, product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' %} {% set title = product.name[language] ~ ' ' ~ product.brand[language] ~ format_dimensions(product.dimensions.x,
product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' %}
<article id="{{ product.getId() }}" class="product unselectable"> <article id="{{ product.getId() }}" class="product unselectable">
<a data-product-identifier="{{ product.identifier }}" href="?product={{ product.identifier }}" onclick="return core.catalog.product(this);" onkeydown="event.keyCode === 13 && core.catalog.product(this)" tabindex="10"> <a data-product-identifier="{{ product.identifier }}" href="?product={{ product.identifier }}"
onclick="return core.catalog.product(this);" onkeydown="event.keyCode === 13 && core.catalog.product(this)"
tabindex="10">
<img src="{{ product.images.0.storage }}" alt="{{ product.name[language] }}" ondrugstart="return false;"> <img src="{{ product.images.0.storage }}" alt="{{ product.name[language] }}" ondrugstart="return false;">
<p class="title" title="{{ product.name[language] }}"> <p class="title" title="{{ product.name[language] }}">
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }} {{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
</p> </p>
</a> </a>
<button title="Добавить в корзину" onclick="catalog.cart.add(this)" tabindex="15"> <button title="Добавить в корзину" onclick="core.cart.toggle(this)" data-product-identifier="{{ product.identifier }}"
data-product-cost="{{ product.cost }}" tabindex="15">
{{ product.cost }}р {{ product.cost }}р
</button> </button>
</article> </article>