From 8efce7d6e61925afb0f90524e04226f35c16014a Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Mon, 21 Oct 2024 23:07:26 +0300 Subject: [PATCH] created cart system --- .../arming_bot/system/controllers/cart.php | 121 ++++++++++++++ .../arming_bot/system/controllers/catalog.php | 148 +++++++++--------- .../arming_bot/system/controllers/core.php | 4 +- .../arming_bot/system/controllers/session.php | 2 + mirzaev/arming_bot/system/models/account.php | 15 +- mirzaev/arming_bot/system/models/cart.php | 29 ++++ mirzaev/arming_bot/system/models/category.php | 9 +- mirzaev/arming_bot/system/models/connect.php | 9 +- mirzaev/arming_bot/system/models/core.php | 5 +- mirzaev/arming_bot/system/models/entry.php | 9 +- mirzaev/arming_bot/system/models/menu.php | 10 +- mirzaev/arming_bot/system/models/product.php | 52 ++++-- mirzaev/arming_bot/system/models/session.php | 112 ++++++++++--- mirzaev/arming_bot/system/models/settings.php | 15 +- .../arming_bot/system/models/suspension.php | 13 +- .../system/models/traits/document.php | 56 ++++++- mirzaev/arming_bot/system/public/index.php | 2 + mirzaev/arming_bot/system/public/js/cart.js | 85 +++++++++- .../arming_bot/system/public/js/catalog.js | 4 +- .../public/themes/default/css/catalog.css | 4 + .../default/catalog/elements/products.html | 10 +- 21 files changed, 549 insertions(+), 165 deletions(-) create mode 100755 mirzaev/arming_bot/system/controllers/cart.php create mode 100755 mirzaev/arming_bot/system/models/cart.php diff --git a/mirzaev/arming_bot/system/controllers/cart.php b/mirzaev/arming_bot/system/controllers/cart.php new file mode 100755 index 0000000..a3b539a --- /dev/null +++ b/mirzaev/arming_bot/system/controllers/cart.php @@ -0,0 +1,121 @@ + + */ +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; + } +} diff --git a/mirzaev/arming_bot/system/controllers/catalog.php b/mirzaev/arming_bot/system/controllers/catalog.php index eb1fda1..e95eb97 100755 --- a/mirzaev/arming_bot/system/controllers/catalog.php +++ b/mirzaev/arming_bot/system/controllers/catalog.php @@ -42,64 +42,14 @@ final class catalog extends core */ public function index(array $parameters = []): ?string { - // 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 + // validating if (!empty($parameters['product']) && preg_match('/[\d]+/', $parameters['product'], $matches)) $product = (int) $matches[0]; 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 - $this->view->product = product::read( + $this->view->product = product::read( filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true", sort: 'd.created DESC', amount: 1, @@ -116,14 +66,6 @@ final class catalog extends core // Initializing buffer of filters query (AQL) $_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 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 { // 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) { // Amount of rendered products is more than 0 @@ -314,6 +300,18 @@ final class catalog extends core 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)) { @@ -347,7 +345,9 @@ final class catalog extends core } // 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') { // POST request diff --git a/mirzaev/arming_bot/system/controllers/core.php b/mirzaev/arming_bot/system/controllers/core.php index 954599b..22e59a2 100755 --- a/mirzaev/arming_bot/system/controllers/core.php +++ b/mirzaev/arming_bot/system/controllers/core.php @@ -119,8 +119,8 @@ class core extends controller $this->settings = settings::active(); // Initializing of language - if ($this->account?->language) $this->language = $this->account->language ?? language::en->name; - else if ($this->settings?->language) $this->language = $this->settings->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; // Initializing of preprocessor of views $this->view = new templater( diff --git a/mirzaev/arming_bot/system/controllers/session.php b/mirzaev/arming_bot/system/controllers/session.php index 3a6e8ce..5ac2ce7 100755 --- a/mirzaev/arming_bot/system/controllers/session.php +++ b/mirzaev/arming_bot/system/controllers/session.php @@ -161,6 +161,8 @@ final class session extends core * @param array $parameters Parameters of the request (POST + GET) * * @return void + * + * @todo переделать под trait buffer */ public function write(array $parameters = []): void { diff --git a/mirzaev/arming_bot/system/models/account.php b/mirzaev/arming_bot/system/models/account.php index 9b70c9f..4077f7a 100755 --- a/mirzaev/arming_bot/system/models/account.php +++ b/mirzaev/arming_bot/system/models/account.php @@ -7,9 +7,10 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, 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\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; // Framework for ArangoDB @@ -33,9 +34,9 @@ use exception; * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -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; } @@ -77,20 +78,20 @@ final class account extends core implements arangodb_document_interface // Initialized the account // Initializing the object - $account = new account; + $account = new static; if (method_exists($account, '__document')) { // Object can implement a document from ArangoDB // 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 $account->__document($result); // Exit (success) return $account; - } + } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB'); } else if ($registration) { // Not found the account and registration is requested diff --git a/mirzaev/arming_bot/system/models/cart.php b/mirzaev/arming_bot/system/models/cart.php new file mode 100755 index 0000000..bd2193e --- /dev/null +++ b/mirzaev/arming_bot/system/models/cart.php @@ -0,0 +1,29 @@ + + */ +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'; +} diff --git a/mirzaev/arming_bot/system/models/category.php b/mirzaev/arming_bot/system/models/category.php index baf0515..242d548 100755 --- a/mirzaev/arming_bot/system/models/category.php +++ b/mirzaev/arming_bot/system/models/category.php @@ -6,8 +6,9 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - 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; // Framework for ArangoDB use mirzaev\arangodb\collection, @@ -25,9 +26,9 @@ use exception; * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -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 diff --git a/mirzaev/arming_bot/system/models/connect.php b/mirzaev/arming_bot/system/models/connect.php index 366a9b4..cae2508 100755 --- a/mirzaev/arming_bot/system/models/connect.php +++ b/mirzaev/arming_bot/system/models/connect.php @@ -6,8 +6,9 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - 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; // Framework for ArangoDB 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 * @author Arsen Mirzaev Tatyano-Muradovich */ -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 diff --git a/mirzaev/arming_bot/system/models/core.php b/mirzaev/arming_bot/system/models/core.php index cbf05ae..e54006e 100755 --- a/mirzaev/arming_bot/system/models/core.php +++ b/mirzaev/arming_bot/system/models/core.php @@ -4,9 +4,6 @@ declare(strict_types=1); namespace mirzaev\arming_bot\models; -// Files of the project -use mirzaev\arming_bot\models\interfaces\collection as collection_interface; - // Framework for PHP use mirzaev\minimal\model; @@ -28,7 +25,7 @@ use exception; * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -class core extends model implements collection_interface +class core extends model { /** * Postfix for name of models files diff --git a/mirzaev/arming_bot/system/models/entry.php b/mirzaev/arming_bot/system/models/entry.php index b43038a..b4ee141 100755 --- a/mirzaev/arming_bot/system/models/entry.php +++ b/mirzaev/arming_bot/system/models/entry.php @@ -6,8 +6,9 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - 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; // Library for ArangoDB 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 * @author Arsen Mirzaev Tatyano-Muradovich */ -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 diff --git a/mirzaev/arming_bot/system/models/menu.php b/mirzaev/arming_bot/system/models/menu.php index 8f0069e..6777668 100755 --- a/mirzaev/arming_bot/system/models/menu.php +++ b/mirzaev/arming_bot/system/models/menu.php @@ -6,9 +6,9 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - 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; /** * 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 * @author Arsen Mirzaev Tatyano-Muradovich */ -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 diff --git a/mirzaev/arming_bot/system/models/product.php b/mirzaev/arming_bot/system/models/product.php index 020b22d..46b39a8 100755 --- a/mirzaev/arming_bot/system/models/product.php +++ b/mirzaev/arming_bot/system/models/product.php @@ -7,12 +7,17 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, 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 use mirzaev\arangodb\collection, mirzaev\arangodb\document; +// Library for ArangoDB +use ArangoDBClient\Document as _document; + // Built-in libraries use exception; @@ -24,9 +29,9 @@ use exception; * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -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 @@ -118,11 +123,11 @@ final class product extends core * @param int $page Page * @param int $amount Amount per page * @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 &$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( ?string $search = null, @@ -131,22 +136,25 @@ final class product extends core int $page = 1, int $amount = 100, ?string $return = 'DISTINCT d', - language $language = language::en, + ?language $language = null, array $parameters = [], array &$errors = [] - ): array { + ): array|static { try { if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized the collection + // Initializing of the language parameter + if ($language instanceof language) $parameters['language'] = $language->name; + // Initializing parameters for search if ($search) $parameters += [ 'search' => $search, - 'analyzer' => 'text_' . $language->name + 'analyzer' => 'text_' . $language->name ?? language::en->name ]; - // Reading products - $documents = collection::execute( + // Search for products + $result = collection::execute( sprintf( <<<'AQL' FOR d IN @@collection %s @@ -182,19 +190,31 @@ final class product extends core ), [ '@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search', - 'language' => $language->name, 'offset' => --$page <= 0 ? $page = 0 : $page * $amount, 'amount' => $amount, ] + $parameters, errors: $errors ); - if ($documents) { - // Found products + if ($result instanceof _document) { + // Found product - // Exit (success) - return is_array($documents) ? $documents : [$documents]; - } else return []; + // 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) + return $product; + } 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); } catch (exception $e) { // Writing to the registry of errors diff --git a/mirzaev/arming_bot/system/models/session.php b/mirzaev/arming_bot/system/models/session.php index f0423e4..b86aeae 100755 --- a/mirzaev/arming_bot/system/models/session.php +++ b/mirzaev/arming_bot/system/models/session.php @@ -7,11 +7,13 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\account, mirzaev\arming_bot\models\connect, + mirzaev\arming_bot\models\cart, mirzaev\arming_bot\models\enumerations\session as verification, 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\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; // Framework for ArangoDB @@ -32,9 +34,9 @@ use exception; * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -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; } @@ -98,7 +100,7 @@ final class session extends core implements arangodb_document_interface RETURN d AQL, [ - '@collection' => static::COLLECTION, + '@collection' => static::COLLECTION, '_id' => $_id, 'time' => time() ], @@ -148,6 +150,7 @@ final class session extends core implements arangodb_document_interface $result = collection::execute( <<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 $account->__document($result); // Exit (success) return $account; - } + } else throw new exception('Class ' . account::class . ' does not implement a document from ArangoDB'); } else return null; } else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::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 * - * @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 { if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) { - if (collection::initialize(account::COLLECTION, account::TYPE, errors: $errors)) { - // Collections initialized + if (collection::initialize(cart::COLLECTION, cart::TYPE, errors: $errors)) { + // Initialized collections - // The instance of the session document from ArangoDB is initialized? - isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized'); - - // Writing document and exit (success) - return document::write( - connect::COLLECTION, + // Search for connected cart + $result = collection::execute( + << $account->getId(), - '_to' => $this->document->getId() + 'session' => $this->getId() ], 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 ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { diff --git a/mirzaev/arming_bot/system/models/settings.php b/mirzaev/arming_bot/system/models/settings.php index 2fccc67..8f7a58e 100755 --- a/mirzaev/arming_bot/system/models/settings.php +++ b/mirzaev/arming_bot/system/models/settings.php @@ -6,15 +6,16 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - 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; // Framework for ArangoDB use mirzaev\arangodb\collection, mirzaev\arangodb\document; -// Library для ArangoDB +// Library for ArangoDB use ArangoDBClient\Document as _document; // Built-in libraries @@ -28,9 +29,9 @@ use exception; * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -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 @@ -76,14 +77,14 @@ final class settings extends core implements arangodb_document_interface // Object can implement a document from ArangoDB // 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 $settings->__document($result); // Exit (success) return $settings; - } + } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB'); } else if ($create) { // Not found active settings and requested their creating diff --git a/mirzaev/arming_bot/system/models/suspension.php b/mirzaev/arming_bot/system/models/suspension.php index ab7f2f3..2d4bc47 100755 --- a/mirzaev/arming_bot/system/models/suspension.php +++ b/mirzaev/arming_bot/system/models/suspension.php @@ -6,9 +6,10 @@ namespace mirzaev\arming_bot\models; // Files of the project use mirzaev\arming_bot\models\core, - mirzaev\arming_bot\models\enumerations\language, - mirzaev\arming_bot\models\traits\document as arangodb_document_trait, - 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; // Framework for ArangoDB use mirzaev\arangodb\collection, @@ -29,9 +30,9 @@ use exception, * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -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 @@ -81,7 +82,7 @@ final class suspension extends core implements arangodb_document_interface // Exit (success) return $suspension; - } + } else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB'); } else return null; } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); } catch (exception $e) { diff --git a/mirzaev/arming_bot/system/models/traits/document.php b/mirzaev/arming_bot/system/models/traits/document.php index 2af6a00..aa6fa8f 100755 --- a/mirzaev/arming_bot/system/models/traits/document.php +++ b/mirzaev/arming_bot/system/models/traits/document.php @@ -5,13 +5,17 @@ declare(strict_types=1); namespace mirzaev\arming_bot\models\traits; // 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 use ArangoDBClient\Document as _document; // 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 use exception; @@ -19,6 +23,8 @@ use exception; /** * Trait for implementing a document instance from ArangoDB * + * @uses document_interface + * * @var protected readonly _document|null $document An instance of the ArangoDB document * * @package mirzaev\arming_bot\models\traits @@ -71,6 +77,52 @@ trait document 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 * diff --git a/mirzaev/arming_bot/system/public/index.php b/mirzaev/arming_bot/system/public/index.php index 483509e..d3e9594 100755 --- a/mirzaev/arming_bot/system/public/index.php +++ b/mirzaev/arming_bot/system/public/index.php @@ -40,6 +40,8 @@ $router = new router; $router ->write('/', 'catalog', 'index', 'GET') ->write('/', 'catalog', 'index', 'POST') + ->write('/cart', 'cart', 'index', 'GET') + ->write('/cart/write', 'cart', 'write', 'POST') ->write('/account/write', 'account', 'write', 'POST') ->write('/session/write', 'session', 'write', 'POST') ->write('/session/connect/telegram', 'session', 'telegram', 'POST') diff --git a/mirzaev/arming_bot/system/public/js/cart.js b/mirzaev/arming_bot/system/public/js/cart.js index 9237584..517fcd7 100755 --- a/mirzaev/arming_bot/system/public/js/cart.js +++ b/mirzaev/arming_bot/system/public/js/cart.js @@ -35,9 +35,90 @@ import("/js/core.js").then(() => */ 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 + * @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 + * @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 + * + * @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"); + } + } + }); + } + } + } }; } } diff --git a/mirzaev/arming_bot/system/public/js/catalog.js b/mirzaev/arming_bot/system/public/js/catalog.js index 41b4a12..42d4899 100755 --- a/mirzaev/arming_bot/system/public/js/catalog.js +++ b/mirzaev/arming_bot/system/public/js/catalog.js @@ -797,7 +797,7 @@ import("/js/core.js").then(() => * * @return {Promise} Request to the server */ - static product_system(identifier) { + static async product_system(identifier) { if (typeof identifier === "string") { // Validated identifier @@ -807,7 +807,7 @@ import("/js/core.js").then(() => // Write parameter to the buffer of URN parameters parameters.set("product", identifier); - return core.request("?" + parameters).then((json) => { + return await core.request("?" + parameters).then((json) => { if ( json.errors !== null && typeof json.errors === "object" && diff --git a/mirzaev/arming_bot/system/public/themes/default/css/catalog.css b/mirzaev/arming_bot/system/public/themes/default/css/catalog.css index c261663..f01a48b 100644 --- a/mirzaev/arming_bot/system/public/themes/default/css/catalog.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/catalog.css @@ -137,6 +137,10 @@ main>section#products>div.column>article.product>a>p.title { 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 { z-index: 100; height: 33px; diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html index f6031a4..bf68fbf 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html @@ -1,13 +1,17 @@ {% 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 ~ 'г' %}