cart + interfase + reservation + currencies
This commit is contained in:
parent
8efce7d6e6
commit
140e40e79a
72
README.md
72
README.md
|
@ -7,35 +7,35 @@ Basis for developing chat-robots with "Web App" technology for Telegram
|
||||||
### AnangoDB
|
### AnangoDB
|
||||||
|
|
||||||
1. Create a Graph with the specified values
|
1. Create a Graph with the specified values
|
||||||
**Name:** catalog<br>
|
**Name:** catalog<br/>
|
||||||
<br>
|
<br/>
|
||||||
**edgeDefinition:** entry<br>
|
**edgeDefinition:** entry<br/>
|
||||||
**fromCollections:** categoy, product<br>
|
**fromCollections:** categoy, product<br/>
|
||||||
**toCollections:** category
|
**toCollections:** category
|
||||||
|
|
||||||
2. Create a Graph with the specified values
|
2. Create a Graph with the specified values
|
||||||
**Name:** sessions<br>
|
**Name:** sessions<br/>
|
||||||
<br>
|
<br/>
|
||||||
**edgeDefinition:** connect<br>
|
**edgeDefinition:** connect<br/>
|
||||||
**fromCollections:** account<br>
|
**fromCollections:** account<br/>
|
||||||
**toCollections:** session
|
**toCollections:** session
|
||||||
|
|
||||||
3. Create indexes for the "product" collection
|
3. Create indexes for the "product" collection
|
||||||
**Type:** "Inverted Index"<br>
|
**Type:** "Inverted Index"<br/>
|
||||||
**Fields:** name.ru<br>
|
**Fields:** name.ru<br/>
|
||||||
**Analyzer:** "text_ru"<br>
|
**Analyzer:** "text_ru"<br/>
|
||||||
**Search field:** true<br>
|
**Search field:** true<br/>
|
||||||
**Name:** name_ru<br>
|
**Name:** name_ru<br/>
|
||||||
<br>
|
<br/>
|
||||||
*Add indexes for all search parameters and for all languages (search language is selected based on the user's language, <br>
|
*Add indexes for all search parameters and for all languages (search language is selected based on the user's language, <br/>
|
||||||
otherwise from the default language specified in the active settings from **settings** collection document)*<br>
|
otherwise from the default language specified in the active settings from **settings** collection document)*<br/>
|
||||||
<br>
|
<br/>
|
||||||
*See fields in the `mirzaev/arming_bot/models/product`<br>
|
*See fields in the `mirzaev/arming_bot/models/product`<br/>
|
||||||
**name.ru**, **description.ru** and **compatibility.ru***
|
**name.ru**, **description.ru** and **compatibility.ru***
|
||||||
|
|
||||||
4. Create a View with the specified values
|
4. Create a View with the specified values
|
||||||
**type:** search-alias (you can also use "arangosearch")<br>
|
**type:** search-alias (you can also use "arangosearch")<br/>
|
||||||
**name:** **product**s_search<br>
|
**name:** **product**s_search<br/>
|
||||||
**indexes:**
|
**indexes:**
|
||||||
```json
|
```json
|
||||||
"indexes": [
|
"indexes": [
|
||||||
|
@ -68,27 +68,39 @@ location ~ \.php$ {
|
||||||
|
|
||||||
1. Execute: `sudo cp telegram-huesos.service /etc/systemd/system/telegram-huesos.service`
|
1. Execute: `sudo cp telegram-huesos.service /etc/systemd/system/telegram-huesos.service`
|
||||||
|
|
||||||
*before you execute the command think about **what it does** and whether the **paths** are specified correctly*<br>
|
*before you execute the command think about **what it does** and whether the **paths** are specified correctly*<br/>
|
||||||
*the configuration file is very simple and you can remake it for any alternative to SystemD that you like*
|
*the configuration file is very simple and you can remake it for any alternative to SystemD that you like*
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
Settings of chat-robot and Web App<br>
|
Settings of chat-robot and Web App<br/>
|
||||||
<br>
|
<br/>
|
||||||
Make sure you have a **settings** collection (can be created automatically) and at least one document with the "status" parameter set to "active"
|
Make sure you have a **settings** collection (can be created automatically) and at least one document with the "status" parameter set to "active"
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"status": "active"
|
"status": "active",
|
||||||
|
"project": {
|
||||||
|
"name": "NAME_OF_THE_PROJECT"
|
||||||
|
},
|
||||||
|
"language": "en",
|
||||||
|
"currency": "usd"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### language
|
### Language
|
||||||
Language for system messages if user language could not be determined<br>
|
Language for render of interface, if account or session language is not initialized<br/>
|
||||||
<br>
|
<br/>
|
||||||
**Value:** en
|
**Value:** en<br/>
|
||||||
|
**⚠️ The value will be converted to an instance of enumeration** `mirzaev\arming_bot\models\enumerations\language`
|
||||||
|
|
||||||
|
### Currency
|
||||||
|
Currency for calculations and render of interface, if account or session currency is not initialized<br/>
|
||||||
|
<br/>
|
||||||
|
**Value:** usd<br/>
|
||||||
|
**⚠️ The value will be converted to an instance of enumeration** `mirzaev\arming_bot\models\enumerations\currency`
|
||||||
|
|
||||||
## Suspensions
|
## Suspensions
|
||||||
System of suspensions of chat-robot and Web App<br>
|
System of suspensions of chat-robot and Web App<br/>
|
||||||
<br>
|
<br/>
|
||||||
Make sure you have a **suspension** collection (can be created automatically)
|
Make sure you have a **suspension** collection (can be created automatically)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,22 +32,28 @@ final class cart extends core
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write or delete from the cart
|
* Product
|
||||||
|
*
|
||||||
|
* Write or delete the product in the cart
|
||||||
*
|
*
|
||||||
* @param array $parameters Parameters of the request (POST + GET)
|
* @param array $parameters Parameters of the request (POST + GET)
|
||||||
|
*
|
||||||
|
* @todo
|
||||||
|
* 1. Add a limit on adding products to the cart based on the number of products in stock
|
||||||
*/
|
*/
|
||||||
public function write(array $parameters = []): ?string
|
public function product(array $parameters = []): ?string
|
||||||
{
|
{
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
// POST request
|
// POST request
|
||||||
|
|
||||||
// The cart contains the product?
|
// Declaring of the buffer with amount of the product in the cart
|
||||||
$status = false;
|
$amount = 0;
|
||||||
|
|
||||||
// Validating @todo add throwing errors
|
// Validating @todo add throwing errors
|
||||||
if (!empty($parameters['product']) && preg_match('/[\d]+/', urldecode($parameters['product']), $matches)) $product = (int) $matches[0];
|
$identifier = null;
|
||||||
|
if (!empty($parameters['identifier']) && preg_match('/[\d]+/', urldecode($parameters['identifier']), $matches)) $identifier = (int) $matches[0];
|
||||||
|
|
||||||
if (isset($product)) {
|
if (isset($identifier)) {
|
||||||
// Received and validated identfier of the product
|
// Received and validated identfier of the product
|
||||||
|
|
||||||
// Search for the product
|
// Search for the product
|
||||||
|
@ -55,34 +61,98 @@ final class cart extends core
|
||||||
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
||||||
sort: 'd.created DESC',
|
sort: 'd.created DESC',
|
||||||
amount: 1,
|
amount: 1,
|
||||||
parameters: ['identifier' => $product],
|
parameters: ['identifier' => $identifier],
|
||||||
errors: $this->errors['cart']
|
errors: $this->errors['cart']
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($product instanceof product) {
|
if ($product instanceof product) {
|
||||||
// Initialized the product
|
// Initialized the product
|
||||||
|
|
||||||
// Initializing of the cart
|
// Initializing the buffer with amount of the product in the cart
|
||||||
$cart = $this->session->cart(errors: $this->errors['cart']);
|
$amount = $this->cart->count(product: $product, limit: 100, errors: $this->errors['cart']) ?? 0;
|
||||||
|
|
||||||
if ($cart instanceof model) {
|
if ($this->cart instanceof model) {
|
||||||
// Initialized the cart
|
// Initialized the cart
|
||||||
|
|
||||||
if (0 < $amount = $cart->has($product, errors: $this->errors['cart'])) {
|
// Validating @todo add throwing errors
|
||||||
// The cart contains the product
|
$type = null;
|
||||||
|
if (!empty($parameters['type']) && preg_match('/[\w]+/', urldecode($parameters['type']), $matches)) $type = $matches[0];
|
||||||
|
|
||||||
// temporary
|
if (isset($type)) {
|
||||||
/* $cart->disconnect($product, errors: $this->errors['cart']); */
|
// Received and validated type of action with the product
|
||||||
$cart->cancel($product, errors: $this->errors['cart']);
|
|
||||||
|
|
||||||
$status = false;
|
if ($type === 'toggle') {
|
||||||
} else {
|
// Write the product to the cart if is not in the cart and vice versa
|
||||||
// The cart not contains the product
|
|
||||||
|
|
||||||
// temporary
|
if ($amount > 0) {
|
||||||
$cart->connect($product, errors: $this->errors['cart']);
|
// The cart has the product
|
||||||
|
|
||||||
$status = true;
|
// Deleting the product from the cart
|
||||||
|
$this->cart->delete(product: $product, amount: $amount, errors: $this->errors['cart']);
|
||||||
|
|
||||||
|
// Reinitializing the buffer with amount of the product in the cart
|
||||||
|
$amount = 0;
|
||||||
|
} else {
|
||||||
|
// The cart has no the product
|
||||||
|
|
||||||
|
// Writing the product to the cart
|
||||||
|
$this->cart->write(product: $product, amount: 1, errors: $this->errors['cart']);
|
||||||
|
|
||||||
|
// Reinitializing the buffer with amount of the product in the cart
|
||||||
|
$amount = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Received not the "toggle" command
|
||||||
|
|
||||||
|
// Validating @todo add throwing errors
|
||||||
|
$_amount = null;
|
||||||
|
if (!empty($parameters['amount']) && preg_match('/[\d]+/', urldecode($parameters['amount']), $matches)) $_amount = (int) $matches[0];
|
||||||
|
|
||||||
|
if (isset($_amount)) {
|
||||||
|
// Received and validated amount parameter for action with the product
|
||||||
|
|
||||||
|
if ($type === 'write') {
|
||||||
|
// Increase amount of the product in the cart
|
||||||
|
|
||||||
|
if (101 > $amount += $_amount) {
|
||||||
|
// Validated amount to wrting
|
||||||
|
|
||||||
|
// Writing the product to the cart
|
||||||
|
$this->cart->write(product: $product, amount: $_amount, errors: $this->errors['cart']);
|
||||||
|
}
|
||||||
|
} else if ($type === 'delete') {
|
||||||
|
// Decrease amount of the product in the cart
|
||||||
|
|
||||||
|
if (-1 < $amount -= $_amount) {
|
||||||
|
// Validated amount to deleting
|
||||||
|
|
||||||
|
// Deleting the product from the cart
|
||||||
|
$this->cart->delete(product: $product, amount: $_amount, errors: $this->errors['cart']);
|
||||||
|
}
|
||||||
|
} else if ($type === 'set') {
|
||||||
|
// Set amount of the product in the cart
|
||||||
|
|
||||||
|
if ($_amount > -1 && $_amount < 101) {
|
||||||
|
// Validated amount to setting
|
||||||
|
|
||||||
|
if ($_amount > $amount) {
|
||||||
|
// Requested amount more than actual amount of the product in the cart
|
||||||
|
|
||||||
|
// Writing the product from the cart
|
||||||
|
$this->cart->write(product: $product, amount: $_amount - $amount, errors: $this->errors['cart']);
|
||||||
|
} else {
|
||||||
|
// Requested amount less than actual amount of the product in the cart
|
||||||
|
|
||||||
|
// Deleting the product from the cart
|
||||||
|
$this->cart->delete(product: $product, amount: $amount - $_amount, errors: $this->errors['cart']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinitializing the buffer with amount of the product in the cart
|
||||||
|
$amount = $_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +169,7 @@ final class cart extends core
|
||||||
// Generating the reponse
|
// Generating the reponse
|
||||||
echo json_encode(
|
echo json_encode(
|
||||||
[
|
[
|
||||||
'status' => $status ?? false,
|
'amount' => $amount, // $amount does not store a real value, but is calculated without a repeated request to ArangoDB
|
||||||
'errors' => $this->errors
|
'errors' => $this->errors
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
@ -53,9 +53,10 @@ final class catalog extends core
|
||||||
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
||||||
sort: 'd.created DESC',
|
sort: 'd.created DESC',
|
||||||
amount: 1,
|
amount: 1,
|
||||||
return: '{name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, images: d.images[*].storage}',
|
return: '{name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, cost: d.cost.@currency, images: d.images[*].storage}',
|
||||||
parameters: ['identifier' => $product],
|
|
||||||
language: $this->language,
|
language: $this->language,
|
||||||
|
currency: $this->currency,
|
||||||
|
parameters: ['identifier' => $product],
|
||||||
errors: $this->errors['catalog']
|
errors: $this->errors['catalog']
|
||||||
)[0]?->getAll() ?? null;
|
)[0]?->getAll() ?? null;
|
||||||
}
|
}
|
||||||
|
@ -251,6 +252,9 @@ final class catalog extends core
|
||||||
$entries = entry::search(
|
$entries = entry::search(
|
||||||
document: $category,
|
document: $category,
|
||||||
amount: 50,
|
amount: 50,
|
||||||
|
categories_merge: 'name: v.name.@language',
|
||||||
|
/* products_merge: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language, brand: d.brand.@language, cost: d.cost.@currency})', */
|
||||||
|
parameters: ['language' => $this->language->name],
|
||||||
errors: $this->errors['catalog']
|
errors: $this->errors['catalog']
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -284,7 +288,12 @@ final class catalog extends core
|
||||||
// Not received identifier of the category
|
// Not received identifier of the category
|
||||||
|
|
||||||
// search for root ascendants categories
|
// search for root ascendants categories
|
||||||
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']) ?? null;
|
$this->view->categories = entry::ascendants(
|
||||||
|
descendant: new category,
|
||||||
|
return: 'DISTINCT MERGE(ascendant, { name: ascendant.name.@language})',
|
||||||
|
parameters: ['language' => $this->language->name],
|
||||||
|
errors: $this->errors['catalog']
|
||||||
|
) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search among products in the $category
|
// Search among products in the $category
|
||||||
|
@ -307,9 +316,10 @@ final class catalog extends core
|
||||||
filter: $_filters,
|
filter: $_filters,
|
||||||
sort: $_sort,
|
sort: $_sort,
|
||||||
amount: 50, // @todo pagination
|
amount: 50, // @todo pagination
|
||||||
|
return: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language, brand: d.brand.@language, cost: d.cost.@currency})',
|
||||||
language: $this->language,
|
language: $this->language,
|
||||||
|
currency: $this->currency,
|
||||||
parameters: $_parameters,
|
parameters: $_parameters,
|
||||||
return: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language})',
|
|
||||||
errors: $this->errors['catalog']
|
errors: $this->errors['catalog']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ use mirzaev\arming_bot\views\templater,
|
||||||
mirzaev\arming_bot\models\account,
|
mirzaev\arming_bot\models\account,
|
||||||
mirzaev\arming_bot\models\session,
|
mirzaev\arming_bot\models\session,
|
||||||
mirzaev\arming_bot\models\settings,
|
mirzaev\arming_bot\models\settings,
|
||||||
|
mirzaev\arming_bot\models\cart,
|
||||||
mirzaev\arming_bot\models\suspension,
|
mirzaev\arming_bot\models\suspension,
|
||||||
mirzaev\arming_bot\models\enumerations\language;
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
// Framework for PHP
|
// Framework for PHP
|
||||||
use mirzaev\minimal\controller;
|
use mirzaev\minimal\controller;
|
||||||
|
@ -37,20 +39,30 @@ class core extends controller
|
||||||
protected readonly settings $settings;
|
protected readonly settings $settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of a session
|
* Instance of the session
|
||||||
*/
|
*/
|
||||||
protected readonly session $session;
|
protected readonly session $session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of an account
|
* Instance of the account
|
||||||
*/
|
*/
|
||||||
protected readonly ?account $account;
|
protected readonly ?account $account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of the cart
|
||||||
|
*/
|
||||||
|
protected readonly ?cart $cart;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language
|
* Language
|
||||||
*/
|
*/
|
||||||
protected language $language = language::en;
|
protected language $language = language::en;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currency
|
||||||
|
*/
|
||||||
|
protected currency $currency = currency::usd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry of errors
|
* Registry of errors
|
||||||
*/
|
*/
|
||||||
|
@ -66,7 +78,9 @@ class core extends controller
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*
|
*
|
||||||
* @todo settings account и session не имеют проверок на возврат null
|
* @todo
|
||||||
|
* 1. settings account и session не имеют проверок на возврат null
|
||||||
|
* 2. TRANSIT EVERYTHING TO MIDDLEWARES
|
||||||
*/
|
*/
|
||||||
public function __construct(bool $initialize = true)
|
public function __construct(bool $initialize = true)
|
||||||
{
|
{
|
||||||
|
@ -118,15 +132,21 @@ class core extends controller
|
||||||
// Initializing of the settings
|
// Initializing of the settings
|
||||||
$this->settings = settings::active();
|
$this->settings = settings::active();
|
||||||
|
|
||||||
// Initializing of language
|
// Initializing of the language
|
||||||
if ($this->account?->language) $this->language = $this->account->language ?? language::en;
|
$this->language = $this->account?->language ?? $this->session?->buffer['language'] ?? $this->settings?->language ?? language::en;
|
||||||
else if ($this->settings?->language) $this->language = $this->settings->language ?? language::en;
|
|
||||||
|
// Initializing of the currency
|
||||||
|
$this->currency = $this->account?->currency ?? $this->session?->buffer['currency'] ?? $this->settings?->currency ?? currency::usd;
|
||||||
|
|
||||||
|
// Initializing of the cart
|
||||||
|
$this->cart = $this->account?->cart() ?? $this->session?->cart();
|
||||||
|
|
||||||
// Initializing of preprocessor of views
|
// Initializing of preprocessor of views
|
||||||
$this->view = new templater(
|
$this->view = new templater(
|
||||||
session: $this->session,
|
session: $this->session,
|
||||||
account: $this->account,
|
account: $this->account,
|
||||||
settings: $this->settings
|
settings: $this->settings,
|
||||||
|
cart: $this->cart
|
||||||
);
|
);
|
||||||
|
|
||||||
// @todo перенести в middleware
|
// @todo перенести в middleware
|
||||||
|
|
|
@ -8,10 +8,12 @@ namespace mirzaev\arming_bot\models;
|
||||||
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\buffer,
|
mirzaev\arming_bot\models\traits\buffer,
|
||||||
|
mirzaev\arming_bot\models\traits\cart,
|
||||||
mirzaev\arming_bot\models\traits\document as 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\document as document_interface,
|
||||||
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
||||||
mirzaev\arming_bot\models\enumerations\language;
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -36,8 +38,9 @@ use exception;
|
||||||
*/
|
*/
|
||||||
final class account extends core implements document_interface, collection_interface
|
final class account extends core implements document_interface, collection_interface
|
||||||
{
|
{
|
||||||
use status, document_trait, buffer {
|
use status, document_trait, buffer, cart {
|
||||||
buffer::write as write;
|
buffer::write as write;
|
||||||
|
cart::initialize as cart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,6 +88,7 @@ final class account extends core implements document_interface, collection_inter
|
||||||
|
|
||||||
// Abstractioning of parameters
|
// Abstractioning of parameters
|
||||||
if (isset($result->language)) $result->language = language::{$result->language};
|
if (isset($result->language)) $result->language = language::{$result->language};
|
||||||
|
if (isset($result->currency)) $result->currency = currency::{$result->currency};
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
|
@ -6,13 +6,27 @@ 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\reservation,
|
||||||
mirzaev\arming_bot\models\traits\document as 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\document as document_interface,
|
||||||
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
|
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
|
// Framework for ArangoDB
|
||||||
|
use mirzaev\arangodb\collection,
|
||||||
|
mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
// Library for ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Built-in libraries
|
||||||
|
use exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model of cart
|
* Model of cart
|
||||||
*
|
*
|
||||||
|
* @uses reservation
|
||||||
* @package mirzaev\arming_bot\models
|
* @package mirzaev\arming_bot\models
|
||||||
*
|
*
|
||||||
* @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
|
||||||
|
@ -26,4 +40,240 @@ final class cart extends core implements document_interface, collection_interfac
|
||||||
* Name of the collection in ArangoDB
|
* Name of the collection in ArangoDB
|
||||||
*/
|
*/
|
||||||
final public const string COLLECTION = 'cart';
|
final public const string COLLECTION = 'cart';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for all products
|
||||||
|
*
|
||||||
|
* Search for all products in the cart
|
||||||
|
*
|
||||||
|
* @param language|null $language Language
|
||||||
|
* @param currency|null $currency Currency
|
||||||
|
* @param array &$errors Registry of errors
|
||||||
|
*
|
||||||
|
* @return array|null Array with implementing objects of documents from ArangoDB, if found
|
||||||
|
*/
|
||||||
|
public function all(
|
||||||
|
?language $language = language::en,
|
||||||
|
?currency $currency = currency::usd,
|
||||||
|
array &$errors = []
|
||||||
|
): ?array {
|
||||||
|
try {
|
||||||
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(product::COLLECTION, product::TYPE, errors: $errors)) {
|
||||||
|
// Initialized collections
|
||||||
|
|
||||||
|
// Search for all products in the cart
|
||||||
|
$result = @collection::execute(
|
||||||
|
<<<AQL
|
||||||
|
FOR v IN 1..1 INBOUND @cart GRAPH @graph
|
||||||
|
FILTER IS_SAME_COLLECTION(@collection, v._id)
|
||||||
|
COLLECT d = v WITH COUNT INTO amount
|
||||||
|
RETURN {
|
||||||
|
[d._id]: {
|
||||||
|
amount,
|
||||||
|
product: MERGE(d, {
|
||||||
|
name: d.name.@language,
|
||||||
|
description: d.description.@language,
|
||||||
|
compatibility: d.compatibility.@language,
|
||||||
|
brand: d.brand.@language,
|
||||||
|
cost: d.cost.@currency
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AQL,
|
||||||
|
[
|
||||||
|
'graph' => 'catalog',
|
||||||
|
'cart' => $this->getId(),
|
||||||
|
'collection' => product::COLLECTION,
|
||||||
|
'language' => $language->name,
|
||||||
|
'currency' => $currency->name
|
||||||
|
],
|
||||||
|
flat: true,
|
||||||
|
errors: $errors
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* МеНЯ ЭТО РАЗДРАЖАЕТ
|
||||||
|
*/
|
||||||
|
|
||||||
|
$products = [];
|
||||||
|
|
||||||
|
foreach ($result ?? [] as $raw) {
|
||||||
|
foreach ($raw as $key => $value) {
|
||||||
|
$products[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return $products;
|
||||||
|
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
|
||||||
|
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count
|
||||||
|
*
|
||||||
|
* Count of the product in the cart
|
||||||
|
*
|
||||||
|
* @param product $product The product
|
||||||
|
* @param int $limit Limit for counting
|
||||||
|
* @param array &$errors Registry of errors
|
||||||
|
*
|
||||||
|
* @return int|null Amount of the product in the cart, if counted
|
||||||
|
*/
|
||||||
|
public function count(product $product, int $limit = 100, array &$errors = []): ?int
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(product::COLLECTION, product::TYPE, errors: $errors)) {
|
||||||
|
// Initialized collections
|
||||||
|
|
||||||
|
// Search for the products in the cart and count them
|
||||||
|
return (int) collection::execute(
|
||||||
|
<<<AQL
|
||||||
|
FOR v IN 1..1 INBOUND @cart GRAPH @graph
|
||||||
|
FILTER IS_SAME_COLLECTION(@collection, v._id) && v._id == @product
|
||||||
|
LIMIT @limit
|
||||||
|
COLLECT WITH COUNT INTO length
|
||||||
|
RETURN length
|
||||||
|
AQL,
|
||||||
|
[
|
||||||
|
'graph' => 'catalog',
|
||||||
|
'cart' => $this->getId(),
|
||||||
|
'collection' => product::COLLECTION,
|
||||||
|
'product' => $product->getId(),
|
||||||
|
'limit' => $limit
|
||||||
|
],
|
||||||
|
errors: $errors
|
||||||
|
);
|
||||||
|
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
|
||||||
|
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::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 the product in the cart
|
||||||
|
*
|
||||||
|
* @param product $product The product
|
||||||
|
* @param int $amount Amount of writings
|
||||||
|
* @param array &$errors Registry of errors
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function write(product $product, int $amount = 1, array &$errors = []): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(product::COLLECTION, product::TYPE, errors: $errors)) {
|
||||||
|
// Initialized collections
|
||||||
|
|
||||||
|
// Writing the product to the cart
|
||||||
|
collection::execute(
|
||||||
|
<<<AQL
|
||||||
|
FOR i IN 1..@amount
|
||||||
|
INSERT { _from: @product, _to: @cart, created: @created, updated: @created, active: true } INTO @@edge
|
||||||
|
AQL,
|
||||||
|
[
|
||||||
|
'cart' => $this->getId(),
|
||||||
|
'product' => $product->getId(),
|
||||||
|
'@edge' => reservation::COLLECTION,
|
||||||
|
'amount' => $amount,
|
||||||
|
'created' => time()
|
||||||
|
],
|
||||||
|
errors: $errors
|
||||||
|
);
|
||||||
|
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
|
||||||
|
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::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()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete
|
||||||
|
*
|
||||||
|
* Delete the product from the cart
|
||||||
|
*
|
||||||
|
* @param product $product The product
|
||||||
|
* @param int $amount Amount of deletings
|
||||||
|
* @param array &$errors Registry of errors
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function delete(product $product, int $amount = 1, array &$errors = []): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(product::COLLECTION, product::TYPE, errors: $errors)) {
|
||||||
|
// Initialized collections
|
||||||
|
|
||||||
|
// Deleting the product from the cart
|
||||||
|
collection::execute(
|
||||||
|
<<<AQL
|
||||||
|
FOR v, e IN 1..1 INBOUND @cart GRAPH @graph
|
||||||
|
FILTER IS_SAME_COLLECTION(@collection, v._id) && v._id == @product
|
||||||
|
LIMIT @amount
|
||||||
|
REMOVE e._key IN @@reservation
|
||||||
|
AQL,
|
||||||
|
[
|
||||||
|
'graph' => 'catalog',
|
||||||
|
'cart' => $this->getId(),
|
||||||
|
'collection' => product::COLLECTION,
|
||||||
|
'product' => $product->getId(),
|
||||||
|
'@reservation' => reservation::COLLECTION,
|
||||||
|
'amount' => $amount
|
||||||
|
],
|
||||||
|
errors: $errors
|
||||||
|
);
|
||||||
|
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
|
||||||
|
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::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()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\models\entry,
|
mirzaev\arming_bot\models\entry,
|
||||||
mirzaev\arming_bot\models\traits\files,
|
mirzaev\arming_bot\models\traits\files,
|
||||||
mirzaev\arming_bot\models\enumerations\language,
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency,
|
||||||
mirzaev\arming_bot\models\traits\yandex\disk as yandex;
|
mirzaev\arming_bot\models\traits\yandex\disk as yandex;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
|
@ -77,6 +78,7 @@ final class catalog extends core
|
||||||
int &$products_old = 0,
|
int &$products_old = 0,
|
||||||
int &$products_new = 0,
|
int &$products_new = 0,
|
||||||
language $language = language::en,
|
language $language = language::en,
|
||||||
|
currency $currency = currency::usd,
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): void {
|
): void {
|
||||||
try {
|
try {
|
||||||
|
@ -314,7 +316,7 @@ final class catalog extends core
|
||||||
(int) $row['identifier'],
|
(int) $row['identifier'],
|
||||||
[$language->name => $row['name']],
|
[$language->name => $row['name']],
|
||||||
[$language->name => $row['description']],
|
[$language->name => $row['description']],
|
||||||
(float) $row['cost'],
|
[$currency->name => (float) $row['cost']],
|
||||||
(float) $row['weight'],
|
(float) $row['weight'],
|
||||||
['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
|
['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
|
||||||
[$language->name => $row['brand']],
|
[$language->name => $row['brand']],
|
||||||
|
|
|
@ -113,8 +113,8 @@ class core extends model
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($result instanceof _document) {
|
if ($amount === 1 && $result instanceof _document) {
|
||||||
// Received only 1 document and
|
// Received only 1 document and @todo rebuild
|
||||||
|
|
||||||
// Initializing the object
|
// Initializing the object
|
||||||
$object = new static;
|
$object = new static;
|
||||||
|
|
|
@ -91,38 +91,47 @@ final class entry extends core implements document_interface, collection_interfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find ascendants
|
* Ascendants
|
||||||
*
|
*
|
||||||
* Find ascendants that are not descendants for anyone
|
* Search for ascendants that are not descendants for anyone
|
||||||
*
|
*
|
||||||
* @param category|product $descendant Descendant document
|
* @param category|product $descendant Descendant document
|
||||||
|
* @param string|null $return Return (AQL)
|
||||||
|
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return array|null Ascendants that are not descendants for anyone, if found
|
* @return array|null Ascendants that are not descendants for anyone, if found
|
||||||
*/
|
*/
|
||||||
public static function ascendants(
|
public static function ascendants(
|
||||||
category|product $descendant,
|
category|product $descendant,
|
||||||
|
?string $return = 'DISTINCT ascendant',
|
||||||
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): ?array {
|
): ?array {
|
||||||
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
|
||||||
if ($ascendants = collection::execute(
|
|
||||||
<<<'AQL'
|
// Search for ascendants
|
||||||
FOR d IN @@collection
|
if ($result = collection::execute(
|
||||||
FOR ascendant IN OUTBOUND d @@edge
|
sprintf(
|
||||||
RETURN DISTINCT ascendant
|
<<<'AQL'
|
||||||
AQL,
|
FOR d IN @@collection
|
||||||
|
FOR ascendant IN OUTBOUND d @@edge
|
||||||
|
RETURN %s
|
||||||
|
AQL,
|
||||||
|
empty($return) ? 'DISTINCT ascendant' : $return
|
||||||
|
),
|
||||||
[
|
[
|
||||||
'@collection' => $descendant::COLLECTION,
|
'@collection' => $descendant::COLLECTION,
|
||||||
'@edge' => static::COLLECTION
|
'@edge' => static::COLLECTION
|
||||||
],
|
] + $parameters,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
)) {
|
)) {
|
||||||
// Found ascendants
|
// Found ascendants
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return is_array($ascendants) ? $ascendants : [$ascendants];
|
return is_array($result) ? $result : [$result];
|
||||||
} else return [];
|
} else return [];
|
||||||
} 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) {
|
||||||
|
@ -211,6 +220,9 @@ final class entry extends core implements document_interface, collection_interfa
|
||||||
* @param string|null $sort Expression for sorting (AQL)
|
* @param string|null $sort Expression for sorting (AQL)
|
||||||
* @param int $page Страница
|
* @param int $page Страница
|
||||||
* @param int $amount Количество товаров на странице
|
* @param int $amount Количество товаров на странице
|
||||||
|
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
|
||||||
|
* @param string|null $products_merge Expression with paremeters to return for products (AQL)
|
||||||
|
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return array Массив с найденными вхождениями (может быть пустым)
|
* @return array Массив с найденными вхождениями (может быть пустым)
|
||||||
|
@ -221,6 +233,9 @@ final class entry extends core implements document_interface, collection_interfa
|
||||||
?string $sort = 'v.position ASC, v.created DESC',
|
?string $sort = 'v.position ASC, v.created DESC',
|
||||||
int $page = 1,
|
int $page = 1,
|
||||||
int $amount = 100,
|
int $amount = 100,
|
||||||
|
?string $categories_merge = null,
|
||||||
|
?string $products_merge = null,
|
||||||
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): array {
|
): array {
|
||||||
try {
|
try {
|
||||||
|
@ -236,18 +251,21 @@ final class entry extends core implements document_interface, collection_interfa
|
||||||
%s
|
%s
|
||||||
%s
|
%s
|
||||||
LIMIT @offset, @amount
|
LIMIT @offset, @amount
|
||||||
LET _type = (FOR v2 IN INBOUND v._id GRAPH @graph RETURN v2)[0] ? "category" : "product"
|
RETURN DISTINCT IS_SAME_COLLECTION(@category, v._id) ? MERGE(v, {_type: @category%s}) : MERGE(v, {_type: @product%s})
|
||||||
RETURN MERGE(v, {_type})
|
|
||||||
AQL,
|
AQL,
|
||||||
empty($filter) ? '' : "FILTER $filter",
|
empty($filter) ? '' : "FILTER $filter",
|
||||||
empty($sort) ? '' : "SORT $sort",
|
empty($sort) ? '' : "SORT $sort",
|
||||||
|
empty($categories_merge) ? '' : ", $categories_merge",
|
||||||
|
empty($products_merge) ? '' : ", $products_merge"
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
'category' => category::COLLECTION,
|
||||||
|
'product' => product::COLLECTION,
|
||||||
'graph' => 'catalog',
|
'graph' => 'catalog',
|
||||||
'document' => $document->getId(),
|
'document' => $document->getId(),
|
||||||
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
||||||
'amount' => $amount
|
'amount' => $amount
|
||||||
],
|
] + $parameters,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
)) ? $result : [$result];
|
)) ? $result : [$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);
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\models\enumerations;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use mirzaev\arming_bot\models\enumerations\language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types of currencies by ISO 4217 standart
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\models\enumerations
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
enum currency
|
||||||
|
{
|
||||||
|
case usd;
|
||||||
|
case rub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label
|
||||||
|
*
|
||||||
|
* Initialize label of the currency
|
||||||
|
*
|
||||||
|
* @param language|null $language Language into which to translate
|
||||||
|
*
|
||||||
|
* @return string Translated label of the currency
|
||||||
|
*
|
||||||
|
* @todo
|
||||||
|
* 1. More currencies
|
||||||
|
* 2. Cases???
|
||||||
|
*/
|
||||||
|
public function label(?language $language = language::en): string
|
||||||
|
{
|
||||||
|
// Exit (success)
|
||||||
|
return match ($this) {
|
||||||
|
currency::usd => match ($language) {
|
||||||
|
language::en => 'Dollar',
|
||||||
|
language::ru => 'Доллар'
|
||||||
|
},
|
||||||
|
currency::rub => match ($language) {
|
||||||
|
language::en => 'Ruble',
|
||||||
|
language::ru => 'Рубль'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Symbol
|
||||||
|
*
|
||||||
|
* Initialize symbol of the currency
|
||||||
|
*
|
||||||
|
* @return string Symbol of the currency
|
||||||
|
*
|
||||||
|
* @todo
|
||||||
|
* 1. More currencies
|
||||||
|
*/
|
||||||
|
public function symbol(): string
|
||||||
|
{
|
||||||
|
// Exit (success)
|
||||||
|
return match ($this) {
|
||||||
|
currency::usd => '$',
|
||||||
|
currency::rub => '₽'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||||
namespace mirzaev\arming_bot\models\enumerations;
|
namespace mirzaev\arming_bot\models\enumerations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of human languages
|
* Types of languages by ISO 639-1 standart
|
||||||
*
|
*
|
||||||
* @package mirzaev\arming_bot\models\enumerations
|
* @package mirzaev\arming_bot\models\enumerations
|
||||||
*
|
*
|
||||||
|
@ -18,17 +18,19 @@ enum language
|
||||||
case ru;
|
case ru;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate label of language
|
* Label
|
||||||
|
*
|
||||||
|
* Initialize label of the language
|
||||||
*
|
*
|
||||||
* @param language|null $language Language into which to translate
|
* @param language|null $language Language into which to translate
|
||||||
*
|
*
|
||||||
* @return string Translated label of language
|
* @return string Translated label of the language
|
||||||
*
|
*
|
||||||
* @todo
|
* @todo
|
||||||
* 1. More languages
|
* 1. More languages
|
||||||
* 2. Cases???
|
* 2. Cases???
|
||||||
*/
|
*/
|
||||||
public function translate(?language $language = language::en): string
|
public function label(?language $language = language::en): string
|
||||||
{
|
{
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
|
|
|
@ -6,10 +6,11 @@ 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 document_trait,
|
||||||
mirzaev\arming_bot\models\interfaces\document as document_interface,
|
mirzaev\arming_bot\models\interfaces\document as document_interface,
|
||||||
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
|
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -63,7 +64,7 @@ final class product extends core implements document_interface, collection_inter
|
||||||
int $identifier,
|
int $identifier,
|
||||||
array $name = [['en' => 'ERROR']],
|
array $name = [['en' => 'ERROR']],
|
||||||
?array $description = [['en' => 'ERROR']],
|
?array $description = [['en' => 'ERROR']],
|
||||||
float $cost = 0,
|
array $cost = [['usd' => 0]],
|
||||||
float $weight = 0,
|
float $weight = 0,
|
||||||
array $dimensions = ['x' => 0, 'y' => 0, 'z' => 0],
|
array $dimensions = ['x' => 0, 'y' => 0, 'z' => 0],
|
||||||
?array $brand = [['en' => 'ERROR']],
|
?array $brand = [['en' => 'ERROR']],
|
||||||
|
@ -123,11 +124,14 @@ final class product extends core implements document_interface, collection_inter
|
||||||
* @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|null $language Language code (en, ru...)
|
* @param language|null $language Language
|
||||||
|
* @param currency|null $currency Currency
|
||||||
* @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|static Found products or instance of the product from ArangoDB (can be empty)
|
* @return array|static Found products or instance of the product from ArangoDB (can be empty)
|
||||||
|
*
|
||||||
|
* @todo убрать language и currency
|
||||||
*/
|
*/
|
||||||
public static function read(
|
public static function read(
|
||||||
?string $search = null,
|
?string $search = null,
|
||||||
|
@ -137,6 +141,7 @@ final class product extends core implements document_interface, collection_inter
|
||||||
int $amount = 100,
|
int $amount = 100,
|
||||||
?string $return = 'DISTINCT d',
|
?string $return = 'DISTINCT d',
|
||||||
?language $language = null,
|
?language $language = null,
|
||||||
|
?currency $currency = null,
|
||||||
array $parameters = [],
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): array|static {
|
): array|static {
|
||||||
|
@ -147,6 +152,9 @@ final class product extends core implements document_interface, collection_inter
|
||||||
// Initializing of the language parameter
|
// Initializing of the language parameter
|
||||||
if ($language instanceof language) $parameters['language'] = $language->name;
|
if ($language instanceof language) $parameters['language'] = $language->name;
|
||||||
|
|
||||||
|
// Initializing of the currency parameter
|
||||||
|
if ($currency instanceof currency) $parameters['currency'] = $currency->name;
|
||||||
|
|
||||||
// Initializing parameters for search
|
// Initializing parameters for search
|
||||||
if ($search) $parameters += [
|
if ($search) $parameters += [
|
||||||
'search' => $search,
|
'search' => $search,
|
||||||
|
@ -158,10 +166,10 @@ final class product extends core implements document_interface, collection_inter
|
||||||
sprintf(
|
sprintf(
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR d IN @@collection %s
|
FOR d IN @@collection %s
|
||||||
%s
|
%s
|
||||||
%s
|
%s
|
||||||
LIMIT @offset, @amount
|
LIMIT @offset, @amount
|
||||||
RETURN %s
|
RETURN %s
|
||||||
AQL,
|
AQL,
|
||||||
empty($search) ? '' : <<<'AQL'
|
empty($search) ? '' : <<<'AQL'
|
||||||
SEARCH
|
SEARCH
|
||||||
|
@ -196,8 +204,8 @@ final class product extends core implements document_interface, collection_inter
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($result instanceof _document) {
|
if ($amount === 1 && $result instanceof _document) {
|
||||||
// Found product
|
// Found product @todo need to rebuild this
|
||||||
|
|
||||||
// Initializing the object
|
// Initializing the object
|
||||||
$product = new static;
|
$product = new static;
|
||||||
|
@ -214,7 +222,7 @@ final class product extends core implements document_interface, collection_inter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return $result ?? [];
|
return is_array($result) ? $result : [$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
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\models;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use mirzaev\arming_bot\models\core,
|
||||||
|
mirzaev\arming_bot\models\cart,
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model of reservtion
|
||||||
|
*
|
||||||
|
* @used-by 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 reservation extends core implements document_interface, collection_interface
|
||||||
|
{
|
||||||
|
use document_trait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the collection in ArangoDB
|
||||||
|
*/
|
||||||
|
final public const string COLLECTION = 'reservation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of the collection in ArangoDB
|
||||||
|
*/
|
||||||
|
public const type TYPE = type::edge;
|
||||||
|
}
|
|
@ -7,20 +7,21 @@ 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\buffer,
|
mirzaev\arming_bot\models\traits\buffer,
|
||||||
|
mirzaev\arming_bot\models\traits\cart,
|
||||||
mirzaev\arming_bot\models\traits\document as 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\document as document_interface,
|
||||||
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
||||||
mirzaev\arming_bot\models\enumerations\language;
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -36,8 +37,9 @@ use exception;
|
||||||
*/
|
*/
|
||||||
final class session extends core implements document_interface, collection_interface
|
final class session extends core implements document_interface, collection_interface
|
||||||
{
|
{
|
||||||
use status, document_trait, buffer {
|
use status, document_trait, buffer, cart {
|
||||||
buffer::write as write;
|
buffer::write as write;
|
||||||
|
cart::initialize as cart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,13 +151,15 @@ final class session extends core implements document_interface, collection_inter
|
||||||
// Search for connected account
|
// Search for connected account
|
||||||
$result = collection::execute(
|
$result = collection::execute(
|
||||||
<<<AQL
|
<<<AQL
|
||||||
FOR v IN INBOUND @session GRAPH sessions
|
FOR v IN INBOUND @session GRAPH @graph
|
||||||
FILTER IS_SAME_COLLECTION(account, v._id)
|
FILTER IS_SAME_COLLECTION(@collection, v._id)
|
||||||
SORT v.created DESC
|
SORT v.created DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
RETURN v
|
RETURN v
|
||||||
AQL,
|
AQL,
|
||||||
[
|
[
|
||||||
|
'graph' => 'users',
|
||||||
|
'collection' => account::COLLECTION,
|
||||||
'session' => $this->getId()
|
'session' => $this->getId()
|
||||||
],
|
],
|
||||||
errors: $errors
|
errors: $errors
|
||||||
|
@ -172,6 +176,7 @@ final class session extends core implements document_interface, collection_inter
|
||||||
|
|
||||||
// Abstractioning of parameters
|
// Abstractioning of parameters
|
||||||
if (isset($result->language)) $result->language = language::{$result->language};
|
if (isset($result->language)) $result->language = language::{$result->language};
|
||||||
|
if (isset($result->currency)) $result->currency = currency::{$result->currency};
|
||||||
|
|
||||||
// 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);
|
||||||
|
@ -197,114 +202,6 @@ final class session extends core implements document_interface, collection_inter
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for a connected cart
|
|
||||||
*
|
|
||||||
* @param array &$errors Registry of errors
|
|
||||||
*
|
|
||||||
* @return cart|null An object implements the instance of the cart document from ArangoDB, if found
|
|
||||||
*/
|
|
||||||
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(cart::COLLECTION, cart::TYPE, errors: $errors)) {
|
|
||||||
// Initialized collections
|
|
||||||
|
|
||||||
// Search for connected cart
|
|
||||||
$result = collection::execute(
|
|
||||||
<<<AQL
|
|
||||||
FOR v IN INBOUND @session GRAPH sessions
|
|
||||||
FILTER IS_SAME_COLLECTION(cart, v._id) && v.active == true && v.ordered != true
|
|
||||||
SORT v.updated DESC, v.created DESC
|
|
||||||
LIMIT 1
|
|
||||||
RETURN v
|
|
||||||
AQL,
|
|
||||||
[
|
|
||||||
'session' => $this->getId()
|
|
||||||
],
|
|
||||||
errors: $errors
|
|
||||||
);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// Writing to the registry of errors
|
|
||||||
$errors[] = [
|
|
||||||
'text' => $e->getMessage(),
|
|
||||||
'file' => $e->getFile(),
|
|
||||||
'line' => $e->getLine(),
|
|
||||||
'stack' => $e->getTrace()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit (fail)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search by hash
|
* Search by hash
|
||||||
*
|
*
|
||||||
|
|
|
@ -9,7 +9,8 @@ use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\models\traits\document as 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\document as document_interface,
|
||||||
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
||||||
mirzaev\arming_bot\models\enumerations\language;
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -78,6 +79,7 @@ final class settings extends core implements document_interface, collection_inte
|
||||||
|
|
||||||
// Abstractioning of parameters
|
// Abstractioning of parameters
|
||||||
if (isset($result->language)) $result->language = language::{$result->language};
|
if (isset($result->language)) $result->language = language::{$result->language};
|
||||||
|
if (isset($result->currency)) $result->currency = currency::{$result->currency};
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
|
@ -18,13 +18,14 @@ use mirzaev\arangodb\collection,
|
||||||
use exception;
|
use exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait for buffer
|
* Buffer
|
||||||
*
|
*
|
||||||
* @uses collection_interface
|
* Storage of data in the document from ArangoDB
|
||||||
*
|
*
|
||||||
* @param static COLLECTION Name of the collection in ArangoDB
|
* @param static COLLECTION Name of the collection in ArangoDB
|
||||||
* @param static TYPE Type of the collection in ArangoDB
|
* @param static TYPE Type of the collection in ArangoDB
|
||||||
*
|
*
|
||||||
|
* @uses collection_interface
|
||||||
* @package mirzaev\arming_bot\models\traits
|
* @package mirzaev\arming_bot\models\traits
|
||||||
*
|
*
|
||||||
* @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
|
||||||
|
@ -33,12 +34,12 @@ use exception;
|
||||||
trait buffer
|
trait buffer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Write to buffer of the session
|
* Write to buffer of the document
|
||||||
*
|
*
|
||||||
* @param array $data Data for writing (merge)
|
* @param array $data Data for writing (merge)
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return bool Is data has written into the session document from ArangoDB?
|
* @return bool Is data has written into the document from ArangoDB?
|
||||||
*/
|
*/
|
||||||
public function write(array $data, array &$errors = []): bool
|
public function write(array $data, array &$errors = []): bool
|
||||||
{
|
{
|
||||||
|
@ -46,10 +47,10 @@ trait buffer
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
|
||||||
// The instance of the session document from ArangoDB is initialized?
|
// The instance of the document from ArangoDB is initialized?
|
||||||
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized');
|
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized');
|
||||||
|
|
||||||
// Writing data into buffer of the instance of the session document from ArangoDB
|
// Writing data into buffer of the instance of the document from ArangoDB
|
||||||
$this->document->buffer = array_replace_recursive($this->document->buffer ?? [], $data);
|
$this->document->buffer = array_replace_recursive($this->document->buffer ?? [], $data);
|
||||||
|
|
||||||
// Writing to ArangoDB and exit (success)
|
// Writing to ArangoDB and exit (success)
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\models\traits;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use mirzaev\arming_bot\models\interfaces\collection as collection_interface,
|
||||||
|
mirzaev\arming_bot\models\interfaces\document as document_interface,
|
||||||
|
mirzaev\arming_bot\models\traits\document as document_trait,
|
||||||
|
mirzaev\arming_bot\models\connect,
|
||||||
|
mirzaev\arming_bot\models\cart as model;
|
||||||
|
|
||||||
|
// Library для ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Framework for ArangoDB
|
||||||
|
use mirzaev\arangodb\collection,
|
||||||
|
mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
// Built-in libraries
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cart
|
||||||
|
*
|
||||||
|
* Cart for a document from ArangoDB
|
||||||
|
*
|
||||||
|
* @uses collection_interface
|
||||||
|
* @uses document_interface
|
||||||
|
* @package mirzaev\arming_bot\models\traits
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
trait cart
|
||||||
|
{
|
||||||
|
use document_trait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cart
|
||||||
|
*
|
||||||
|
* Search for a connected cart
|
||||||
|
*
|
||||||
|
* @param array &$errors Registry of errors
|
||||||
|
*
|
||||||
|
* @return model|null An object implements the instance of the cart document from ArangoDB, if found
|
||||||
|
*/
|
||||||
|
public function initialize(array &$errors = []): ?model
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) {
|
||||||
|
if (collection::initialize(model::COLLECTION, model::TYPE, errors: $errors)) {
|
||||||
|
// Initialized collections
|
||||||
|
|
||||||
|
// Search for connected cart
|
||||||
|
$result = collection::execute(
|
||||||
|
<<<AQL
|
||||||
|
FOR v IN 1..1 INBOUND @document GRAPH @graph
|
||||||
|
FILTER IS_SAME_COLLECTION(@cart, v._id) && v.active == true && v.ordered != true
|
||||||
|
SORT v.updated DESC, v.created DESC
|
||||||
|
LIMIT 1
|
||||||
|
RETURN v
|
||||||
|
AQL,
|
||||||
|
[
|
||||||
|
'cart' => model::COLLECTION,
|
||||||
|
'graph' => 'users',
|
||||||
|
'document' => $this->document->getId()
|
||||||
|
],
|
||||||
|
errors: $errors
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($result instanceof _document) {
|
||||||
|
// Found the cart
|
||||||
|
|
||||||
|
// Initializing the object
|
||||||
|
$cart = new model;
|
||||||
|
|
||||||
|
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(
|
||||||
|
model::COLLECTION,
|
||||||
|
[
|
||||||
|
'active' => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($result = collection::execute(
|
||||||
|
<<<'AQL'
|
||||||
|
FOR d IN @@collection
|
||||||
|
FILTER d._id == @_id && d.active == true
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
[
|
||||||
|
'@collection' => model::COLLECTION,
|
||||||
|
'_id' => $_id
|
||||||
|
],
|
||||||
|
errors: $errors
|
||||||
|
)) {
|
||||||
|
// Found the instance of just created new cart
|
||||||
|
|
||||||
|
// Initializing the object
|
||||||
|
$cart = new model;
|
||||||
|
|
||||||
|
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 document
|
||||||
|
$connected = $this->connect($cart, $errors);
|
||||||
|
|
||||||
|
if ($connected) {
|
||||||
|
// The cart has been connected to the document
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return $cart;
|
||||||
|
} else throw new exception('Failed to connect the ' . model::class . ' to the ' . static::class);
|
||||||
|
} else throw new exception('Class ' . model::class . ' does not implement a document from ArangoDB');
|
||||||
|
} else throw new exception('Failed to create or find just created ' . static::class);
|
||||||
|
}
|
||||||
|
} else throw new exception('Failed to initialize ' . model::TYPE . ' collection: ' . model::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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,9 @@ 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
|
||||||
*
|
*
|
||||||
|
* @uses document_interface
|
||||||
* @package mirzaev\arming_bot\models\traits
|
* @package mirzaev\arming_bot\models\traits
|
||||||
*
|
*
|
||||||
* @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
|
||||||
|
|
|
@ -41,7 +41,8 @@ $router
|
||||||
->write('/', 'catalog', 'index', 'GET')
|
->write('/', 'catalog', 'index', 'GET')
|
||||||
->write('/', 'catalog', 'index', 'POST')
|
->write('/', 'catalog', 'index', 'POST')
|
||||||
->write('/cart', 'cart', 'index', 'GET')
|
->write('/cart', 'cart', 'index', 'GET')
|
||||||
->write('/cart/write', 'cart', 'write', 'POST')
|
->write('/cart', 'cart', 'index', 'POST')
|
||||||
|
->write('/cart/product', 'cart', 'product', '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')
|
||||||
|
|
|
@ -35,7 +35,9 @@ import("/js/core.js").then(() =>
|
||||||
*/
|
*/
|
||||||
core.cart = class cart {
|
core.cart = class cart {
|
||||||
/**
|
/**
|
||||||
* Write or delete product from the cart (interface)
|
* Toggle
|
||||||
|
*
|
||||||
|
* Toggle the product in the cart (interface)
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} button Button of the product <a>
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
* @param {bool} force Ignore the damper? (false)
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
@ -54,7 +56,9 @@ import("/js/core.js").then(() =>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write or delete product from the cart (damper)
|
* Toggle
|
||||||
|
*
|
||||||
|
* Toggle the product in the cart (damper)
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} button Button of the product <a>
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
* @param {bool} force Ignore the damper? (false)
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
@ -63,12 +67,14 @@ import("/js/core.js").then(() =>
|
||||||
*/
|
*/
|
||||||
static toggle_damper = core.damper(
|
static toggle_damper = core.damper(
|
||||||
(...variables) => this.toggle_system(...variables),
|
(...variables) => this.toggle_system(...variables),
|
||||||
800,
|
300,
|
||||||
1,
|
2,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write or delete product from the cart (system)
|
* Toggle
|
||||||
|
*
|
||||||
|
* Toggle the product in the cart (system)
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} button Button of the product <a>
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
*
|
*
|
||||||
|
@ -80,15 +86,23 @@ import("/js/core.js").then(() =>
|
||||||
if (button instanceof HTMLElement) {
|
if (button instanceof HTMLElement) {
|
||||||
// Validated
|
// Validated
|
||||||
|
|
||||||
|
// Initializing of the wrap of buttons
|
||||||
|
const wrap = button.parentElement;
|
||||||
|
|
||||||
|
// Initializing of the product
|
||||||
|
const product = wrap.parentElement;
|
||||||
|
|
||||||
// Initializing of identifier of the product
|
// Initializing of identifier of the product
|
||||||
const identifier = button.getAttribute("data-product-identifier");
|
const identifier = product.getAttribute(
|
||||||
|
"data-product-identifier",
|
||||||
|
);
|
||||||
|
|
||||||
if (typeof identifier === "string" && identifier.length > 0) {
|
if (typeof identifier === "string" && identifier.length > 0) {
|
||||||
// Validated identifier
|
// Validated identifier
|
||||||
|
|
||||||
return await core.request(
|
return await core.request(
|
||||||
"/cart/write",
|
"/cart/product",
|
||||||
`product=${identifier}`,
|
`identifier=${identifier}&type=toggle`,
|
||||||
)
|
)
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
if (
|
if (
|
||||||
|
@ -103,16 +117,388 @@ import("/js/core.js").then(() =>
|
||||||
// Unblocking the button
|
// Unblocking the button
|
||||||
button.removeAttribute("disabled");
|
button.removeAttribute("disabled");
|
||||||
|
|
||||||
if (json.status) {
|
// Writing offset of hue-rotate to indicate that the product is in the cart
|
||||||
// The product in the cart
|
wrap.style.setProperty(
|
||||||
|
"--hue-rotate-offset",
|
||||||
|
json.amount + '0deg',
|
||||||
|
);
|
||||||
|
|
||||||
// Writing style of added to the cart button
|
// Writing attribute with amount of the product in the cart
|
||||||
button.classList.add("cart");
|
product.setAttribute(
|
||||||
} else {
|
"data-product-amount",
|
||||||
// The product is not in the cart
|
json.amount,
|
||||||
|
);
|
||||||
|
|
||||||
// Deleting style of added to the cart button
|
// Initializing the amount <span> element
|
||||||
button.classList.remove("cart");
|
const amount = wrap.querySelector(
|
||||||
|
'span[data-product-button-text="amount"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (amount instanceof HTMLElement) {
|
||||||
|
// Initialized the amount <span> element
|
||||||
|
|
||||||
|
// Writing amount of the product in the cart
|
||||||
|
amount.innerText = json.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write
|
||||||
|
*
|
||||||
|
* Write the product in the cart (interface)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of writings
|
||||||
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
*
|
||||||
|
* @return {bool} True if an error occurs to continue the event execution
|
||||||
|
*/
|
||||||
|
static write(button, amount = 1, force = false) {
|
||||||
|
// Blocking the button
|
||||||
|
button.setAttribute("disabled", "true");
|
||||||
|
|
||||||
|
// Execute under damper
|
||||||
|
this.write_damper(button, amount, force);
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write
|
||||||
|
*
|
||||||
|
* Write the product in the cart (damper)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of writings
|
||||||
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
static write_damper = core.damper(
|
||||||
|
(...variables) => this.write_system(...variables),
|
||||||
|
300,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write
|
||||||
|
*
|
||||||
|
* Write the product in the cart (system)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of writings
|
||||||
|
*
|
||||||
|
* @return {Promise} Request to the server
|
||||||
|
*
|
||||||
|
* @todo add unblocking button by timer + everywhere
|
||||||
|
*/
|
||||||
|
static async write_system(button, amount = 1) {
|
||||||
|
if (
|
||||||
|
button instanceof HTMLElement &&
|
||||||
|
typeof amount === "number" &&
|
||||||
|
amount > -1 &&
|
||||||
|
amount < 100
|
||||||
|
) {
|
||||||
|
// Validated
|
||||||
|
|
||||||
|
// Initializing of the wrap of buttons
|
||||||
|
const wrap = button.parentElement;
|
||||||
|
|
||||||
|
// Initializing of the product
|
||||||
|
const product = wrap.parentElement;
|
||||||
|
|
||||||
|
// Initializing of identifier of the product
|
||||||
|
const identifier = product.getAttribute(
|
||||||
|
"data-product-identifier",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof identifier === "string" && identifier.length > 0) {
|
||||||
|
// Validated identifier
|
||||||
|
|
||||||
|
return await core.request(
|
||||||
|
"/cart/product",
|
||||||
|
`identifier=${identifier}&type=write&amount=${amount}`,
|
||||||
|
)
|
||||||
|
.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");
|
||||||
|
|
||||||
|
// Writing offset of hue-rotate to indicate that the product is in the cart
|
||||||
|
wrap.style.setProperty(
|
||||||
|
"--hue-rotate-offset",
|
||||||
|
json.amount + '0deg',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing attribute with amount of the product in the cart
|
||||||
|
product.setAttribute(
|
||||||
|
"data-product-amount",
|
||||||
|
json.amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initializing the amount <span> element
|
||||||
|
const amount = wrap.querySelector(
|
||||||
|
'span[data-product-button-text="amount"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (amount instanceof HTMLElement) {
|
||||||
|
// Initialized the amount <span> element
|
||||||
|
|
||||||
|
// Writing amount of the product in the cart
|
||||||
|
amount.innerText = json.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete
|
||||||
|
*
|
||||||
|
* Delete the product from the cart (interface)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of deletings
|
||||||
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
*
|
||||||
|
* @return {bool} True if an error occurs to continue the event execution
|
||||||
|
*/
|
||||||
|
static delete(button, amount = 1, force = false) {
|
||||||
|
// Blocking the button
|
||||||
|
button.setAttribute("disabled", "true");
|
||||||
|
|
||||||
|
// Execute under damper
|
||||||
|
this.delete_damper(button, amount, force);
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete
|
||||||
|
*
|
||||||
|
* Delete the product from the cart (damper)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of deletings
|
||||||
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
static delete_damper = core.damper(
|
||||||
|
(...variables) => this.delete_system(...variables),
|
||||||
|
300,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete
|
||||||
|
*
|
||||||
|
* Delete the product from the cart (system)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of deletings
|
||||||
|
*
|
||||||
|
* @return {Promise} Request to the server
|
||||||
|
*
|
||||||
|
* @todo add unblocking button by timer + everywhere
|
||||||
|
*/
|
||||||
|
static async delete_system(button, amount = 1) {
|
||||||
|
if (
|
||||||
|
button instanceof HTMLElement &&
|
||||||
|
typeof amount === "number" &&
|
||||||
|
amount > 0 &&
|
||||||
|
amount < 101
|
||||||
|
) {
|
||||||
|
// Validated
|
||||||
|
|
||||||
|
// Initializing of the wrap of buttons
|
||||||
|
const wrap = button.parentElement;
|
||||||
|
|
||||||
|
// Initializing of the product
|
||||||
|
const product = wrap.parentElement;
|
||||||
|
|
||||||
|
// Initializing of identifier of the product
|
||||||
|
const identifier = product.getAttribute(
|
||||||
|
"data-product-identifier",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof identifier === "string" && identifier.length > 0) {
|
||||||
|
// Validated identifier
|
||||||
|
|
||||||
|
return await core.request(
|
||||||
|
"/cart/product",
|
||||||
|
`identifier=${identifier}&type=delete&amount=${amount}`,
|
||||||
|
)
|
||||||
|
.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");
|
||||||
|
|
||||||
|
// Writing offset of hue-rotate to indicate that the product is in the cart
|
||||||
|
wrap.style.setProperty(
|
||||||
|
"--hue-rotate-offset",
|
||||||
|
json.amount + '0deg',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing attribute with amount of the product in the cart
|
||||||
|
product.setAttribute(
|
||||||
|
"data-product-amount",
|
||||||
|
json.amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initializing the amount <span> element
|
||||||
|
const amount = wrap.querySelector(
|
||||||
|
'span[data-product-button-text="amount"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (amount instanceof HTMLElement) {
|
||||||
|
// Initialized the amount <span> element
|
||||||
|
|
||||||
|
// Writing amount of the product in the cart
|
||||||
|
amount.innerText = json.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set
|
||||||
|
*
|
||||||
|
* Set amount of the product in the cart (interface)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of the product in the cart to be setted
|
||||||
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
*
|
||||||
|
* @return {bool} True if an error occurs to continue the event execution
|
||||||
|
*/
|
||||||
|
static set(button, amount = 1, force = false) {
|
||||||
|
// Blocking the button
|
||||||
|
button.setAttribute("disabled", "true");
|
||||||
|
|
||||||
|
// Execute under damper
|
||||||
|
this.set_damper(button, amount, force);
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set
|
||||||
|
*
|
||||||
|
* Set the product in the cart (damper)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of the product in the cart to be setted
|
||||||
|
* @param {bool} force Ignore the damper? (false)
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
static set_damper = core.damper(
|
||||||
|
(...variables) => this.set_system(...variables),
|
||||||
|
300,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set
|
||||||
|
*
|
||||||
|
* Set the product in the cart (system)
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} button Button of the product <a>
|
||||||
|
* @param {number} amount Amount of the product in the cart to be setted
|
||||||
|
*
|
||||||
|
* @return {Promise} Request to the server
|
||||||
|
*
|
||||||
|
* @todo add unblocking button by timer + everywhere
|
||||||
|
*/
|
||||||
|
static async set_system(button, amount = 1) {
|
||||||
|
if (
|
||||||
|
button instanceof HTMLElement &&
|
||||||
|
typeof amount === "number" &&
|
||||||
|
amount > -1 &&
|
||||||
|
amount < 101
|
||||||
|
) {
|
||||||
|
// Validated
|
||||||
|
|
||||||
|
// Initializing of the wrap of buttons
|
||||||
|
const wrap = button.parentElement;
|
||||||
|
|
||||||
|
// Initializing of the product
|
||||||
|
const product = wrap.parentElement;
|
||||||
|
|
||||||
|
// Initializing of identifier of the product
|
||||||
|
const identifier = product.getAttribute(
|
||||||
|
"data-product-identifier",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof identifier === "string" && identifier.length > 0) {
|
||||||
|
// Validated identifier
|
||||||
|
|
||||||
|
return await core.request(
|
||||||
|
"/cart/product",
|
||||||
|
`identifier=${identifier}&type=set&amount=${amount}`,
|
||||||
|
)
|
||||||
|
.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");
|
||||||
|
|
||||||
|
// Writing offset of hue-rotate to indicate that the product is in the cart
|
||||||
|
wrap.style.setProperty(
|
||||||
|
"--hue-rotate-offset",
|
||||||
|
json.amount + '0deg',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing attribute with amount of the product in the cart
|
||||||
|
product.setAttribute(
|
||||||
|
"data-product-amount",
|
||||||
|
json.amount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initializing the amount <span> element
|
||||||
|
const amount = wrap.querySelector(
|
||||||
|
'span[data-product-button-text="amount"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (amount instanceof HTMLElement) {
|
||||||
|
// Initialized the amount <span> element
|
||||||
|
|
||||||
|
// Writing amount of the product in the cart
|
||||||
|
amount.innerText = json.amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -137,17 +137,44 @@ 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 {
|
main>section#products>div.column>article.product>div[data-product="buttons"]:last-of-type {
|
||||||
filter: hue-rotate(120deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
main>section#products>div.column>article.product>button:last-of-type {
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
height: 33px;
|
height: 33px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
}
|
||||||
color: var(--tg-theme-button-text-color);
|
|
||||||
background-color: var(--tg-theme-button-color);
|
main>section#products>div.column>article.product[data-product-amount]:not(:is([data-product-amount="0"], [data-product-amount="1"]))>div[data-product="buttons"]:last-of-type {
|
||||||
|
container-type: inline-size;
|
||||||
|
container-name: product-buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"] {
|
||||||
|
padding: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product:is([data-product-amount="0"], [data-product-amount="1"])>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-button-text="amount"],
|
||||||
|
main>section#products>div.column>article.product[data-product-amount="0"]>div[data-product="buttons"]>button:is([data-product-button="write"], [data-product-button="delete"]) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-button-text="amount"]:after {
|
||||||
|
content: '*';
|
||||||
|
margin: 0 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product[data-product-amount]:not([data-product-amount="0"])>div[data-product="buttons"] {
|
||||||
|
filter: hue-rotate(calc(120deg + var(--hue-rotate-offset, 0deg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@container product-buttons (max-width: 200px) {
|
||||||
|
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"]>span:is([data-product-button-text="cost"], [data-product-button-text="currency"]) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-button-text="amount"]:after {
|
||||||
|
content: unset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
i.icon.minus {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 16px;
|
||||||
|
height: 2px;
|
||||||
|
background: currentColor;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
i.icon.plus,
|
||||||
|
i.icon.plus::after {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: currentColor;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
i.icon.plus {
|
||||||
|
margin-top: -2px;
|
||||||
|
position: relative;
|
||||||
|
width: 16px;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
i.icon.plus::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 2px;
|
||||||
|
height: 16px;
|
||||||
|
top: -7px;
|
||||||
|
left: 7px;
|
||||||
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ search:has(input:disabled) {
|
||||||
backdrop-filter: contrast(0.5);
|
backdrop-filter: contrast(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
*[type="button"] {
|
button, *[type="button"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +144,8 @@ search:has(input:disabled) {
|
||||||
|
|
||||||
button {
|
button {
|
||||||
height: 33px;
|
height: 33px;
|
||||||
|
color: var(--tg-theme-button-text-color);
|
||||||
|
background-color: var(--tg-theme-button-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a[type="button"] {
|
a[type="button"] {
|
||||||
|
|
|
@ -7,7 +7,10 @@ namespace mirzaev\arming_bot\views;
|
||||||
// Files of the project
|
// Files of the project
|
||||||
use mirzaev\arming_bot\models\session,
|
use mirzaev\arming_bot\models\session,
|
||||||
mirzaev\arming_bot\models\account,
|
mirzaev\arming_bot\models\account,
|
||||||
mirzaev\arming_bot\models\settings;
|
mirzaev\arming_bot\models\settings,
|
||||||
|
mirzaev\arming_bot\models\cart,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
|
mirzaev\arming_bot\models\enumerations\currency;
|
||||||
|
|
||||||
// Framework for PHP
|
// Framework for PHP
|
||||||
use mirzaev\minimal\controller;
|
use mirzaev\minimal\controller;
|
||||||
|
@ -46,28 +49,35 @@ final class templater extends controller implements ArrayAccess
|
||||||
*
|
*
|
||||||
* @param session|null $session The object implementing a session instance from ArangoDB
|
* @param session|null $session The object implementing a session instance from ArangoDB
|
||||||
* @param account|null $account The object implementing an account instance from ArangoDB
|
* @param account|null $account The object implementing an account instance from ArangoDB
|
||||||
* @param settings|null $settings The object implementing an account instance from ArangoDB
|
* @param settings|null $settings The object implementing a settings instance from ArangoDB
|
||||||
|
* @param cart|null $cart The object implementing a cart instance from ArangoDB
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
?session $session = null,
|
?session $session = null,
|
||||||
?account $account = null,
|
?account $account = null,
|
||||||
?settings $settings = null
|
?settings $settings = null,
|
||||||
|
?cart $cart = null
|
||||||
) {
|
) {
|
||||||
// Initializing of an instance of twig
|
// Initializing an instance of twig
|
||||||
$this->twig = new twig(new FilesystemLoader(VIEWS));
|
$this->twig = new twig(new FilesystemLoader(VIEWS));
|
||||||
|
|
||||||
// Initializing of global variables
|
// Declaring buffers for initializinf global variables
|
||||||
|
$language = $currency = null;
|
||||||
|
|
||||||
|
// Initializing global variables
|
||||||
$this->twig->addGlobal('theme', 'default');
|
$this->twig->addGlobal('theme', 'default');
|
||||||
$this->twig->addGlobal('server', $_SERVER);
|
$this->twig->addGlobal('server', $_SERVER);
|
||||||
$this->twig->addGlobal('cookies', $_COOKIE);
|
$this->twig->addGlobal('cookies', $_COOKIE);
|
||||||
$this->twig->addGlobal('settings', $settings);
|
$this->twig->addGlobal('settings', $settings);
|
||||||
if (!empty($session?->status())) $this->twig->addGlobal('session', $session);
|
if (!empty($session?->status())) $this->twig->addGlobal('session', $session);
|
||||||
if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
|
if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
|
||||||
$this->twig->addGlobal('language', $account?->language->name ?? $settings?->language->name ?? 'en');
|
$this->twig->addGlobal('language', $language = $account?->language ?? $session?->buffer['language'] ?? $settings?->language ?? language::en);
|
||||||
|
$this->twig->addGlobal('currency', $currency = $account?->currency ?? $session?->buffer['currency'] ?? $settings?->currency ?? currency::usd);
|
||||||
|
$this->twig->addGlobal('cart', $cart->all(language: $language, currency: $currency));
|
||||||
|
|
||||||
// Initialize function of dimensions formattinx
|
// Initialize function of dimensions formatting
|
||||||
$this->twig->addFunction(
|
$this->twig->addFunction(
|
||||||
new TwigFunction(
|
new TwigFunction(
|
||||||
'format_dimensions',
|
'format_dimensions',
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search(event))"
|
onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search(event))"
|
||||||
tabindex="3">
|
tabindex="3">
|
||||||
{% if category.images %}
|
{% if category.images %}
|
||||||
<img src="{{ category.images.0.storage }}" alt="{{ category.name[language] }}" ondrugstart="return false;">
|
<img src="{{ category.images.0.storage }}" alt="{{ category.name }}" ondrugstart="return false;">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>{{ category.name[language] }}</p>
|
<p>{{ category.name }}</p>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
{% set buffer_brand = account.buffer.catalog.filters.brand ?? session.buffer.catalog.filters.brand %}
|
{% set buffer_brand = account.buffer.catalog.filters.brand ?? session.buffer.catalog.filters.brand %}
|
||||||
<input name="brand" type="radio" id="brand_title" {% if buffer_brand is empty %} checked{% endif %}>
|
<input name="brand" type="radio" id="brand_title" {% if buffer_brand is empty %} checked{% endif %}>
|
||||||
<label for="brand_title" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
<label for="brand_title" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
||||||
{{ language == 'ru' ? 'Бренд' : 'Brand' }}
|
{{ language.name == 'ru' ? 'Бренд' : 'Brand' }}
|
||||||
</label>
|
</label>
|
||||||
<input name="brand" type="radio" id="brand_all">
|
<input name="brand" type="radio" id="brand_all">
|
||||||
<label for="brand_all" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
<label for="brand_all" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)">
|
||||||
{{ language == 'ru' ? 'Все бренды' : 'All brands' }}
|
{{ language.name == 'ru' ? 'Все бренды' : 'All brands' }}
|
||||||
</label>
|
</label>
|
||||||
{% for brand in filters.brands %}
|
{% for brand in filters.brands %}
|
||||||
<input name="brand" type="radio" id="brand_{{ loop.index }}" {% if brand == buffer_brand %} checked{% endif %}>
|
<input name="brand" type="radio" id="brand_{{ loop.index }}" {% if brand == buffer_brand %} checked{% endif %}>
|
||||||
|
|
|
@ -1,19 +1,25 @@
|
||||||
{% macro card(product) %}
|
{% macro card(product) %}
|
||||||
{% set title = product.name[language] ~ ' ' ~ product.brand[language] ~ format_dimensions(product.dimensions.x,
|
{% set title = product.name ~ ' ' ~ product.brand ~ format_dimensions(product.dimensions.x,
|
||||||
product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' %}
|
product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' %}
|
||||||
<article id="{{ product.getId() }}" class="product unselectable">
|
{% set amount = cart[product.getId()].amount ?? 0 %}
|
||||||
<a data-product-identifier="{{ product.identifier }}" href="?product={{ product.identifier }}"
|
<article id="{{ product.getId() }}" class="product unselectable" data-product-identifier="{{ product.identifier }}"
|
||||||
onclick="return core.catalog.product(this);" onkeydown="event.keyCode === 13 && core.catalog.product(this)"
|
data-product-amount="{{ amount }}">
|
||||||
tabindex="10">
|
<a data-product="cover" href="?product={{ product.identifier }}" onclick="return core.catalog.product(this);"
|
||||||
<img src="{{ product.images.0.storage }}" alt="{{ product.name[language] }}" ondrugstart="return false;">
|
onkeydown="event.keyCode === 13 && core.catalog.product(this)" tabindex="10">
|
||||||
<p class="title" title="{{ product.name[language] }}">
|
<img src="{{ product.images.0.storage }}" alt="{{ product.name }}" ondrugstart="return false;">
|
||||||
|
<p class="title" title="{{ product.name }}">
|
||||||
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<button title="Добавить в корзину" onclick="core.cart.toggle(this)" data-product-identifier="{{ product.identifier }}"
|
<div data-product="buttons"{% if amount > 0 %} style="--hue-rotate-offset: {{ amount }}0deg;"{% endif %}>
|
||||||
data-product-cost="{{ product.cost }}" tabindex="15">
|
<button data-product-button="delete" onclick="core.cart.delete(this, 1)"><i class="icon minus"></i></button>
|
||||||
{{ product.cost }}р
|
<button data-product-button="toggle" onclick="core.cart.toggle(this)" tabindex="15">
|
||||||
</button>
|
<span data-product-button-text="amount">{{ amount }}</span>
|
||||||
|
<span data-product-button-text="cost">{{ product.cost }}</span>
|
||||||
|
<span data-product-button-text="currency">{{ currency.symbol }}</span>
|
||||||
|
</button>
|
||||||
|
<button data-product-button="write" onclick="core.cart.write(this, 1)"><i class="icon plus"></i></button>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
{% if products is not empty %}
|
{% if products is not empty %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<search id="search">
|
<search id="search">
|
||||||
<label class="unselectable"><i class="icon search"></i></label>
|
<label class="unselectable"><i class="icon search"></i></label>
|
||||||
{% set buffer_search = account.buffer.catalog.search.text ?? session.buffer.catalog.search.text %}
|
{% set buffer_search = account.buffer.catalog.search.text ?? session.buffer.catalog.search.text %}
|
||||||
<input placeholder="Поиск по каталогу" type="search" tabindex="1"
|
<input placeholder="{{ language.name == 'ru' ? 'Поиск по каталогу' : 'Search in the catalog' }}" type="search" tabindex="1"
|
||||||
onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); core.catalog.search(event, this)"
|
onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); core.catalog.search(event, this)"
|
||||||
{% if buffer_search is not empty %} value="{{ buffer_search }}" {% endif %} />
|
{% if buffer_search is not empty %} value="{{ buffer_search }}" {% endif %} />
|
||||||
</search>
|
</search>
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/close.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/close.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/plus.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/minus.css" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
Loading…
Reference in New Issue