import from excel + broken dependencies see next commit
This commit is contained in:
parent
f69d6ae96c
commit
af48d505bf
71
README.md
71
README.md
|
@ -6,39 +6,36 @@ Basis for developing chat-robots with "Web App" technology for Telegram
|
|||
|
||||
### AnangoDB
|
||||
|
||||
1. Create a View in ArangoDB for the document "product"
|
||||
`
|
||||
"links": {
|
||||
"product": {
|
||||
"fields": {
|
||||
"description": {
|
||||
"analyzers": [
|
||||
"text_ru"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
2. Create a Graph with the specified values
|
||||
**Name:** hierarchy
|
||||
1. Create a Graph with the specified values
|
||||
**Name:** catalog
|
||||
|
||||
**edgeDefinition:** entry
|
||||
**fromCollections:** part, product...
|
||||
**toCollections:** category, part...
|
||||
**fromCollections:** categoy, product
|
||||
**toCollections:** category
|
||||
|
||||
2. Create a Graph with the specified values
|
||||
**Name:** sessions
|
||||
|
||||
**edgeDefinition:** connect
|
||||
**fromCollections:** account
|
||||
**toCollections:** session
|
||||
|
||||
3. Create indexes for the "product" collection
|
||||
**Type:** "Inverted Index"
|
||||
**Fields:** title.RU
|
||||
**Fields:** name.ru
|
||||
**Analyzer:** "text_ru"
|
||||
**Search field:** true
|
||||
**Name:** title_ru
|
||||
**Name:** name_ru
|
||||
|
||||
*Add indexes for all search parameters and for all languages (search language is selected based on the user's language,
|
||||
otherwise from the default language specified in the active settings from **settings** collection document)*
|
||||
|
||||
*See fields in the `mirzaev/arming_bot/models/product`
|
||||
**name.ru**, **description.ru** and **compatibility.ru***
|
||||
|
||||
4. Create a View with the specified values
|
||||
**Name:** products_search
|
||||
|
||||
**type:** search-alias (you can also use "arangosearch")
|
||||
**name:** **product**s_search
|
||||
**indexes:**
|
||||
`
|
||||
"indexes": [
|
||||
|
@ -51,19 +48,33 @@ Basis for developing chat-robots with "Web App" technology for Telegram
|
|||
|
||||
### NGINX
|
||||
|
||||
1. Add this to a NGINX config: `try_files $uri $uri/ /index.php;`
|
||||
|
||||
2. Add this to a NGINX config
|
||||
1. Example of NGINX server file
|
||||
`
|
||||
location /images {
|
||||
alias /PATH/TO/public/themes/default/images;
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php;
|
||||
}
|
||||
|
||||
location ~ /(?<type>categories|products) {
|
||||
root /var/www/arming_bot/mirzaev/arming_bot/system/storage;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
...
|
||||
}
|
||||
`
|
||||
|
||||
### SystemD (or any alternative you like)
|
||||
|
||||
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*
|
||||
*the configuration file is very simple and you can remake it for any alternative to SystemD that you like*
|
||||
|
||||
## Settings
|
||||
Settings of chat-robot and Web App
|
||||
|
||||
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"
|
||||
`
|
||||
{
|
||||
"status": "active"
|
||||
|
@ -78,7 +89,7 @@ Language for system messages if user language could not be determined
|
|||
## Suspensions
|
||||
System of suspensions of chat-robot and Web App
|
||||
|
||||
Make sure you have a "suspension" collection (can be created automatically)
|
||||
Make sure you have a **suspension** collection (can be created automatically)
|
||||
`
|
||||
{
|
||||
"end": 1726068961,
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"twig/twig": "^3.10",
|
||||
"twig/extra-bundle": "^3.7",
|
||||
"twig/intl-extra": "^3.10",
|
||||
"phpoffice/phpspreadsheet": "^2.1"
|
||||
"avadim/fast-excel-reader": "^2.19"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -4,8 +4,120 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "61697600b2677e22a6133cff9768d9eb",
|
||||
"content-hash": "e580e133abf1c4a60898f255e006a3e9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "avadim/fast-excel-helper",
|
||||
"version": "v1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aVadim483/fast-excel-helper.git",
|
||||
"reference": "2792c4a20b529b537ede7148e2c29dc4677818db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aVadim483/fast-excel-helper/zipball/2792c4a20b529b537ede7148e2c29dc4677818db",
|
||||
"reference": "2792c4a20b529b537ede7148e2c29dc4677818db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-zip": "*",
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"avadim\\FastExcelHelper\\": "./src/FastExcelHelper"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Helper for avadim/fast-excel-reader and avadim/fast-excel-writer",
|
||||
"homepage": "https://github.com/aVadim483/fast-excel-helper",
|
||||
"keywords": [
|
||||
"MS Office",
|
||||
"PHPExcel",
|
||||
"excel",
|
||||
"library",
|
||||
"office 2007",
|
||||
"parser",
|
||||
"php",
|
||||
"reader",
|
||||
"spreadsheet",
|
||||
"writer",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/aVadim483/fast-excel-helper/issues",
|
||||
"source": "https://github.com/aVadim483/fast-excel-helper/tree/v1.2.1"
|
||||
},
|
||||
"time": "2024-09-06T20:37:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "avadim/fast-excel-reader",
|
||||
"version": "v2.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aVadim483/fast-excel-reader.git",
|
||||
"reference": "e2fa125a93c0c5115344ebab1a36b81cd2be63c8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aVadim483/fast-excel-reader/zipball/e2fa125a93c0c5115344ebab1a36b81cd2be63c8",
|
||||
"reference": "e2fa125a93c0c5115344ebab1a36b81cd2be63c8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"avadim/fast-excel-helper": "^1.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-zip": "*",
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"avadim\\FastExcelReader\\": "./src/FastExcelReader"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Lightweight and very fast XLSX Excel Spreadsheet Reader in PHP",
|
||||
"homepage": "https://github.com/aVadim483/fast-excel-reader",
|
||||
"keywords": [
|
||||
"MS Office",
|
||||
"PHPExcel",
|
||||
"excel",
|
||||
"import",
|
||||
"library",
|
||||
"office 2007",
|
||||
"parser",
|
||||
"php",
|
||||
"reader",
|
||||
"spreadsheet",
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/aVadim483/fast-excel-reader/issues",
|
||||
"source": "https://github.com/aVadim483/fast-excel-reader/tree/v2.19.0"
|
||||
},
|
||||
"time": "2024-09-22T19:34:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "badfarm/zanzara",
|
||||
"version": "0.9.1",
|
||||
|
@ -584,194 +696,6 @@
|
|||
},
|
||||
"time": "2024-08-02T07:48:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maennchen/ZipStream-PHP.git",
|
||||
"reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
|
||||
"reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"ext-zlib": "*",
|
||||
"php-64bit": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-zip": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3.16",
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"php-coveralls/php-coveralls": "^2.5",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"vimeo/psalm": "^5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"guzzlehttp/psr7": "^2.4",
|
||||
"psr/http-message": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ZipStream\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paul Duncan",
|
||||
"email": "pabs@pablotron.org"
|
||||
},
|
||||
{
|
||||
"name": "Jonatan Männchen",
|
||||
"email": "jonatan@maennchen.ch"
|
||||
},
|
||||
{
|
||||
"name": "Jesse Donat",
|
||||
"email": "donatj@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "András Kolesár",
|
||||
"email": "kolesar@kolesar.hu"
|
||||
}
|
||||
],
|
||||
"description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
|
||||
"keywords": [
|
||||
"stream",
|
||||
"zip"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
|
||||
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/maennchen",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://opencollective.com/zipstream",
|
||||
"type": "open_collective"
|
||||
}
|
||||
],
|
||||
"time": "2023-06-21T14:59:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/complex",
|
||||
"version": "3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPComplex.git",
|
||||
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Complex\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@lange.demon.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with complex numbers",
|
||||
"homepage": "https://github.com/MarkBaker/PHPComplex",
|
||||
"keywords": [
|
||||
"complex",
|
||||
"mathematics"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
|
||||
},
|
||||
"time": "2022-12-06T16:21:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/matrix",
|
||||
"version": "3.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPMatrix.git",
|
||||
"reference": "728434227fe21be27ff6d86621a1b13107a2562c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
|
||||
"reference": "728434227fe21be27ff6d86621a1b13107a2562c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpdocumentor/phpdocumentor": "2.*",
|
||||
"phploc/phploc": "^4.0",
|
||||
"phpmd/phpmd": "2.*",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"sebastian/phpcpd": "^4.0",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Matrix\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@demon-angel.eu"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with matrices",
|
||||
"homepage": "https://github.com/MarkBaker/PHPMatrix",
|
||||
"keywords": [
|
||||
"mathematics",
|
||||
"matrix",
|
||||
"vector"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
|
||||
},
|
||||
"time": "2022-12-02T22:17:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mirzaev/arangodb",
|
||||
"version": "1.3.0",
|
||||
|
@ -1417,110 +1341,6 @@
|
|||
},
|
||||
"time": "2024-09-04T13:22:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "2.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||
"reference": "ffbcee68069b073bff07a71eb321dcd9f2763513"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/ffbcee68069b073bff07a71eb321dcd9f2763513",
|
||||
"reference": "ffbcee68069b073bff07a71eb321dcd9f2763513",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||
"markbaker/complex": "^3.0",
|
||||
"markbaker/matrix": "^3.0",
|
||||
"php": "^8.1",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
|
||||
"dompdf/dompdf": "^2.0 || ^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"mitoteam/jpgraph": "^10.3",
|
||||
"mpdf/mpdf": "^8.1.1",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^9.6 || ^10.5",
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"tecnickcom/tcpdf": "^6.5"
|
||||
},
|
||||
"suggest": {
|
||||
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||
"ext-intl": "PHP Internationalization Functions",
|
||||
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "https://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"homepage": "https://markbakeruk.net"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "https://rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
},
|
||||
{
|
||||
"name": "Adrien Crivelli"
|
||||
}
|
||||
],
|
||||
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||
"keywords": [
|
||||
"OpenXML",
|
||||
"excel",
|
||||
"gnumeric",
|
||||
"ods",
|
||||
"php",
|
||||
"spreadsheet",
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.2.2"
|
||||
},
|
||||
"time": "2024-08-08T02:31:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/cache",
|
||||
"version": "3.0.0",
|
||||
|
@ -1668,58 +1488,6 @@
|
|||
},
|
||||
"time": "2019-01-08T18:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
"homepage": "https://github.com/php-fig/http-client",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-client",
|
||||
"psr",
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"time": "2023-09-23T14:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
|
@ -1878,57 +1646,6 @@
|
|||
},
|
||||
"time": "2021-05-03T11:20:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/simple-cache",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/simple-cache.git",
|
||||
"reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
|
||||
"reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\SimpleCache\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for simple caching",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"caching",
|
||||
"psr",
|
||||
"psr-16",
|
||||
"simple-cache"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
|
||||
},
|
||||
"time": "2021-10-29T13:26:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "react/cache",
|
||||
"version": "v1.2.0",
|
||||
|
|
|
@ -6,7 +6,8 @@ namespace mirzaev\arming_bot\controllers;
|
|||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\controllers\core,
|
||||
mirzaev\arming_bot\models\categories,
|
||||
mirzaev\arming_bot\models\catalog as model,
|
||||
mirzaev\arming_bot\models\entry,
|
||||
mirzaev\arming_bot\models\category,
|
||||
mirzaev\arming_bot\models\product;
|
||||
|
||||
|
@ -34,54 +35,43 @@ final class catalog extends core
|
|||
*/
|
||||
public function index(array $parameters = []): ?string
|
||||
{
|
||||
if (!empty($parameters['categories']) && $parameters['categories'] !== ['/']) {
|
||||
// Переданы категории ["category1", "category2", "category3"] (иерархия)
|
||||
if (!empty($parameters['category'])) {
|
||||
// Передана категория (идентификатор)
|
||||
|
||||
// Инициализация актуальной категории
|
||||
$category = end($parameters['categories']);
|
||||
$category = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => $parameters['category']]));
|
||||
|
||||
if ($model = categories::category($category)) {
|
||||
// Найдена модель обработки актуальной категории
|
||||
if ($category instanceof category) {
|
||||
// Found the category
|
||||
|
||||
if (method_exists($model, 'entries')) {
|
||||
// Найден метод поиска вхождений
|
||||
// Поиск категорий или товаров входящих в актуальную категорию
|
||||
$entries = entry::search(
|
||||
document: $category,
|
||||
amount: 30,
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
|
||||
// Поиск категорий или товаров входящих в актуальную категорию
|
||||
$entries = $model::entries(
|
||||
category: $model->getId(),
|
||||
filter: 'v.deleted != true && v.hidden != true',
|
||||
amount: 30,
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
// Объявление буферов категорий и товаров (важно - в единственном числе, по параметру из базы данных)
|
||||
$category = $product = [];
|
||||
|
||||
// Объявление буферов категорий и товаров (важно - в единственном числе, по параметру из базы данных)
|
||||
$category = $product = [];
|
||||
foreach ($entries as $entry) {
|
||||
// Перебор вхождений
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
// Перебор вхождений
|
||||
|
||||
// Запись массивов категорий и товаров ($category и $product) в буфер глобальной переменной шаблонизатора
|
||||
${$entry->_type}[] = $entry;
|
||||
}
|
||||
|
||||
// Запись категорий из буфера в глобальную переменную шаблонизатора
|
||||
$this->view->categories = $category;
|
||||
|
||||
// Запись товаров из буфера в глобальную переменную шаблонизатора
|
||||
$this->view->products = $product;
|
||||
// Запись массивов категорий и товаров ($category и $product) в буфер глобальной переменной шаблонизатора
|
||||
${$entry->_type}[] = $entry;
|
||||
}
|
||||
|
||||
// Запись категорий из буфера в глобальную переменную шаблонизатора
|
||||
$this->view->categories = $category;
|
||||
|
||||
// Запись товаров из буфера в глобальную переменную шаблонизатора
|
||||
$this->view->products = $product;
|
||||
}
|
||||
} else {
|
||||
// Не переданы категории
|
||||
// Не передана категория
|
||||
|
||||
// Поиск категорий: "categories"
|
||||
// @todo сделать автоматический поиск "самой верхней" категории
|
||||
$this->view->categories = category::_read(
|
||||
filter: 'd.deleted != true && d.hidden != true',
|
||||
sort: 'd.position ASC, d.name ASC, d.created DESC',
|
||||
amount: 30,
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
// Поиск категорий: "categories" (самый верхний уровень)
|
||||
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']);
|
||||
|
||||
// Search for products
|
||||
/* $this->view->products = product::read(
|
||||
|
@ -92,6 +82,11 @@ final class catalog extends core
|
|||
); */
|
||||
}
|
||||
|
||||
// Generating filters
|
||||
$this->view->filters = [
|
||||
'brands' => product::collect('d.brand.ru', $this->errors['catalog'])
|
||||
];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// GET request
|
||||
|
||||
|
@ -114,7 +109,8 @@ final class catalog extends core
|
|||
'title' => $title ?? '',
|
||||
'html' => [
|
||||
'categories' => $this->view->render('catalog/elements/categories.html'),
|
||||
'products' => $this->view->render('catalog/elements/products/2columns.html')
|
||||
'products' => $this->view->render('catalog/elements/products/2columns.html'),
|
||||
'filters' => $this->view->render('catalog/elemments/filters.html')
|
||||
],
|
||||
'errors' => $this->errors
|
||||
]
|
||||
|
|
|
@ -12,9 +12,6 @@ use mirzaev\arming_bot\views\templater,
|
|||
mirzaev\arming_bot\models\settings,
|
||||
mirzaev\arming_bot\models\suspension;
|
||||
|
||||
// Library for ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\controller;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ use mirzaev\arming_bot\controllers\core,
|
|||
*/
|
||||
final class index extends core
|
||||
{
|
||||
|
||||
/**
|
||||
* Render the main page
|
||||
*
|
||||
|
@ -23,11 +24,8 @@ final class index extends core
|
|||
*/
|
||||
public function index(array $parameters = []): ?string
|
||||
{
|
||||
// Поиск товаров
|
||||
/* $this->view->products = product::read(); */
|
||||
|
||||
// Exit (success)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render('catalog.html');
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render('index.html');
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
|
|
|
@ -14,9 +14,6 @@ use mirzaev\arming_bot\models\core,
|
|||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Library для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Framework for Telegram
|
||||
use Zanzara\Telegram\Type\User as telegram;
|
||||
|
||||
|
@ -24,9 +21,11 @@ use Zanzara\Telegram\Type\User as telegram;
|
|||
use exception;
|
||||
|
||||
/**
|
||||
* Model of an account
|
||||
* Model of account
|
||||
*
|
||||
* @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 account extends core implements arangodb_document_interface
|
||||
|
@ -39,77 +38,90 @@ final class account extends core implements arangodb_document_interface
|
|||
final public const string COLLECTION = 'account';
|
||||
|
||||
/**
|
||||
* Инициализация
|
||||
* Initialize
|
||||
*
|
||||
* @param int $id Идентификатор Telegram
|
||||
* @param int $identifier Identifier of the account
|
||||
* @param telegram|array|null $registration Данные для регистрация, если аккаунт не найден
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return static|null Объект аккаунта, если найден
|
||||
*/
|
||||
public static function initialization(int $id, telegram|array|null $registration = null, array &$errors = []): static|null
|
||||
public static function initialize(int $identifier, telegram|array|null $registration = null, array &$errors = []): static|null
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, self::COLLECTION)) {
|
||||
if ($document = collection::search(core::$arangodb->session, sprintf("FOR d IN %s FILTER d.id == %u RETURN d", self::COLLECTION, $id))) {
|
||||
// Найден аккаунт
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Инициализация объекта аккаунта
|
||||
$account = new static;
|
||||
|
||||
// Запись инстанции документа в объект
|
||||
$account->document = $document;
|
||||
|
||||
// Возврат (успех)
|
||||
return $account;
|
||||
} else if ($registration) {
|
||||
// Не найден аккаунт и запрошена его регистрация
|
||||
|
||||
// Создание аккаунта
|
||||
document::write(
|
||||
core::$arangodb->session,
|
||||
self::COLLECTION,
|
||||
(is_array($registration)
|
||||
? $registration :
|
||||
try {
|
||||
// Initializing the account and exit (success)
|
||||
return new static(
|
||||
document: collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d.identifier == @identifier
|
||||
RETURN d
|
||||
AQL,
|
||||
[
|
||||
'id' => $registration->getId(),
|
||||
'name' => [
|
||||
'first' => $registration->getFirstName(),
|
||||
'last' => $registration->getLastName()
|
||||
],
|
||||
'domain' => $registration->getUsername(),
|
||||
'robot' => $registration->isBot(),
|
||||
'banned' => false,
|
||||
'tester' => false,
|
||||
'developer' => false,
|
||||
'access' => [
|
||||
'settings' => false
|
||||
],
|
||||
'menus' => [
|
||||
'attachments' => $registration->getAddedToAttachmentMenu()
|
||||
],
|
||||
'messages' => true,
|
||||
'groups' => [
|
||||
'join' => $registration->getCanJoinGroups(),
|
||||
'messages' => $registration->getCanReadAllGroupMessages()
|
||||
],
|
||||
'premium' => $registration->isPremium(),
|
||||
'language' => $registration->getLanguageCode(),
|
||||
'queries' => [
|
||||
'inline' => $registration->getSupportsInlineQueries()
|
||||
]
|
||||
]) + [
|
||||
'version' => ROBOT_VERSION,
|
||||
'active' => true
|
||||
]
|
||||
'@collection' => static::COLLECTION,
|
||||
'identifier' => $identifier
|
||||
],
|
||||
errors: $errors
|
||||
)
|
||||
);
|
||||
} catch (exception $e) {
|
||||
if ($registration) {
|
||||
// Not found the account and registration is requested
|
||||
|
||||
// Инициализация (без регистрации)
|
||||
return static::initialization($id, errors: $errors);
|
||||
} else throw new exception('Account not found');
|
||||
} else throw new exception('Failed to initialize document collection: ' . self::COLLECTION);
|
||||
// Creating account
|
||||
$account = document::write(
|
||||
static::COLLECTION,
|
||||
(is_array($registration)
|
||||
? $registration :
|
||||
[
|
||||
'identifier' => $registration->getId(),
|
||||
'name' => [
|
||||
'first' => $registration->getFirstName(),
|
||||
'last' => $registration->getLastName()
|
||||
],
|
||||
'domain' => $registration->getUsername(),
|
||||
'robot' => $registration->isBot(),
|
||||
'banned' => false,
|
||||
'tester' => false,
|
||||
'developer' => false,
|
||||
'access' => [
|
||||
'settings' => false
|
||||
],
|
||||
'menus' => [
|
||||
'attachments' => $registration->getAddedToAttachmentMenu()
|
||||
],
|
||||
'messages' => true,
|
||||
'groups' => [
|
||||
'join' => $registration->getCanJoinGroups(),
|
||||
'messages' => $registration->getCanReadAllGroupMessages()
|
||||
],
|
||||
'premium' => $registration->isPremium(),
|
||||
'language' => $registration->getLanguageCode(),
|
||||
'queries' => [
|
||||
'inline' => $registration->getSupportsInlineQueries()
|
||||
]
|
||||
]) + [
|
||||
'version' => ROBOT_VERSION,
|
||||
'active' => true
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($account) {
|
||||
// Created account
|
||||
|
||||
// Initializing of the account (without registration request)
|
||||
return static::initialize($identifier, errors: $errors);
|
||||
} else throw new exception('Failed to register account');
|
||||
} else throw new exception('Failed to find account');
|
||||
}
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -118,7 +130,7 @@ final class account extends core implements arangodb_document_interface
|
|||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,487 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\product,
|
||||
mirzaev\arming_bot\models\category,
|
||||
mirzaev\arming_bot\models\entry,
|
||||
mirzaev\arming_bot\models\traits\files,
|
||||
mirzaev\arming_bot\models\traits\yandex\disk as yandex;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Framework for Excel
|
||||
use avadim\FastExcelReader\Excel as excel;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Model of the catalog
|
||||
*
|
||||
* @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 catalog extends core
|
||||
{
|
||||
use yandex, files {
|
||||
yandex::download as yandex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect parameter from all products
|
||||
*
|
||||
* @param string $documment Path to the EXCEL-document
|
||||
* @param int &$categories_loaded Counter of loaded categories
|
||||
* @param int &$categories_created Counter of created categories
|
||||
* @param int &$categories_updated Counter of updated categories
|
||||
* @param int &$categories_deleted Counter of deleted categories
|
||||
* @param int &$categories_new Counter of new categories
|
||||
* @param int &$categories_old Counter of old categories
|
||||
* @param int &$products_loaded Counter of loaded products
|
||||
* @param int &$products_created Counter of created products
|
||||
* @param int &$products_updated Counter of updated products
|
||||
* @param int &$products_deleted Counter of deleted products
|
||||
* @param int &$products_new Counter of new products
|
||||
* @param int &$products_old Counter of old products
|
||||
* @param string &$language Language (en, ru...)
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @todo
|
||||
* 1. Сначала создать все категории и затем снова по циклу пройтись уже создавать entry между ними
|
||||
* 2. Сжимать изображения
|
||||
*/
|
||||
public static function import(
|
||||
string $document,
|
||||
int &$categories_loaded = 0,
|
||||
int &$categories_created = 0,
|
||||
int &$categories_updated = 0,
|
||||
int &$categories_deleted = 0,
|
||||
int &$categories_old = 0,
|
||||
int &$categories_new = 0,
|
||||
int &$products_loaded = 0,
|
||||
int &$products_created = 0,
|
||||
int &$products_updated = 0,
|
||||
int &$products_deleted = 0,
|
||||
int &$products_old = 0,
|
||||
int &$products_new = 0,
|
||||
string $language = 'en',
|
||||
array &$errors = []
|
||||
): void {
|
||||
try {
|
||||
// Initializing the spreadsheet
|
||||
$spreadsheet = excel::open($document);
|
||||
|
||||
// Inititalizing worksheets
|
||||
$categories = $spreadsheet->getSheet('Категории');
|
||||
$products = $spreadsheet->getSheet('Товары');
|
||||
|
||||
// Counting old documents
|
||||
$categories_old = collection::count(category::COLLECTION, errors: $errors);
|
||||
$products_old = collection::count(product::COLLECTION, errors: $errors);
|
||||
|
||||
// Initializing the buffer of handler categories and products
|
||||
$handled = [
|
||||
'categories' => [],
|
||||
'products' => []
|
||||
];
|
||||
|
||||
foreach (
|
||||
$categories->nextRow(
|
||||
[
|
||||
'A' => 'identifier',
|
||||
'B' => 'name',
|
||||
'C' => 'category',
|
||||
'D' => 'images',
|
||||
'E' => 'position'
|
||||
],
|
||||
excel::KEYS_FIRST_ROW
|
||||
) as $number => $row
|
||||
) {
|
||||
// Iterate over categories
|
||||
|
||||
try {
|
||||
if (!empty($row['identifier']) && !empty($row['name'])) {
|
||||
// All required cells are filled in
|
||||
|
||||
// Incrementing the counter of loaded categories
|
||||
++$categories_loaded;
|
||||
|
||||
// Declaring the variable with the status that a new category has been created
|
||||
$created = false;
|
||||
|
||||
// Declaring the variable with the category
|
||||
$category = null;
|
||||
|
||||
try {
|
||||
// Initializing the category
|
||||
$category = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors)[0] ?? null);
|
||||
|
||||
// Initializing name of the category
|
||||
$category->name[$language] === $row['name'] || $category->name = [[$language => $row['name']]] + $category->name ?? [];
|
||||
|
||||
// Initializing position of the category
|
||||
$category->position === $row['position'] || $category->position = $row['position'];
|
||||
} catch (exception $e) {
|
||||
// Not found the category
|
||||
|
||||
// Creating the category
|
||||
$_id = category::write((int) $row['identifier'], [$language => $row['name']], $row['position'] ?? null, $errors);
|
||||
|
||||
// Initializing the category
|
||||
$category = new category(document: $created = category::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors)[0] ?? null);
|
||||
|
||||
// Incrementing the counter of created categories
|
||||
if ($created) ++$categories_created;
|
||||
};
|
||||
|
||||
if ($category instanceof category) {
|
||||
// Found the category
|
||||
|
||||
if (!empty($row['category'])) {
|
||||
// Received the ascendant category
|
||||
|
||||
// Initializing the ascendant category
|
||||
$ascendant = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['category']], errors: $errors)[0] ?? null);
|
||||
|
||||
if ($ascendant instanceof category) {
|
||||
// Found the ascendant category
|
||||
|
||||
// Deleting entries of the category in ArangoDB
|
||||
entry::banish($category, $errors);
|
||||
|
||||
// Writing the category as an entry to the ascendant category in ArangoDB
|
||||
entry::write($category, $ascendant, $errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($row['images'])) {
|
||||
// Received images
|
||||
|
||||
// Initializing new images of the category
|
||||
$images = explode(' ', trim($row['images']));
|
||||
|
||||
// Reinitialize images? (true, if no images found or their amount does not match)
|
||||
$reinitialize = !$category->images || count($category->images) !== count($images);
|
||||
|
||||
// Checking the identity of existing images with new images (if reinitialization of images has not yet been requested)
|
||||
if (!$reinitialize) foreach ($category->images as $key => $image) if ($reinitialize = $image['source'] !== $images[$key]) break;
|
||||
|
||||
if ($reinitialize) {
|
||||
// Requested reinitialization of images
|
||||
|
||||
// Initializing the buffer of images
|
||||
$buffer = [];
|
||||
|
||||
foreach ($images as $index => $image) {
|
||||
// Iterating over new images
|
||||
|
||||
// Skipping empty URI`s
|
||||
if (empty($image = trim($image))) continue;
|
||||
|
||||
// Initializing path to directory of the image in storage
|
||||
$directory = DIRECTORY_SEPARATOR . 'categories' . DIRECTORY_SEPARATOR . $row['identifier'];
|
||||
|
||||
// Initializing URL of the image in storage
|
||||
$url = STORAGE . $directory;
|
||||
|
||||
// Initializing URN of the image in storage
|
||||
$urn = $index . '.jpg';
|
||||
|
||||
// Initializing URI of the image in storage
|
||||
$uri = $url . DIRECTORY_SEPARATOR . $urn;
|
||||
|
||||
// Initializing the directory in storage
|
||||
if (!file_exists($url)) mkdir($url, 0775, true);
|
||||
|
||||
if (static::yandex($image, $uri, errors: $errors)) {
|
||||
// The image is downloaded
|
||||
|
||||
// Writing the image to the buffer if images
|
||||
$buffer[] = [
|
||||
'source' => $image,
|
||||
'storage' => $directory . DIRECTORY_SEPARATOR . $urn
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Initializing images of the category
|
||||
$category->images = $buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// Writing in ArangoDB
|
||||
$updated = document::update($category->__document(), errors: $errors);
|
||||
|
||||
// Incrementing the counter of updated categories
|
||||
if ($updated && !$created) ++$categories_updated;
|
||||
} else throw new exception("Failed to initialize category: {$row['name']} ($number)");
|
||||
}
|
||||
|
||||
// Writing to the registry of handled categories and products
|
||||
$handled['categories'][] = $row['identifier'];
|
||||
} catch (exception $e) {
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach (
|
||||
$products->nextRow(
|
||||
[
|
||||
'A' => 'identifier',
|
||||
'B' => 'name',
|
||||
'C' => 'category',
|
||||
'D' => 'description',
|
||||
'E' => 'cost',
|
||||
'F' => 'weight',
|
||||
'G' => 'x',
|
||||
'H' => 'y',
|
||||
'I' => 'z',
|
||||
'J' => 'brand',
|
||||
'K' => 'compatibility',
|
||||
'L' => 'images',
|
||||
'M' => 'position'
|
||||
],
|
||||
excel::KEYS_FIRST_ROW
|
||||
) as $number => $row
|
||||
) {
|
||||
// Iterate over products
|
||||
|
||||
try {
|
||||
if (!empty($row['identifier']) && !empty($row['name'])) {
|
||||
// All required cells are filled in
|
||||
|
||||
// Incrementing the counter of loaded products
|
||||
++$products_loaded;
|
||||
|
||||
// Declaring the variable with the status that a new product has been created
|
||||
$created = false;
|
||||
|
||||
// Declaring the variable with the product
|
||||
$product = null;
|
||||
|
||||
try {
|
||||
// Initializing the product
|
||||
$product = new product(document: product::_read('d.identifier == %u', parameters: ['identifier' => (int) $row['identifier']], errors: $errors)[0] ?? null);
|
||||
|
||||
// Initializing name of the category
|
||||
$product->name[$language] === $row['name'] || $product->name = [[$language => $row['name']]] + $product->name ?? [];
|
||||
|
||||
// Initializing position of the product
|
||||
$product->position === $row['position'] || $product->position = $row['position'];
|
||||
} catch (exception $e) {
|
||||
// Not found the product
|
||||
|
||||
// Creating the product
|
||||
$_id = product::write(
|
||||
(int) $row['identifier'],
|
||||
[$language => $row['name']],
|
||||
[$language => $row['description']],
|
||||
(float) $row['cost'],
|
||||
(float) $row['weight'],
|
||||
['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
|
||||
$row['position'] ?? null,
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
// Initializing the product
|
||||
$product = new product(document: $created = product::_read(sprintf('d._id == "%s"', $_id), errors: $errors)[0] ?? null);
|
||||
|
||||
// Incrementing the counter of created products
|
||||
if ($created) ++$products_created;
|
||||
}
|
||||
|
||||
if ($product instanceof product) {
|
||||
// Found the product
|
||||
|
||||
if (!empty($row['category'])) {
|
||||
// Received the category
|
||||
|
||||
// Initializing the category
|
||||
$category = new category(document: category::_read(sprintf('d.identifier == %u', (int) $row['category']), errors: $errors)[0] ?? null);
|
||||
|
||||
if ($category instanceof category) {
|
||||
// Found the ascendant category
|
||||
|
||||
// Deleting entries of the product in ArangoDB
|
||||
entry::banish($product, $errors);
|
||||
|
||||
// Writing the product as an entry to the ascendant category in ArangoDB
|
||||
entry::write($product, $category, $errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($row['images'])) {
|
||||
// Received images
|
||||
|
||||
// Initializing new images of the category
|
||||
$images = explode(' ', trim($row['images']));
|
||||
|
||||
// Reinitialize images? (true, if no images found or their amount does not match)
|
||||
$reinitialize = !$product->images || count($product->images) !== count($images);
|
||||
|
||||
// Checking the identity of existing images with new images (if reinitialization of images has not yet been requested)
|
||||
if (!$reinitialize) foreach ($product->images as $key => $image) if ($reinitialize = $image['source'] !== $images[$key]) break;
|
||||
|
||||
if ($reinitialize) {
|
||||
// Requested reinitialization of images
|
||||
|
||||
// Initializing the buffer of images
|
||||
$buffer = [];
|
||||
|
||||
foreach ($images as $index => $image) {
|
||||
// Iterating over new images
|
||||
|
||||
// Skipping empty URI`s
|
||||
if (empty($image = trim($image))) continue;
|
||||
|
||||
// Initializing path to directory of the image in storage
|
||||
$directory = DIRECTORY_SEPARATOR . 'products' . DIRECTORY_SEPARATOR . $row['identifier'];
|
||||
|
||||
// Initializing URL of the image in storage
|
||||
$url = STORAGE . $directory;
|
||||
|
||||
// Initializing URN of the image in storage
|
||||
$urn = $index . '.jpg';
|
||||
|
||||
// Initializing URI of the image in storage
|
||||
$uri = $url . DIRECTORY_SEPARATOR . $urn;
|
||||
|
||||
// Initializing the directory in storage
|
||||
if (!file_exists($url)) mkdir($url, 0775, true);
|
||||
|
||||
if (static::yandex($image, $uri, errors: $errors)) {
|
||||
// The image is downloaded
|
||||
|
||||
// Writing the image to the buffer if images
|
||||
$buffer[] = [
|
||||
'source' => $image,
|
||||
'storage' => $directory . DIRECTORY_SEPARATOR . $urn
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Initializing images of the category
|
||||
$product->images = $buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// Writing in ArangoDB
|
||||
$updated = document::update($product->__document(), errors: $errors);
|
||||
|
||||
// Incrementing the counter of updated categories
|
||||
if ($updated && !$created) ++$products_updated;
|
||||
} else throw new exception("Failed to initialize product: {$row['name']} ($number)");
|
||||
}
|
||||
|
||||
// Writing to the registry of handled categories and products
|
||||
$handled['products'][] = $row['identifier'];
|
||||
} catch (exception $e) {
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting old categories
|
||||
foreach (
|
||||
category::_read(
|
||||
/* filter: sprintf('%s - d.updated > 3600', time()), */
|
||||
sort: 'd.updated DESC',
|
||||
amount: 100000,
|
||||
errors: $errors
|
||||
) as $document
|
||||
) {
|
||||
// Iterating over categories
|
||||
|
||||
// Initializing the category
|
||||
$category = new category(document: $document);
|
||||
|
||||
if (
|
||||
$category instanceof category
|
||||
&& array_search($category->identifier, $handled['categories']) === false
|
||||
) {
|
||||
// Not found identifier of the product in the buffer of handled categories and products
|
||||
|
||||
// Deleting images of the category from storage
|
||||
static::delete(STORAGE . DIRECTORY_SEPARATOR . 'categories' . DIRECTORY_SEPARATOR . $category->identifier, errors: $errors);
|
||||
|
||||
// Deleting entries of the category in ArangoDB
|
||||
entry::banish($category, errors: $errors);
|
||||
|
||||
// Deleting the category in ArangoDB
|
||||
document::delete($category->__document(), errors: $errors);
|
||||
|
||||
// Incrementing the counter of deleted categories
|
||||
++$categories_deleted;
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting old products
|
||||
foreach (
|
||||
product::_read(
|
||||
/* filter: sprintf('%s - d.updated > 3600', time()), */
|
||||
sort: 'd.updated DESC',
|
||||
amount: 100000,
|
||||
errors: $errors
|
||||
) as $document
|
||||
) {
|
||||
// Iterating over products
|
||||
|
||||
// Initializing the category
|
||||
$product = new product(document: $document);
|
||||
|
||||
if (
|
||||
$product instanceof product
|
||||
&& array_search($product->identifier, $handled['products']) === false
|
||||
) {
|
||||
// Not found identifier of the product in the buffer of handled categories and products
|
||||
|
||||
// Deleting images of the product from storage
|
||||
static::delete(STORAGE . DIRECTORY_SEPARATOR . 'products' . DIRECTORY_SEPARATOR . $product->identifier, errors: $errors);
|
||||
|
||||
// Deleting entries of the product in ArangoDB
|
||||
entry::banish($product, errors: $errors);
|
||||
|
||||
// Deleting the product in ArangoDB
|
||||
document::delete($product->__document(), errors: $errors);
|
||||
|
||||
// Incrementing the counter of deleted products
|
||||
++$products_deleted;
|
||||
}
|
||||
}
|
||||
|
||||
// Counting new documents
|
||||
$categories_new = collection::count(category::COLLECTION, errors: $errors);
|
||||
$products_new = collection::count(product::COLLECTION, errors: $errors);
|
||||
} catch (exception $e) {
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,294 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Library для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Model of a category
|
||||
*
|
||||
* @package mirzaev\arming_bot\models
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
class categories extends core implements arangodb_document_interface
|
||||
{
|
||||
use arangodb_document_trait;
|
||||
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
public const string COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST';
|
||||
|
||||
/**
|
||||
* Запись категории
|
||||
*
|
||||
* @param string $name Название
|
||||
* @param arrau $labels Ярлыки для отображения в интерфейсе ['EN' => "Label"]
|
||||
* @param array $hierarchy Иерархия вложенности (от родителей к потомкам: [ model, model ... ])
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null Идентификатор (_id) документа, если создан
|
||||
*/
|
||||
public static function write(
|
||||
string $name,
|
||||
array $labels = ['RU' => 'Без названия'],
|
||||
array $hierarchy = [],
|
||||
array &$errors = []
|
||||
): string|null {
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
// Создание категории
|
||||
$category = document::write(
|
||||
core::$arangodb->session,
|
||||
static::COLLECTION,
|
||||
[
|
||||
'name' => $name,
|
||||
'labels' => $labels,
|
||||
'version' => ROBOT_VERSION
|
||||
]
|
||||
);
|
||||
|
||||
if ($category) {
|
||||
// Создана категория
|
||||
|
||||
/* if (collection::init(core::$arangodb->session, 'entry', true)) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
foreach ($hierarchy as $model) {
|
||||
// Перебор иерархической структуры категорий
|
||||
|
||||
// Инициализация вложенной категории (следующей в массиве)
|
||||
$next = current($hierarchy);
|
||||
|
||||
// Поиск ребра описывающего иерархическую связь
|
||||
|
||||
document::write(core::$arangodb->session, 'entry');
|
||||
}
|
||||
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION); */
|
||||
}
|
||||
} else throw new exception('Failed to initialize edge collection: entry');
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск вхождений (подкатегории или товары)
|
||||
*
|
||||
* Находит вхождения через ребро entry
|
||||
* Генерирует _type со значениями "category" и "product"
|
||||
* относительно того есть ли у документа ещё вложения (у product вложений быть не может)
|
||||
* Объединяет возвращаемые объекты документа с переменной _type
|
||||
*
|
||||
* @param string|null $category Category identifier (_id)
|
||||
* @param string|null $filter Expression for filtering (AQL)
|
||||
* @param string|null $sort Expression for sorting (AQL)
|
||||
* @param int $page Страница
|
||||
* @param int $amount Количество товаров на странице
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Массив с найденными вхождениями (может быть пустым)
|
||||
*/
|
||||
public static function entries(
|
||||
?string $category = null,
|
||||
?string $filter = null,
|
||||
?string $sort = 'v.promotion DESC, v.position ASC, v.created DESC',
|
||||
int $page = 1,
|
||||
int $amount = 100,
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
if ($documents = collection::search(
|
||||
core::$arangodb->session,
|
||||
sprintf(
|
||||
<<<AQL
|
||||
FOR v IN 1..1 INBOUND %s GRAPH hierarchy
|
||||
%s
|
||||
%s
|
||||
LIMIT %u, %u
|
||||
LET _type = (FOR v2 IN INBOUND v._id GRAPH hierarchy RETURN v2)[0] ? "category" : "product"
|
||||
RETURN MERGE(v, {_type})
|
||||
AQL,
|
||||
empty($category) ? '(FOR d IN ' . ${static::COLLECTION} . 'LIMIT 1 RETURN d._id)[0]' : "\"$category\"",
|
||||
empty($filter) ? '' : "FILTER $filter",
|
||||
empty($sort) ? '' : "SORT $sort",
|
||||
--$page <= 0 ? $page = 0 : $page * $amount,
|
||||
$amount,
|
||||
)
|
||||
)) {
|
||||
// Найдены вхождения
|
||||
|
||||
// Возврат (успех)
|
||||
return is_array($documents) ? $documents : [$documents];
|
||||
} else return [];
|
||||
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск категорий и товаров
|
||||
*
|
||||
* Ищет категории и товары по коллекции рёбер entry из _from и _to
|
||||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Массив с уникализированными найденными коллекциями (может быть пустым)
|
||||
*/
|
||||
protected static function collections(
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, $collection = 'entry')) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
if ($names = collection::search(
|
||||
core::$arangodb->session,
|
||||
sprintf(
|
||||
<<<AQL
|
||||
FOR e IN %s
|
||||
COLLECT AGGREGATE
|
||||
from_category = UNIQUE(PARSE_IDENTIFIER(e._from).collection),
|
||||
to_category = UNIQUE(PARSE_IDENTIFIER(e._to).collection)
|
||||
RETURN UNIQUE(UNION(from_category, to_category))
|
||||
AQL,
|
||||
$collection
|
||||
)
|
||||
)) {
|
||||
// Найдены коллекции
|
||||
|
||||
// Возврат (успех)
|
||||
return $names->getAll();
|
||||
} else return [];
|
||||
} else throw new exception('Failed to initialize edge collection: entry');
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск категории по названияю
|
||||
*
|
||||
* Перебирает все коллекции из self::collections() и ищет в них соответствие с параметром name
|
||||
* ВНИМАНИЕ: НЕЛЬЗЯ ДОПУСКАТЬ ОДИНАКОВЫЕ НАЗВАНИЯ СРЕДИ РАЗНЫХ КАТЕГОРИЙ
|
||||
*
|
||||
* @param string $name Name of a collection
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return categories|null Модель имплементирующая документ с категорией, если была найдена
|
||||
*/
|
||||
public static function category(
|
||||
string $name,
|
||||
array &$errors = []
|
||||
): ?categories {
|
||||
try {
|
||||
// Инициалиация списка коллекций
|
||||
$collections = self::collections($errors);
|
||||
|
||||
if (count($collections) > 0) {
|
||||
// Найдены коллекции
|
||||
|
||||
// Инициализация буфера части запроса со списком коллекций для аргументов UNION()
|
||||
$union = [];
|
||||
foreach ($collections as $collection) $union[] = "FOR d IN $collection RETURN d";
|
||||
unset($collection);
|
||||
|
||||
if ($document = collection::search(
|
||||
core::$arangodb->session,
|
||||
sprintf(
|
||||
<<<AQL
|
||||
FOR u IN UNION(%s)
|
||||
FILTER u.name == "%s"
|
||||
SORT u.created ASC
|
||||
LIMIT 1
|
||||
RETURN u
|
||||
AQL,
|
||||
implode(', ', $union),
|
||||
$name
|
||||
)
|
||||
)) {
|
||||
// Найдена категория
|
||||
|
||||
// Инициализация названия коллекции
|
||||
$collection = explode('/', $document->getId())[0];
|
||||
|
||||
if (class_exists($model = "mirzaev\\arming_bot\\models\\$collection")) {
|
||||
// Найдена модель имплементирующая документы из этой коллекции
|
||||
|
||||
// Инициализация объекта модели
|
||||
$object = new $model;
|
||||
|
||||
if ($object instanceof categories) {
|
||||
// Объект является инстанцией категории (то есть не товара)
|
||||
|
||||
// Запись инстанции документа в объект модели
|
||||
$object->document = $document;
|
||||
|
||||
// Возврат (успех)
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
} else return null;
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -5,18 +5,81 @@ declare(strict_types=1);
|
|||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\categories;
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
|
||||
/**
|
||||
* Model of a category
|
||||
* Model of category
|
||||
*
|
||||
* @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 category extends categories
|
||||
final class category extends core implements arangodb_document_interface
|
||||
{
|
||||
use arangodb_document_trait;
|
||||
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'category';
|
||||
|
||||
/**
|
||||
* Write the category
|
||||
*
|
||||
* @param int $identifier Identifier (unique)
|
||||
* @param array $name Name [['en' => value], ['ru' => значение]]
|
||||
* @param int|null $position Position for soring in the catalog (ASC)
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null Identifier (_id) of the document in ArangoDB, if created
|
||||
*
|
||||
* @todo
|
||||
* 1. Bind parameters
|
||||
*/
|
||||
public static function write(
|
||||
int $identifier,
|
||||
array $name = [['en' => 'ERROR']],
|
||||
?int $position = null,
|
||||
array &$errors = []
|
||||
): string|null {
|
||||
try {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Writing to ArangoDB and exit (success)
|
||||
return document::write(
|
||||
static::COLLECTION,
|
||||
[
|
||||
'identifier' => $identifier,
|
||||
'name' => $name,
|
||||
'position' => $position,
|
||||
'version' => ROBOT_VERSION
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\enumerations\collection\type;
|
||||
|
||||
/**
|
||||
* Model of connect
|
||||
*
|
||||
* @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 connect extends core implements arangodb_document_interface
|
||||
{
|
||||
use arangodb_document_trait;
|
||||
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'connect';
|
||||
|
||||
/**
|
||||
* Type of the collection in ArangoDB
|
||||
*/
|
||||
public const type TYPE = type::edge;
|
||||
}
|
|
@ -10,11 +10,10 @@ use mirzaev\minimal\model;
|
|||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\connection as arangodb,
|
||||
mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
mirzaev\arangodb\enumerations\collection\type;
|
||||
|
||||
// Libraries for ArangoDB
|
||||
use ArangoDBClient\Document as _document,
|
||||
ArangoDBClient\DocumentHandler as _document_handler;
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
@ -23,6 +22,8 @@ use exception;
|
|||
* Core of models
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
class core extends model
|
||||
|
@ -47,6 +48,11 @@ class core extends model
|
|||
*/
|
||||
public const string COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST';
|
||||
|
||||
/**
|
||||
* Type of the collection in ArangoDB
|
||||
*/
|
||||
public const type TYPE = type::document;
|
||||
|
||||
/**
|
||||
* Constructor of an instance
|
||||
*
|
||||
|
@ -66,7 +72,7 @@ class core extends model
|
|||
if (isset($arangodb)) {
|
||||
// Recieved an instance of a session of ArangoDB
|
||||
|
||||
// Write an instance of a session of ArangoDB to the property
|
||||
// Writing an instance of a session of ArangoDB to the property
|
||||
$this->__set('arangodb', $arangodb);
|
||||
} else {
|
||||
// Not recieved an instance of a session of ArangoDB
|
||||
|
@ -78,14 +84,15 @@ class core extends model
|
|||
}
|
||||
|
||||
/**
|
||||
* Read from ArangoDB
|
||||
* Read document from ArangoDB
|
||||
*
|
||||
* @param string $filter Expression for filtering (AQL)
|
||||
* @param string $sort Expression for sorting (AQL)
|
||||
* @param int $amount Amount of documents for collect
|
||||
* @param int $page Page
|
||||
* @param string $return Expression describing the parameters to return (AQL)
|
||||
* @param array &$errors The registry on errors
|
||||
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return mixed An array of instances of documents from ArangoDB, if they are found
|
||||
*/
|
||||
|
@ -95,37 +102,40 @@ class core extends model
|
|||
int $amount = 1,
|
||||
int $page = 1,
|
||||
string $return = 'd',
|
||||
array $parameters = [],
|
||||
array &$errors = []
|
||||
): _document|array|null {
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, static::COLLECTION)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Read from ArangoDB and exit (success)
|
||||
$result = collection::search(
|
||||
static::$arangodb->session,
|
||||
// Read from ArangoDB
|
||||
$result = collection::execute(
|
||||
sprintf(
|
||||
<<<'AQL'
|
||||
FOR d IN %s
|
||||
FOR d IN @@collection
|
||||
%s
|
||||
%s
|
||||
LIMIT %d, %d
|
||||
RETURN %s
|
||||
LIMIT @offset, @amount
|
||||
RETURN @return
|
||||
AQL,
|
||||
static::COLLECTION,
|
||||
empty($filter) ? '' : "FILTER $filter",
|
||||
empty($sort) ? '' : "SORT $sort",
|
||||
--$page <= 0 ? 0 : $page * $amount,
|
||||
$amount,
|
||||
$return
|
||||
)
|
||||
),
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'offset' => --$page <= 0 ? 0 : $page * $amount,
|
||||
'amount' => $amount,
|
||||
'return' => $return
|
||||
] + $parameters,
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
// Выход (успех)
|
||||
// Exit (success)
|
||||
return is_array($result) ? $result : [$result];
|
||||
} else throw new exception('Failed to initialize the collection');
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -138,50 +148,6 @@ class core extends model
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update in ArangoDB
|
||||
*
|
||||
* @param _document $instance Instance of the document from ArangoDB
|
||||
*
|
||||
* @return bool Writed to ArangoDB without errors?
|
||||
*/
|
||||
public static function _update(_document $instance): bool
|
||||
{
|
||||
// Update in ArangoDB and exit (success)
|
||||
return document::update(static::$arangodb->session, $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete from ArangoDB
|
||||
*
|
||||
* @param _document $instance Instance of the document from ArangoDB
|
||||
* @param array &$errors The registry on errors
|
||||
*
|
||||
* @return bool Deleted from ArangoDB without errors?
|
||||
*/
|
||||
public static function _delete(_document $instance, array &$errors = []): bool
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, static::COLLECTION)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Delete from ArangoDB and exit (success)
|
||||
return (new _document_handler(static::$arangodb->session))->remove($instance);
|
||||
} else throw new exception('Failed to initialize the collection');
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write
|
||||
*
|
||||
|
@ -205,7 +171,7 @@ class core extends model
|
|||
if ($value instanceof arangodb) {
|
||||
// Recieved an appropriate value
|
||||
|
||||
// Write the property and exit (success)
|
||||
// Writing the property and exit (success)
|
||||
self::$arangodb = $value;
|
||||
} else {
|
||||
// Recieved an inappropriate value
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
||||
|
||||
// Library for ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document,
|
||||
mirzaev\arangodb\enumerations\collection\type;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Model of entry
|
||||
*
|
||||
* @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 entry extends core implements arangodb_document_interface
|
||||
{
|
||||
use arangodb_document_trait;
|
||||
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'entry';
|
||||
|
||||
/**
|
||||
* Type of the collection in ArangoDB
|
||||
*/
|
||||
public const type TYPE = type::edge;
|
||||
|
||||
/**
|
||||
* Write an entry
|
||||
*
|
||||
* @param category|product $from Descendant document
|
||||
* @param category $to Ascendant document
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null Identifier (_id) of instance of the entry document in ArangoDB, if created
|
||||
*/
|
||||
public static function write(
|
||||
category|product $from,
|
||||
category $to,
|
||||
array &$errors = []
|
||||
): string|null {
|
||||
try {
|
||||
if (collection::initialize($from::COLLECTION, $from::TYPE, errors: $errors)) {
|
||||
if (collection::initialize($to::COLLECTION, $to::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized collections
|
||||
|
||||
// Creating the entry and exit (success)
|
||||
return document::write(
|
||||
static::COLLECTION,
|
||||
[
|
||||
'_from' => $from->getId(),
|
||||
'_to' => $to->getId(),
|
||||
'version' => ROBOT_VERSION ?? '0.0.0'
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . $to::TYPE . ' collection: ' . $to::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . $from::TYPE . ' collection: ' . $from::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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find ascendants
|
||||
*
|
||||
* Find ascendants that are not descendants for anyone
|
||||
*
|
||||
* @param category|product $descendant Descendant document
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array|null Ascendants that are not descendants for anyone, if found
|
||||
*/
|
||||
public static function ascendants(
|
||||
category|product $descendant,
|
||||
array &$errors = []
|
||||
): ?array {
|
||||
try {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
if ($ascendants = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FOR ascendant IN OUTBOUND d @@edge
|
||||
RETURN DISTINCT ascendant
|
||||
AQL,
|
||||
[
|
||||
'@collection' => $descendant::COLLECTION,
|
||||
'@edge' => static::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found ascendants
|
||||
|
||||
// Exit (success)
|
||||
return is_array($ascendants) ? $ascendants : [$ascendants];
|
||||
} else return [];
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check existence of entry between documents
|
||||
*
|
||||
* @param category|product $from Descendant document
|
||||
* @param category $to Ascendant document
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return ?_document The entry edge, if found
|
||||
*/
|
||||
public static function check(
|
||||
category|product $from,
|
||||
category $to,
|
||||
array &$errors = []
|
||||
): ?_document {
|
||||
try {
|
||||
if (collection::initialize($from::COLLECTION, $from::TYPE, errors: $errors)) {
|
||||
if (collection::initialize($to::COLLECTION, $to::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized collections
|
||||
|
||||
if ($entry = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d._from == @from && d._to == @to
|
||||
SORT d.updated DESC, d.created DESC
|
||||
LIMIT 1
|
||||
RETURN d
|
||||
AQL,
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'from' => $from->getId(),
|
||||
'to' => $to->getId()
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the entry between $from and $to
|
||||
|
||||
// Exit (success)
|
||||
return is_array($entry) ? $entry[0] : $entry;
|
||||
} else return null;
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . $to::TYPE . ' collection: ' . $to::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . $from::TYPE . ' collection: ' . $from::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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск вхождений (подкатегории или товары)
|
||||
*
|
||||
* Находит вхождения через ребро entry
|
||||
* Генерирует _type со значениями "category" и "product"
|
||||
* относительно того есть ли у документа ещё вложения
|
||||
* (подразумевается, что у product вложений быть не может)
|
||||
* Объединяет возвращаемые объекты документа с переменной _type
|
||||
*
|
||||
* @param category|product $document Ascendant document
|
||||
* @param string|null $filter Expression for filtering (AQL)
|
||||
* @param string|null $sort Expression for sorting (AQL)
|
||||
* @param int $page Страница
|
||||
* @param int $amount Количество товаров на странице
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Массив с найденными вхождениями (может быть пустым)
|
||||
*/
|
||||
public static function search(
|
||||
category|product $document,
|
||||
?string $filter = 'v.deleted != true && v.hidden != true',
|
||||
?string $sort = 'v.position ASC, v.created DESC',
|
||||
int $page = 1,
|
||||
int $amount = 100,
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
if (collection::initialize($document::COLLECTION, $document::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized collections
|
||||
|
||||
if ($documents = collection::execute(
|
||||
sprintf(
|
||||
<<<'AQL'
|
||||
FOR v IN 1..1 INBOUND @document GRAPH @graph
|
||||
%s
|
||||
%s
|
||||
LIMIT @offset, @amount
|
||||
LET _type = (FOR v2 IN INBOUND v._id GRAPH @graph RETURN v2)[0] ? "category" : "product"
|
||||
RETURN MERGE(v, {_type})
|
||||
AQL,
|
||||
empty($filter) ? '' : "FILTER $filter",
|
||||
empty($sort) ? '' : "SORT $sort",
|
||||
),
|
||||
[
|
||||
'grapth' => 'catalog',
|
||||
'document' => $document->getId(),
|
||||
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
||||
'amount' => $amount
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Fount entries
|
||||
|
||||
// Возврат (успех)
|
||||
return is_array($documents) ? $documents : [$documents];
|
||||
} else return [];
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::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 [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Banish the document from the catalog
|
||||
*
|
||||
* Removes all entry edges associated with the document
|
||||
*
|
||||
* @param categoru|product $document Document for banishing
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function banish(category|product $document, array &$errors = []): void
|
||||
{
|
||||
try {
|
||||
if (collection::initialize($document::COLLECTION, $document::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized collections
|
||||
|
||||
// Execute and exit (success)
|
||||
collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
// FILTER d._from == @_id || d._to == @_id
|
||||
FILTER d._from == @_id
|
||||
REMOVE d IN @@collection
|
||||
AQL,
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'_id' => $document->getId()
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::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;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\categories;
|
||||
|
||||
/**
|
||||
* Model of a part
|
||||
*
|
||||
* @package mirzaev\arming_bot\models
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class part extends categories
|
||||
{
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'part';
|
||||
}
|
|
@ -5,15 +5,13 @@ declare(strict_types=1);
|
|||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core;
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
|
||||
// Library для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
|
@ -21,155 +19,60 @@ use exception;
|
|||
* Model of a product
|
||||
*
|
||||
* @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 product extends core
|
||||
{
|
||||
use arangodb_document_trait;
|
||||
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'product';
|
||||
|
||||
/**
|
||||
* Чтение товаров
|
||||
* Write a product
|
||||
*
|
||||
* @param string|null $search Поиск
|
||||
* @param string|null $filter Фильтр
|
||||
* @param string|null $sort Сортировка
|
||||
* @param int $page Страница
|
||||
* @param int $amount Количество товаров на странице
|
||||
* @param string|null $return
|
||||
* @param int $identifier Identifier (unique)
|
||||
* @param array $name Name [['en' => value], ['ru' => значение]]
|
||||
* @param array|null $description Description [['en' => value], ['ru' => значение]]
|
||||
* @param float $cost Cost
|
||||
* @param float $weight Weight
|
||||
* @param array $dimensions Dimensions ['x' => 0.0, 'y' => 0.0, 'z' => 0.0]
|
||||
* @param array $images Images (first will be thumbnail)
|
||||
* @param int|null $position Position for sorting in the catalog (ASC)
|
||||
* @param array $data Data
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Массив с найденными товарами (может быть пустым)
|
||||
*/
|
||||
public static function read(
|
||||
?string $search = null,
|
||||
?string $filter = 'd.deleted != true && d.hidden != true',
|
||||
?string $sort = 'd.promotion DESC, d.position ASC, d.created DESC',
|
||||
int $page = 1,
|
||||
int $amount = 100,
|
||||
?string $return = 'd',
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, self::COLLECTION)) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
// Инициализация строки запроса
|
||||
$aql = sprintf(
|
||||
<<<AQL
|
||||
FOR d IN %s
|
||||
AQL,
|
||||
$search ? self::COLLECTION . 's_search' : self::COLLECTION
|
||||
);
|
||||
|
||||
if ($search) {
|
||||
// Search
|
||||
$aql .= sprintf(
|
||||
<<<AQL
|
||||
SEARCH
|
||||
LEVENSHTEIN_MATCH(
|
||||
d.title.ru,
|
||||
TOKENS("%s", "text_ru")[0],
|
||||
1,
|
||||
false
|
||||
) OR
|
||||
levenshtein_match(
|
||||
d.description.ru,
|
||||
tokens("%s", "text_ru")[0],
|
||||
1,
|
||||
false
|
||||
) OR
|
||||
levenshtein_match(
|
||||
d.compatibility.ru,
|
||||
tokens("%s", "text_ru")[0],
|
||||
1,
|
||||
false
|
||||
)
|
||||
AQL,
|
||||
$search,
|
||||
$search,
|
||||
$search
|
||||
);
|
||||
|
||||
// Adding sorting
|
||||
if ($sort) $sort = "BM25(d) DESC, $sort";
|
||||
else $sort = "BM25(d) DESC";
|
||||
}
|
||||
|
||||
if ($documents = collection::search(
|
||||
core::$arangodb->session,
|
||||
sprintf(
|
||||
$aql . <<<AQL
|
||||
%s
|
||||
%s
|
||||
LIMIT %u, %u
|
||||
RETURN %s
|
||||
AQL,
|
||||
empty($filter) ? '' : "FILTER $filter",
|
||||
empty($sort) ? '' : "SORT $sort",
|
||||
--$page <= 0 ? $page = 0 : $page * $amount,
|
||||
$amount,
|
||||
empty($return) ? 'd' : $return
|
||||
)
|
||||
)) {
|
||||
// Найдены товары
|
||||
|
||||
// Возврат (успех)
|
||||
return is_array($documents) ? $documents : [$documents];
|
||||
} else return [];
|
||||
} else throw new exception('Failed to initialize document collection: ' . self::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись товара
|
||||
* @return string|null Identifier (_id) of instance of the product document in ArangoDB, if created
|
||||
*
|
||||
* @param string $title Заголовок
|
||||
* @param string|null $description Описание
|
||||
* @param float $cost Цена
|
||||
* @param float $weight Вес
|
||||
* @param array $dimensions Габариты (float)
|
||||
* @param array $images Изображения (первое - обложка) (https, путь в storage, иначе будет поиск в storage)
|
||||
* @param array $hierarchy Иерархия вложенности (от родителей к потомкам: [ model, model ... ])
|
||||
* @param array $data Дополнительные данные
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return string|null Идентификатор (_id) документа (товара), если создан
|
||||
* @todo
|
||||
* 1. Bind parameters
|
||||
*/
|
||||
public static function write(
|
||||
string $title,
|
||||
?string $description = null,
|
||||
int $identifier,
|
||||
array $name = [['en' => 'ERROR']],
|
||||
?array $description = [['en' => 'ERROR']],
|
||||
float $cost = 0,
|
||||
float $weight = 0,
|
||||
array $dimensions = ['x' => 0, 'y' => 0, 'z' => 0],
|
||||
array $images = [],
|
||||
array $hierarchy = [],
|
||||
?int $position = null,
|
||||
array $data = [],
|
||||
array &$errors = []
|
||||
): string|null {
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, self::COLLECTION)) {
|
||||
// Инициализирована коллекция
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Создание товара
|
||||
$product = document::write(
|
||||
core::$arangodb->session,
|
||||
self::COLLECTION,
|
||||
// Writing in ArangoDB and exit (success)
|
||||
return document::write(
|
||||
static::COLLECTION,
|
||||
[
|
||||
'title' => $title,
|
||||
'identifier' => $identifier,
|
||||
'name' => $name,
|
||||
'description' => $description,
|
||||
'cost' => $cost ?? 0,
|
||||
'weight' => $weight ?? 0,
|
||||
|
@ -179,31 +82,14 @@ final class product extends core
|
|||
'z' => $dimensions['z'] ?? 0,
|
||||
],
|
||||
'images' => $images,
|
||||
'position' => $position,
|
||||
'version' => ROBOT_VERSION
|
||||
] + $data
|
||||
] + $data,
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($product) {
|
||||
// Создан товар
|
||||
|
||||
if (collection::init(core::$arangodb->session, 'entry', true)) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
foreach ($hierarchy as $model) {
|
||||
// Перебор иерархической структуры категорий
|
||||
|
||||
// Инициализация вложенной категории (следующей в массиве)
|
||||
$next = current($hierarchy);
|
||||
|
||||
// Поиск ребра описывающего иерархическую связь
|
||||
|
||||
document::write(core::$arangodb->session, 'entry');
|
||||
}
|
||||
} else throw new exception('Failed to initialize document collection: ' . self::COLLECTION);
|
||||
}
|
||||
} else throw new exception('Failed to initialize document collection: entry');
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -212,7 +98,163 @@ final class product extends core
|
|||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read products
|
||||
*
|
||||
* @param string|null $search Search (text)
|
||||
* @param string|null $filter Flter (AQL)
|
||||
* @param string|null $sort Sort (AQL)
|
||||
* @param int $page Page
|
||||
* @param int $amount Amount per page
|
||||
* @param string|null $return Return (AQL)
|
||||
* @param string $language Language code (en, ru...)
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Массив с найденными товарами (может быть пустым)
|
||||
*/
|
||||
public static function read(
|
||||
?string $search = null,
|
||||
?string $filter = 'd.deleted != true && d.hidden != true',
|
||||
?string $sort = 'd.position ASC, d.created DESC',
|
||||
int $page = 1,
|
||||
int $amount = 100,
|
||||
?string $return = 'd',
|
||||
string $language = 'en',
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Initializing the query
|
||||
$aql = <<<'AQL'
|
||||
FOR d IN @@collection
|
||||
AQL;
|
||||
|
||||
if ($search) {
|
||||
// Requested search
|
||||
|
||||
// Writing to the query
|
||||
$aql .= <<<'AQL'
|
||||
SEARCH
|
||||
LEVENSHTEIN_MATCH(
|
||||
d.name.@language,
|
||||
TOKENS(@search, @analyzer)[0],
|
||||
1,
|
||||
false
|
||||
) OR
|
||||
levenshtein_match(
|
||||
d.description.@language,
|
||||
tokens(@search, @analyzer)[0],
|
||||
1,
|
||||
false
|
||||
) OR
|
||||
levenshtein_match(
|
||||
d.compatibility.@language,
|
||||
tokens(@search, @analyzer)[0],
|
||||
1,
|
||||
false
|
||||
)
|
||||
AQL;
|
||||
|
||||
// Adding sorting
|
||||
if ($sort) $sort = "BM25(d) DESC, $sort";
|
||||
else $sort = "BM25(d) DESC";
|
||||
}
|
||||
|
||||
// Reading products
|
||||
$documents = collection::execute(
|
||||
sprintf(
|
||||
$aql . <<<'AQL'
|
||||
%s
|
||||
%s
|
||||
LIMIT @offset, @amount
|
||||
RETURN $s
|
||||
AQL,
|
||||
empty($filter) ? '' : "FILTER $filter",
|
||||
empty($sort) ? '' : "SORT $sort",
|
||||
empty($return) ? 'd' : $return
|
||||
),
|
||||
[
|
||||
'@collection' => $search ? static::COLLECTION . 's_search' : static::COLLECTION,
|
||||
'search' => $search,
|
||||
'language' => $language,
|
||||
'analyzer' => "text_$language",
|
||||
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
||||
'amount' => $amount,
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($documents) {
|
||||
// Found products
|
||||
|
||||
// Exit (success)
|
||||
return is_array($documents) ? $documents : [$documents];
|
||||
} else return [];
|
||||
} else throw new exception('Failed to initialize ' . static::COLLECTION . ' 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 [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect parameter from all products
|
||||
*
|
||||
* @param string $name Name of the parameter (AQL path)
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Array with found unique parameter values from all products (can be empty)
|
||||
*/
|
||||
public static function collect(
|
||||
string $name = 'd._key',
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
if ($values = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collecton
|
||||
RETURN DISTINCT @parameter
|
||||
AQL,
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'parameter' => $name
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found parameters
|
||||
|
||||
// Exit (success)
|
||||
return $values;
|
||||
} else return [];
|
||||
} 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 [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace mirzaev\arming_bot\models;
|
|||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\account,
|
||||
mirzaev\arming_bot\models\connect,
|
||||
mirzaev\arming_bot\models\enumerations\session as verification,
|
||||
mirzaev\arming_bot\models\traits\status,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||
|
@ -25,6 +26,8 @@ use exception;
|
|||
* Model of a session
|
||||
*
|
||||
* @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 session extends core implements arangodb_document_interface
|
||||
|
@ -37,12 +40,12 @@ final class session extends core implements arangodb_document_interface
|
|||
final public const string COLLECTION = 'session';
|
||||
|
||||
/**
|
||||
* Type of session verification(
|
||||
* Type of sessions verification
|
||||
*/
|
||||
public const verification VERIFICATION = verification::hash_else_address;
|
||||
|
||||
/**
|
||||
* Constructor of an instance
|
||||
* Constructor of instance
|
||||
*
|
||||
* Initialize of a session and write them to the $this->document property
|
||||
*
|
||||
|
@ -50,63 +53,69 @@ final class session extends core implements arangodb_document_interface
|
|||
* @param ?int $expires Date of expiring of the session (used for creating a new session)
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return static instance of the ArangoDB document of session
|
||||
* @return static
|
||||
*/
|
||||
public function __construct(?string $hash = null, ?int $expires = null, array &$errors = [])
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
if (isset($hash) && $document = $this->hash($hash, $errors)) {
|
||||
// Found an instance of the ArangoDB document of session and received a session hash
|
||||
if (isset($hash) && $document = $this->hash($hash, errors: $errors)) {
|
||||
// Found the instance of the ArangoDB document of session and received a session hash
|
||||
|
||||
// Writing document instance of the session from ArangoDB to the property of the implementing object
|
||||
$this->document = $document;
|
||||
} else if (static::VERIFICATION === verification::hash_else_address && $document = $this->address($_SERVER['REMOTE_ADDR'], $errors)) {
|
||||
// Found an instance of the ArangoDB document of session and received a session hash
|
||||
$this->__document($document);
|
||||
} else if (static::VERIFICATION === verification::hash_else_address && $document = $this->address($_SERVER['REMOTE_ADDR'], errors: $errors)) {
|
||||
// Found the instance of the ArangoDB document of session and received a session hash
|
||||
|
||||
// Writing document instance of the session from ArangoDB to the property of the implementing object
|
||||
$this->document = $document;
|
||||
$this->__document($document);
|
||||
} else {
|
||||
// Not found an instance of the ArangoDB document of session
|
||||
// Not found the instance of the ArangoDB document of session
|
||||
|
||||
// Initializing a new session and write they into ArangoDB
|
||||
$_id = document::write($this::$arangodb->session, self::COLLECTION, [
|
||||
'active' => true,
|
||||
'expires' => $expires ?? time() + 604800,
|
||||
'address' => $_SERVER['REMOTE_ADDR'],
|
||||
'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
|
||||
'referer' => $_SERVER['HTTP_REFERER'] ?? null,
|
||||
'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null
|
||||
]);
|
||||
$_id = document::write(
|
||||
static::COLLECTION,
|
||||
[
|
||||
'active' => true,
|
||||
'expires' => $expires ?? time() + 604800,
|
||||
'address' => $_SERVER['REMOTE_ADDR'],
|
||||
'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
|
||||
'referer' => $_SERVER['HTTP_REFERER'] ?? null,
|
||||
'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null
|
||||
]
|
||||
);
|
||||
|
||||
if ($session = collection::search($this::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
FOR d IN %s
|
||||
FILTER d._id == '%s' && d.expires > %d && d.active == true
|
||||
if ($session = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d._id == @_id && d.expires > @time && d.active == true
|
||||
RETURN d
|
||||
AQL,
|
||||
self::COLLECTION,
|
||||
$_id,
|
||||
time()
|
||||
))) {
|
||||
// Found an instance of just created new session
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'_id' => $_id,
|
||||
'time' => time()
|
||||
],
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found the instance of just created new session
|
||||
|
||||
// Generate a hash and write into an instance of the ArangoDB document of session property
|
||||
// Generating a hash and write into the instance of the ArangoDB document of session property
|
||||
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
|
||||
|
||||
if (document::update($this::$arangodb->session, $session)) {
|
||||
// Is writed update
|
||||
if (document::update($session, errors: $errors)) {
|
||||
// Update is writed to ArangoDB
|
||||
|
||||
// Writing document instance of the session from ArangoDB to the property of the implementing object
|
||||
$this->document = $session;
|
||||
} else throw new exception('Could not write the session data');
|
||||
} else throw new exception('Could not create or find just created session');
|
||||
// Writing instance of the session document from ArangoDB to the property of the implementing object
|
||||
$this->__document($session);
|
||||
} else throw new exception('Failed to write the session data');
|
||||
} else throw new exception('Failed to create or find just created session');
|
||||
}
|
||||
} else throw new exception('Could not initialize the collection');
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -121,44 +130,47 @@ final class session extends core implements arangodb_document_interface
|
|||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return account|null An object implementing the account instance from the database, if found
|
||||
* @return account|null An object implements the instance of the account document from ArangoDB, if found
|
||||
*/
|
||||
public function account(array &$errors = []): ?account
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
|
||||
if (collection::init(core::$arangodb->session, 'connect', true)) {
|
||||
if (collection::init(core::$arangodb->session, account::COLLECTION)) {
|
||||
// Инициализирована коллекция
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(account::COLLECTION, account::TYPE, errors: $errors)) {
|
||||
// Initialized collections
|
||||
|
||||
if ($document = collection::search(
|
||||
core::$arangodb->session,
|
||||
sprintf(
|
||||
<<<AQL
|
||||
FOR v IN INBOUND "%s" GRAPH sessions
|
||||
SORT v.created DESC
|
||||
LIMIT 1
|
||||
RETURN v
|
||||
AQL,
|
||||
$this->getId(),
|
||||
)
|
||||
)) {
|
||||
// Найден аккаунт
|
||||
// Search for connected account
|
||||
$document = collection::execute(
|
||||
<<<AQL
|
||||
FOR v IN INBOUND @session GRAPH sessions
|
||||
SORT v.created DESC
|
||||
LIMIT 1
|
||||
RETURN v
|
||||
AQL,
|
||||
[
|
||||
'session' => $this->getId()
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
// Инициализация объекта аккаунта
|
||||
if ($document instanceof _document) {
|
||||
// Found connected account
|
||||
|
||||
// Initializing the implement object of the instance of sesson document from ArangoDB
|
||||
$account = new account;
|
||||
|
||||
// Запись инстанции документа в объект
|
||||
// Writing the instance of session document from ArangoDB to the implement object
|
||||
$account->__document($document);
|
||||
|
||||
// Exit (success)
|
||||
return $account;
|
||||
} else return null;
|
||||
} else throw new exception('Failed to initialize document collection: ' . account::COLLECTION);
|
||||
} else throw new exception('Failed to initialize edge collection: connect');
|
||||
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -182,25 +194,28 @@ final class session extends core implements arangodb_document_interface
|
|||
public function connect(account $account, array &$errors = []): ?string
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
|
||||
if (collection::init(core::$arangodb->session, 'connect', true)) {
|
||||
if (collection::init(core::$arangodb->session, account::COLLECTION)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(connect::COLLECTION, connect::TYPE, errors: $errors)) {
|
||||
if (collection::initialize(account::COLLECTION, account::TYPE, errors: $errors)) {
|
||||
// Collections initialized
|
||||
|
||||
// The instance of the session document from ArangoDB is initialized?
|
||||
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized');
|
||||
|
||||
// Writing document and exit (success)
|
||||
return document::write(
|
||||
core::$arangodb->session,
|
||||
'connect',
|
||||
connect::COLLECTION,
|
||||
[
|
||||
'_from' => $account->getId(),
|
||||
'_to' => $this->document->getId()
|
||||
]
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
} else throw new exception('Failed to initialize document collection: ' . account::COLLECTION);
|
||||
} else throw new exception('Failed to initialize edge collection: connect');
|
||||
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -226,23 +241,26 @@ final class session extends core implements arangodb_document_interface
|
|||
public static function hash(string $hash, array &$errors = []): ?_document
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Collection initialized
|
||||
|
||||
// Search the session data in ArangoDB
|
||||
return collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
FOR d IN %s
|
||||
FILTER d.hash == '%s' && d.expires > %d && d.active == true
|
||||
return collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d.hash == @hash && d.expires > $time && d.active == true
|
||||
RETURN d
|
||||
AQL,
|
||||
static::COLLECTION,
|
||||
$hash,
|
||||
time()
|
||||
));
|
||||
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'hash' => $hash,
|
||||
'time' => time()
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -268,23 +286,26 @@ final class session extends core implements arangodb_document_interface
|
|||
public static function address(string $address, array &$errors = []): ?_document
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, static::COLLECTION)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Collection initialized
|
||||
|
||||
// Search the session data in ArangoDB
|
||||
return collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
FOR d IN %s
|
||||
FILTER d.address == '%s' && d.expires > %d && d.active == true
|
||||
return collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d.address == @address && d.expires > @time && d.active == true
|
||||
RETURN d
|
||||
AQL,
|
||||
static::COLLECTION,
|
||||
$address,
|
||||
time()
|
||||
));
|
||||
} else throw new exception('Failed to initialize document collection: ' . static::COLLECTION);
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'address' => $address,
|
||||
'time' => time()
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -298,33 +319,33 @@ final class session extends core implements arangodb_document_interface
|
|||
}
|
||||
|
||||
/**
|
||||
* Write to buffer of the session
|
||||
* Write to buffer of the session
|
||||
*
|
||||
* @param array $data Data for merging
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return bool Is data has written into the session buffer?
|
||||
* @return bool Is data has written into the session document from ArangoDB?
|
||||
*/
|
||||
public function write(array $data, array &$errors = []): bool
|
||||
{
|
||||
try {
|
||||
if (collection::init($this::$arangodb->session, self::COLLECTION)) {
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// An instance of the ArangoDB document of session is initialized?
|
||||
if (!isset($this->document)) throw new exception('An instance of the ArangoDB document of session is not initialized');
|
||||
// The instance of the session document from ArangoDB is initialized?
|
||||
isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized');
|
||||
|
||||
// Write data into buffwer of an instance of the ArangoDB document of session
|
||||
// Writing data into buffer of the instance of the session document from ArangoDB
|
||||
$this->document->buffer = array_replace_recursive(
|
||||
$this->document->buffer ?? [],
|
||||
[$_SERVER['INTERFACE'] => array_replace_recursive($this->document->buffer[$_SERVER['INTERFACE']] ?? [], $data)]
|
||||
);
|
||||
|
||||
// Write to ArangoDB and exit (success)
|
||||
return document::update($this::$arangodb->session, $this->document) ? true : throw new exception('Не удалось записать данные в буфер сессии');
|
||||
} else throw new exception('Could not initialize the collection');
|
||||
// Writing to ArangoDB and exit (success)
|
||||
return document::update($this->document, errors: $errors);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -333,6 +354,7 @@ final class session extends core implements arangodb_document_interface
|
|||
];
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ use exception;
|
|||
* Model of settings
|
||||
*
|
||||
* @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 settings extends core implements arangodb_document_interface
|
||||
|
@ -40,35 +42,52 @@ final class settings extends core implements arangodb_document_interface
|
|||
* @param array|null $create Данные для создания, если настройки не найдены
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return static|null Object implements an instance of settngs document from ArangoDB
|
||||
* @return static|null Object implements the instance of settngs document from ArangoDB
|
||||
*/
|
||||
public static function active(array|null $create = null, array &$errors = []): static|null
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, self::COLLECTION)) {
|
||||
if ($document = collection::search(core::$arangodb->session, sprintf("FOR d IN %s FILTER d.status == 'active' SORT d.updated DESC LIMIT 1 RETURN d", self::COLLECTION))) {
|
||||
// Найдены активные настройки
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Инициализация объекта настроек
|
||||
// Search for active settings
|
||||
$document = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d.status == 'active'
|
||||
SORT d.updated DESC
|
||||
LIMIT 1
|
||||
RETURN d
|
||||
AQL,
|
||||
[
|
||||
'@collection' => static::COLLECTION
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($document instanceof _document) {
|
||||
// Found active settings
|
||||
|
||||
// Initializing the implement object of the instance of settings document from ArangoDB
|
||||
$settings = new static;
|
||||
|
||||
// Запись инстанции документа в объект
|
||||
$settings->document = $document;
|
||||
// Writing the instance of settings document from ArangoDB to the implement object
|
||||
$settings->__document($document);
|
||||
|
||||
// Возврат (успех)
|
||||
// Exit (success)
|
||||
return $settings;
|
||||
} else if ($create) {
|
||||
// Не найдены активные настройки и запрошено создание
|
||||
// Not found active settings and requested their creating
|
||||
|
||||
// Создание настроек
|
||||
document::write(core::$arangodb->session, self::COLLECTION, ['status' => 'active'] + $create);
|
||||
// Creating a settings
|
||||
document::write(static::COLLECTION, ['status' => 'active'] + $create, errors: $errors);
|
||||
|
||||
// Инициализация (без создания)
|
||||
// Re-search (without creating) and exit (success || fail)
|
||||
return static::active(errors: $errors);
|
||||
} else throw new exception('Settings not found');
|
||||
} else throw new exception('Failed to initialize document collection: ' . self::COLLECTION);
|
||||
} else throw new exception('Active settings not found');
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -77,7 +96,7 @@ final class settings extends core implements arangodb_document_interface
|
|||
];
|
||||
}
|
||||
|
||||
// Выход (провал)
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ use exception,
|
|||
* Model of a suspension
|
||||
*
|
||||
* @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 suspension extends core implements arangodb_document_interface
|
||||
|
@ -42,27 +44,45 @@ final class suspension extends core implements arangodb_document_interface
|
|||
*
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return static|null Object implements an instance of suspension from ArangoDB
|
||||
* @return static|null Object implements the instance of suspension from ArangoDB
|
||||
*/
|
||||
public static function search(array &$errors = []): static|null
|
||||
{
|
||||
try {
|
||||
if (collection::init(core::$arangodb->session, self::COLLECTION)) {
|
||||
if ($document = collection::search(core::$arangodb->session, sprintf("FOR d IN %s FILTER d.end > %u SORT d.end DESC LIMIT 1 RETURN d", self::COLLECTION, time()))) {
|
||||
// Найдены активные настройки
|
||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||
// Initialized the collection
|
||||
|
||||
// Инициализация объекта настроек
|
||||
// Search for active suspension
|
||||
$document = collection::execute(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
FILTER d.end > @time
|
||||
SORT d.end DESC
|
||||
LIMIT 1
|
||||
RETURN d
|
||||
AQL,
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
'time' => time()
|
||||
],
|
||||
errors: $errors
|
||||
);
|
||||
|
||||
if ($document instanceof _document) {
|
||||
// Found active suspension
|
||||
|
||||
// Initializing the implement object of the instance of suspension document from ArangoDB
|
||||
$suspension = new static;
|
||||
|
||||
// Запись инстанции документа в объект
|
||||
$suspension->document = $document;
|
||||
// Writing the instance of suspension document from ArangoDB to the implement object
|
||||
$suspension->__document($document);
|
||||
|
||||
// Возврат (успех)
|
||||
// Exit (success)
|
||||
return $suspension;
|
||||
} else return null;
|
||||
} else throw new exception('Failed to initialize document collection: ' . self::COLLECTION);
|
||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
@ -151,7 +171,7 @@ final class suspension extends core implements arangodb_document_interface
|
|||
}
|
||||
);
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
|
|
@ -7,26 +7,26 @@ namespace mirzaev\arming_bot\models;
|
|||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\controllers\core as controller,
|
||||
mirzaev\arming_bot\models\catalog,
|
||||
mirzaev\arming_bot\models\suspension,
|
||||
mirzaev\arming_bot\models\account;
|
||||
|
||||
// Фреймворк Telegram
|
||||
// Framework for Telegram
|
||||
use Zanzara\Zanzara,
|
||||
Zanzara\Context,
|
||||
Zanzara\Telegram\Type\Input\InputFile,
|
||||
Zanzara\Telegram\Type\File\Document as telegram_document,
|
||||
Zanzara\Telegram\Type\File\File,
|
||||
Zanzara\Middleware\MiddlewareNode as Node;
|
||||
|
||||
// Library для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
/**
|
||||
* Model of a chat
|
||||
* Model of chat (telegram)
|
||||
*
|
||||
* @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 chat extends core
|
||||
final class telegram extends core
|
||||
{
|
||||
/**
|
||||
* Экранирование символов для Markdown
|
||||
|
@ -167,7 +167,8 @@ final class chat extends core
|
|||
],
|
||||
[
|
||||
['text' => '🪖 Сайт', 'url' => 'https://arming.ru'],
|
||||
['text' => '🛒 Wildberries', 'url' => 'https://arming.ru']
|
||||
['text' => '🛒 Wildberries', 'url' => 'https://www.wildberries.ru/seller/137386'],
|
||||
['text' => '🛒 Ozon', 'url' => 'https://www.ozon.ru/seller/arming-1086587/products/?miniapp=seller_1086587'],
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -323,7 +324,7 @@ final class chat extends core
|
|||
$ctx->sendDocument(new InputFile(CATALOG_EXAMPLE), ['disable_notification' => true]);
|
||||
|
||||
// Импорт файла
|
||||
$ctx->nextStep("import");
|
||||
$ctx->nextStep([static::class, 'import'], true);
|
||||
});
|
||||
} else {
|
||||
// Не авторизован доступ к настройкам
|
||||
|
@ -342,7 +343,7 @@ final class chat extends core
|
|||
*/
|
||||
public static function import(Context $ctx): void
|
||||
{
|
||||
if ($ctx->get('account')?->access['settings']) {
|
||||
if (($account = $ctx->get('account'))?->access['settings']) {
|
||||
// Авторизован доступ к настройкам
|
||||
|
||||
// Инициализация документа
|
||||
|
@ -352,35 +353,97 @@ final class chat extends core
|
|||
// Инициализирован документ
|
||||
|
||||
// Инициализация файла
|
||||
$ctx->getFile($document->getFileId())->then(function ($file) use ($ctx) {
|
||||
$ctx->getFile($document->getFileId())->then(function ($file) use ($ctx, $document, $account) {
|
||||
|
||||
if ($file->getFileSize() <= 50000000) {
|
||||
// Не превышает 50 мегабайт (50000000 байт) размер файла
|
||||
// Не превышает 50 мегабайт (50 000 000 байт) размер файла
|
||||
|
||||
if ($file->getFilePath()['extension'] === 'xlsx') {
|
||||
if (pathinfo(parse_url($file->getFilePath())['path'], PATHINFO_EXTENSION) === 'xlsx') {
|
||||
// Имеет расширение xlsx файл
|
||||
|
||||
// Initializing the directory in the storage
|
||||
if (!file_exists($storage = STORAGE . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . $account->getKey() . DIRECTORY_SEPARATOR . time()))
|
||||
mkdir($storage, 0775, true);
|
||||
|
||||
// Сохранение файла
|
||||
file_put_contents(STORAGE . DIRECTORY_SEPARATOR . 'import.xlsx', file_get_contents('https://api.telegram.org/file/bot' . KEY . '/' . $file->getFilePath()));
|
||||
|
||||
// Инициализация счётчика загруженных товаров
|
||||
$loaded = $created = $updated = $deleted = $old = $new = 0;
|
||||
|
||||
|
||||
file_put_contents(
|
||||
$import = $storage . DIRECTORY_SEPARATOR . 'import.xlsx',
|
||||
file_get_contents('https://api.telegram.org/file/bot' . KEY . '/' . parse_url($file->getFilePath())['path'])
|
||||
);
|
||||
|
||||
// Отправка сообщения
|
||||
$ctx->sendMessage(<<<TXT
|
||||
*Загружено для обработки:* $loaded
|
||||
$ctx->sendMessage(sprintf(
|
||||
<<<'TXT'
|
||||
🔬 *Выполняется анализ:* %s \(%s байт\)
|
||||
TXT,
|
||||
static::unmarkdown($document->getFileName()),
|
||||
static::unmarkdown((string) $file->getFileSize())
|
||||
))
|
||||
->then(function ($message) use ($ctx, $import) {
|
||||
// Инициализация счётчика загруженных товаров
|
||||
$categories_loaded
|
||||
= $products_loaded
|
||||
= $categories_created
|
||||
= $products_created
|
||||
= $categories_updated
|
||||
= $products_updated
|
||||
= $categories_deleted
|
||||
= $products_deleted
|
||||
= $categories_old
|
||||
= $products_old
|
||||
= $categories_new
|
||||
= $products_new
|
||||
= 0;
|
||||
|
||||
*Добавлено:* $created
|
||||
*Обновлено:* $updated
|
||||
*Удалено:* $deleted
|
||||
// Import
|
||||
catalog::import(
|
||||
$import,
|
||||
$categories_loaded,
|
||||
$categories_created,
|
||||
$categories_updated,
|
||||
$categories_deleted,
|
||||
$categories_old,
|
||||
$categories_new,
|
||||
$products_loaded,
|
||||
$products_created,
|
||||
$products_updated,
|
||||
$products_deleted,
|
||||
$products_old,
|
||||
$products_new,
|
||||
language: 'ru'
|
||||
);
|
||||
|
||||
*Опубликовано в магазине:* $old \-\> *$new*
|
||||
TXT)
|
||||
->then(function ($message) use ($ctx) {
|
||||
// Завершение диалога
|
||||
$ctx->endConversation();
|
||||
// Отправка сообщения
|
||||
$ctx->sendMessage(<<<TXT
|
||||
🏷 *Категории*
|
||||
|
||||
*Загружено:* $categories_loaded
|
||||
|
||||
*Добавлено:* $categories_created
|
||||
*Обновлено:* $categories_updated
|
||||
*Удалено:* $categories_deleted
|
||||
|
||||
*Было:* $categories_old
|
||||
*Стало:* $categories_new
|
||||
TXT)
|
||||
->then(function ($message) use ($ctx, $products_loaded, $products_created, $products_updated, $products_deleted, $products_old, $products_new) {
|
||||
$ctx->sendMessage(<<<TXT
|
||||
📦 *Товары*
|
||||
|
||||
*Загружено:* $products_loaded
|
||||
|
||||
*Добавлено:* $products_created
|
||||
*Обновлено:* $products_updated
|
||||
*Удалено:* $products_deleted
|
||||
|
||||
*Было:* $products_old
|
||||
*Стало:* $products_new
|
||||
TXT)
|
||||
->then(function ($message) use ($ctx) {
|
||||
// Завершение диалога
|
||||
$ctx->endConversation();
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Не имеет расширение xlsx файл
|
||||
|
@ -473,7 +536,7 @@ final class chat extends core
|
|||
// Поиск технических работ
|
||||
$suspension = suspension::search();
|
||||
|
||||
if ($suspension && $suspension->targets['chat-robot']) {
|
||||
if ($suspension && $suspension->targets['telegram-robot']) {
|
||||
// Найдена активная приостановка
|
||||
|
||||
// Инициализация аккаунта
|
||||
|
@ -501,7 +564,8 @@ final class chat extends core
|
|||
$message = "⚠️ *Работа приостановлена*\n*Оставшееся время\:* " . $suspension->message($account->language ?? controller::$settings?->language);
|
||||
|
||||
// Добавление описания причины приостановки, если найдена
|
||||
if (!empty($suspension->description)) $message .= "\n\n" . $suspension->description[$account->language ?? controller::$settings?->language] ?? array_values($suspension->description)[0];
|
||||
if (!empty($suspension->description))
|
||||
$message .= "\n\n" . $suspension->description[$account->language ?? controller::$settings?->language] ?? array_values($suspension->description)[0];
|
||||
|
||||
// Отправка сообщения
|
||||
$ctx->sendMessage($message)
|
||||
|
@ -519,84 +583,4 @@ final class chat extends core
|
|||
$next($ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write
|
||||
*
|
||||
* Write a property into an instance of the ArangoDB document
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
* @param mixed $value Content of the property
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __set(string $name, mixed $value = null): void
|
||||
{
|
||||
// Write to the property into an instance of the ArangoDB document and exit (success)
|
||||
$this->document->{$name} = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read
|
||||
*
|
||||
* Read a property from an instance of the ArangoDB docuemnt
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
*
|
||||
* @return mixed Content of the property
|
||||
*/
|
||||
public function __get(string $name): mixed
|
||||
{
|
||||
// Read a property from an instance of the ArangoDB document and exit (success)
|
||||
return match ($name) {
|
||||
'arangodb' => $this::$arangodb,
|
||||
default => $this->document->{$name}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*
|
||||
* Deinitialize the property in an instance of the ArangoDB document
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
// Delete the property in an instance of the ArangoDB document and exit (success)
|
||||
unset($this->document->{$name});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check of initialization
|
||||
*
|
||||
* Check of initialization of the property into an instance of the ArangoDB document
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
*
|
||||
* @return bool The property is initialized?
|
||||
*/
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
// Check of initializatio nof the property and exit (success)
|
||||
return isset($this->document->{$name});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a method
|
||||
*
|
||||
* Execute a method from an instance of the ArangoDB document
|
||||
*
|
||||
* @param string $name Name of the method
|
||||
* @param array $arguments Arguments for the method
|
||||
*
|
||||
* @return mixed Result of execution of the method
|
||||
*/
|
||||
public function __call(string $name, array $arguments = []): mixed
|
||||
{
|
||||
// Execute the method and exit (success)
|
||||
if (method_exists($this->document, $name)) return $this->document->{$name}($arguments);
|
||||
}
|
||||
}
|
|
@ -10,9 +10,17 @@ use mirzaev\arming_bot\models\core;
|
|||
// Library для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\connection as arangodb;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Trait for implementing a document instance from ArangoDB
|
||||
*
|
||||
* @var protected readonly _document|null $document An instance of the ArangoDB document
|
||||
*
|
||||
* @package mirzaev\arming_bot\models\traits
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
|
@ -23,6 +31,28 @@ trait document
|
|||
*/
|
||||
protected readonly _document $document;
|
||||
|
||||
/**
|
||||
* Constructor of an instance
|
||||
*
|
||||
* @param bool $initialize Initialize a model?
|
||||
* @param ?arangodb $arangodb Instance of a session of ArangoDB
|
||||
* @param _document|null|false $document An instance of the ArangoDB document
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
bool $initialize = true,
|
||||
?arangodb $arangodb = null,
|
||||
_document|null|false $document = false
|
||||
) {
|
||||
// For the extends system
|
||||
parent::__construct($initialize, $arangodb);
|
||||
|
||||
// Writing to the property
|
||||
if ($document instanceof _document) $this->document = $document;
|
||||
else if ($document === null) throw new exception('Failed to initialize an instance of the document from ArangoDB');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write or read document
|
||||
*
|
||||
|
@ -30,13 +60,13 @@ trait document
|
|||
*
|
||||
* @return _document|null Instance of document from ArangoDB
|
||||
*/
|
||||
public function __document(?_document $document): ?_document
|
||||
public function __document(?_document $document = null): ?_document
|
||||
{
|
||||
// Write a property storing a document instance to ArangoDB
|
||||
if ($document) $this->document = $document;
|
||||
// Writing a property storing a document instance to ArangoDB
|
||||
if ($document) $this->document ??= $document;
|
||||
|
||||
// Read a property storing a document instance to ArangoDB and exit (success)
|
||||
return $this->document;
|
||||
return $this->document ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +81,7 @@ trait document
|
|||
*/
|
||||
public function __set(string $name, mixed $value = null): void
|
||||
{
|
||||
// Write to the property into an instance of the ArangoDB document and exit (success)
|
||||
// Writing to the property into an instance of the ArangoDB document and exit (success)
|
||||
$this->document->{$name} = $value;
|
||||
}
|
||||
|
||||
|
@ -116,6 +146,6 @@ trait document
|
|||
public function __call(string $name, array $arguments = []): mixed
|
||||
{
|
||||
// Execute the method and exit (success)
|
||||
return method_exists($this->document, $name) ?$this->document->{$name}($arguments) ?? null : null;
|
||||
return method_exists($this->document, $name) ? $this->document->{$name}($arguments) ?? null : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models\traits;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Trait for initialization of files handlers
|
||||
*
|
||||
* @package mirzaev\arming_bot\models\traits
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
trait files
|
||||
{
|
||||
/**
|
||||
* Delete files recursively
|
||||
*
|
||||
* @param string $directory Directory
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function delete(string $directory, array &$errors = []): void
|
||||
{
|
||||
try {
|
||||
if (file_exists($directory)) {
|
||||
// Directory exists
|
||||
|
||||
// Deleting descendant files and directories (enter to the recursion)
|
||||
foreach (scandir($directory) as $file) {
|
||||
if ($file === '.' || $file === '..') continue;
|
||||
else if (is_dir("$directory/$file")) static::delete("$directory/$file", $errors);
|
||||
else unlink("$directory/$file");
|
||||
}
|
||||
|
||||
// Deleting the directory
|
||||
rmdir($directory);
|
||||
|
||||
// Exit (success)
|
||||
return;
|
||||
} else throw new exception('Directory does not exist');
|
||||
} 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;
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ trait status
|
|||
// Read from ArangoDB and exit (success)
|
||||
return $this->document->active ?? false;
|
||||
} catch (exception $e) {
|
||||
// Write to the registry of errors
|
||||
// Writing to the registry of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models\traits\yandex;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Trait for "Yandex Disk"
|
||||
*
|
||||
* @package mirzaev\arming_bot\models\traits\yandex
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
trait disk
|
||||
{
|
||||
/**
|
||||
* Download file from "Yandex Disk"
|
||||
*
|
||||
* @param string $uri URI of the file from "Yandex Disk"
|
||||
* @param string $destination Destination to write the file
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return bool The file is downloaded?
|
||||
*/
|
||||
private static function download(
|
||||
string $uri,
|
||||
string $destination,
|
||||
array &$errors = []
|
||||
): bool {
|
||||
try {
|
||||
if (!empty($uri)) {
|
||||
// Not empty URI
|
||||
|
||||
if (!empty($destination)) {
|
||||
// Not empty destination
|
||||
|
||||
// Initializing URL of the file
|
||||
$url = "https://cloud-api.yandex.net/v1/disk/public/resources/download?public_key=$uri";
|
||||
|
||||
// Checking if the file is available for download
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($code === 200) {
|
||||
// The file is available for download
|
||||
|
||||
// Downloading the file and exit (success)
|
||||
return file_put_contents($destination, file_get_contents(json_decode(file_get_contents($url))?->href)) > 0;
|
||||
} else throw new exception("File not available for download: $uri");
|
||||
} else throw new exception("Empty destination");
|
||||
} else throw new exception("Empty URI");
|
||||
} 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 false;
|
||||
}
|
||||
}
|
|
@ -12,9 +12,9 @@ use mirzaev\arming_bot\controllers\core as controller,
|
|||
use mirzaev\minimal\core,
|
||||
mirzaev\minimal\router;
|
||||
|
||||
/* ini_set('error_reporting', E_ALL);
|
||||
ini_set('error_reporting', E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1); */
|
||||
ini_set('display_startup_errors', 1);
|
||||
|
||||
// Версия робота
|
||||
define('ROBOT_VERSION', '1.0.0');
|
||||
|
|
|
@ -15,7 +15,10 @@ import("/js/core.js").then(() =>
|
|||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
const timer_for_response = setTimeout(() => {
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
import("/js/core.js").then(() =>
|
||||
import("/js/damper.js").then(() => {
|
||||
const dependencies = setInterval(() => {
|
||||
if (typeof core === "function" &&
|
||||
typeof core.damper === "function") {
|
||||
if (
|
||||
typeof core === "function" &&
|
||||
typeof core.damper === "function"
|
||||
) {
|
||||
clearInterval(dependencies);
|
||||
clearTimeout(timeout);
|
||||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
if (typeof core.cart === "undefined") {
|
||||
|
|
|
@ -6,7 +6,12 @@ import("/js/core.js").then(() =>
|
|||
import("/js/telegram.js").then(() => {
|
||||
import("/js/hotline.js").then(() => {
|
||||
const dependencies = setInterval(() => {
|
||||
console.log(typeof core, typeof core.damper, typeof core.telegram, typeof core.hotline);
|
||||
console.log(
|
||||
typeof core,
|
||||
typeof core.damper,
|
||||
typeof core.telegram,
|
||||
typeof core.hotline,
|
||||
);
|
||||
if (
|
||||
typeof core === "function" &&
|
||||
typeof core.damper === "function" &&
|
||||
|
@ -18,7 +23,10 @@ import("/js/core.js").then(() =>
|
|||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
if (typeof core.catalog === "undefined") {
|
||||
|
@ -31,6 +39,13 @@ import("/js/core.js").then(() =>
|
|||
*/
|
||||
static categories = [];
|
||||
|
||||
/**
|
||||
* Registry of filters (instead of cookies)
|
||||
*/
|
||||
static filters = new Map([
|
||||
['brand', null]
|
||||
]);
|
||||
|
||||
/**
|
||||
* Select a category (interface)
|
||||
*
|
||||
|
@ -453,63 +468,63 @@ import("/js/core.js").then(() =>
|
|||
const images = document.createElement("div");
|
||||
images.classList.add("images", "unselectable");
|
||||
|
||||
const button = core.telegram.api.isVisible;
|
||||
|
||||
const _open = (event) => {
|
||||
if (event.target === from) {
|
||||
if (typeof images.hotline === "object") {
|
||||
if (images.hotline.moving) return;
|
||||
images.hotline.stop();
|
||||
}
|
||||
|
||||
images.classList.add("extend");
|
||||
|
||||
if (button) core.telegram.api.MainButton.hide();
|
||||
|
||||
setTimeout(() => {
|
||||
images.addEventListener("click", _close);
|
||||
images.addEventListener("touch", _close);
|
||||
}, 300);
|
||||
images.removeEventListener("mouseup", _open);
|
||||
images.removeEventListener("touchend", _open);
|
||||
}
|
||||
};
|
||||
|
||||
const _close = () => {
|
||||
if (typeof images.hotline === "object") {
|
||||
images.hotline.start();
|
||||
}
|
||||
|
||||
images.classList.remove("extend");
|
||||
|
||||
if (button) core.telegram.api.MainButton.show();
|
||||
|
||||
images.removeEventListener("click", _close);
|
||||
images.removeEventListener("touch", _close);
|
||||
images.addEventListener("mousedown", _start);
|
||||
images.addEventListener("touchstart", _start);
|
||||
};
|
||||
|
||||
const _start = (event) => {
|
||||
if (
|
||||
event.type === "touchstart" ||
|
||||
event.button === 0
|
||||
) {
|
||||
images.removeEventListener("mousedown", _start);
|
||||
images.removeEventListener("touchstart", _start);
|
||||
images.addEventListener("mouseup", _open);
|
||||
images.addEventListener("touchend", _open);
|
||||
}
|
||||
};
|
||||
|
||||
images.addEventListener("mousedown", _start);
|
||||
images.addEventListener("touchstart", _start);
|
||||
|
||||
for (const uri of json.product.images) {
|
||||
const image = document.createElement("img");
|
||||
image.setAttribute("src", uri);
|
||||
image.setAttribute("ondragstart", "return false;");
|
||||
|
||||
const button = core.telegram.api.isVisible;
|
||||
|
||||
const open = (event) => {
|
||||
if (event.target === from) {
|
||||
if (typeof images.hotline === "object") {
|
||||
if (images.hotline.moving) return;
|
||||
images.hotline.stop();
|
||||
}
|
||||
|
||||
image.classList.add("extend");
|
||||
|
||||
if (button) core.telegram.api.MainButton.hide();
|
||||
|
||||
setTimeout(() => {
|
||||
image.addEventListener("click", close);
|
||||
image.addEventListener("touch", close);
|
||||
}, 300);
|
||||
image.removeEventListener("mouseup", open);
|
||||
image.removeEventListener("touchend", open);
|
||||
}
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
if (typeof images.hotline === "object") {
|
||||
images.hotline.start();
|
||||
}
|
||||
|
||||
image.classList.remove("extend");
|
||||
|
||||
if (button) core.telegram.api.MainButton.show();
|
||||
|
||||
image.removeEventListener("click", close);
|
||||
image.removeEventListener("touch", close);
|
||||
image.addEventListener("mousedown", start);
|
||||
image.addEventListener("touchstart", start);
|
||||
};
|
||||
|
||||
const start = (event) => {
|
||||
if (
|
||||
event.type === "touchstart" ||
|
||||
event.button === 0
|
||||
) {
|
||||
image.removeEventListener("mousedown", start);
|
||||
image.removeEventListener("touchstart", start);
|
||||
image.addEventListener("mouseup", open);
|
||||
image.addEventListener("touchend", open);
|
||||
}
|
||||
};
|
||||
|
||||
image.addEventListener("mousedown", start);
|
||||
image.addEventListener("touchstart", start);
|
||||
|
||||
images.append(image);
|
||||
}
|
||||
|
||||
|
@ -539,7 +554,7 @@ import("/js/core.js").then(() =>
|
|||
const cost = document.createElement("p");
|
||||
cost.classList.add("cost");
|
||||
cost.innerText = json.product.cost + "р";
|
||||
|
||||
|
||||
h3.append(title);
|
||||
h3.append(brand);
|
||||
card.append(h3);
|
||||
|
|
|
@ -9,7 +9,10 @@ import("/js/core.js").then(() => {
|
|||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
if (typeof core.damper === "undefined") {
|
||||
|
|
|
@ -9,7 +9,10 @@ import("/js/core.js").then(() => {
|
|||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
if (typeof core.hotline === "undefined") {
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
import("/js/core.js").then(() =>
|
||||
import("/js/damper.js").then(() => {
|
||||
const dependencies = setInterval(() => {
|
||||
if (typeof core === "function" &&
|
||||
typeof core.damper === "function") {
|
||||
if (
|
||||
typeof core === "function" &&
|
||||
typeof core.damper === "function"
|
||||
) {
|
||||
clearInterval(dependencies);
|
||||
clearTimeout(timeout);
|
||||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
if (typeof core.session === "undefined") {
|
||||
|
|
|
@ -13,7 +13,10 @@ import("/js/core.js").then(() =>
|
|||
initialization();
|
||||
}
|
||||
}, 10);
|
||||
const timeout = setTimeout(() => clearInterval(dependencies), 5000);
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(dependencies);
|
||||
initialization();
|
||||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
if (typeof core.telegram === "undefined") {
|
||||
|
|
|
@ -7,16 +7,16 @@ namespace mirzaev\arming_bot;
|
|||
// Files of the project
|
||||
use mirzaev\arming_bot\controllers\core as controller,
|
||||
mirzaev\arming_bot\models\core as model,
|
||||
mirzaev\arming_bot\models\chat;
|
||||
mirzaev\arming_bot\models\telegram;
|
||||
|
||||
// Фреймворк Telegram
|
||||
use Zanzara\Zanzara,
|
||||
Zanzara\Context,
|
||||
Zanzara\Config;
|
||||
|
||||
/* ini_set('error_reporting', E_ALL);
|
||||
ini_set('error_reporting', E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1); */
|
||||
ini_set('display_startup_errors', 1);
|
||||
|
||||
// Версия робота
|
||||
define('ROBOT_VERSION', '1.0.0');
|
||||
|
@ -62,27 +62,27 @@ $bot = new Zanzara(KEY, $config);
|
|||
var_dump($ctx->getEffectiveUser() );
|
||||
}); */
|
||||
|
||||
$bot->onCommand('start', fn($ctx) => chat::start($ctx));
|
||||
$bot->onCommand('contacts', fn($ctx) => chat::contacts($ctx));
|
||||
$bot->onCommand('company', fn($ctx) => chat::company($ctx));
|
||||
$bot->onCommand('community', fn($ctx) => chat::community($ctx));
|
||||
$bot->onCommand('settings', fn($ctx) => chat::settings($ctx));
|
||||
$bot->onCommand('start', fn($ctx) => telegram::start($ctx));
|
||||
$bot->onCommand('contacts', fn($ctx) => telegram::contacts($ctx));
|
||||
$bot->onCommand('company', fn($ctx) => telegram::company($ctx));
|
||||
$bot->onCommand('community', fn($ctx) => telegram::community($ctx));
|
||||
$bot->onCommand('settings', fn($ctx) => telegram::settings($ctx));
|
||||
|
||||
$bot->onText('💬 Контакты', fn($ctx) => chat::contacts($ctx));
|
||||
$bot->onText('🏛️ О компании', fn($ctx) => chat::company($ctx));
|
||||
$bot->onText('🎯 Сообщество', fn($ctx) => chat::community($ctx));
|
||||
$bot->onText('⚙️ Настройки', fn($ctx) => chat::settings($ctx));
|
||||
$bot->onText('💬 Контакты', fn($ctx) => telegram::contacts($ctx));
|
||||
$bot->onText('🏛️ О компании', fn($ctx) => telegram::company($ctx));
|
||||
$bot->onText('🎯 Сообщество', fn($ctx) => telegram::community($ctx));
|
||||
$bot->onText('⚙️ Настройки', fn($ctx) => telegram::settings($ctx));
|
||||
|
||||
$bot->onCbQueryData(['mail'], fn($ctx) => chat::_mail($ctx));
|
||||
$bot->onCbQueryData(['import_request'], fn($ctx) => chat::import_request($ctx));
|
||||
$bot->onCbQueryData(['tuning'], fn($ctx) => chat::tuning($ctx));
|
||||
$bot->onCbQueryData(['brands'], fn($ctx) => chat::brands($ctx));
|
||||
$bot->onCbQueryData(['mail'], fn($ctx) => telegram::_mail($ctx));
|
||||
$bot->onCbQueryData(['import_request'], fn($ctx) => telegram::import_request($ctx));
|
||||
$bot->onCbQueryData(['tuning'], fn($ctx) => telegram::tuning($ctx));
|
||||
$bot->onCbQueryData(['brands'], fn($ctx) => telegram::brands($ctx));
|
||||
|
||||
// Инициализация middleware с обработкой аккаунта
|
||||
$bot->middleware([chat::class, "account"]);
|
||||
$bot->middleware([telegram::class, "account"]);
|
||||
|
||||
// Инициализация middleware с обработкой технических работ разных уровней
|
||||
$bot->middleware([chat::class, "suspension"]);
|
||||
$bot->middleware([telegram::class, "suspension"]);
|
||||
|
||||
// Запуск чат-робота
|
||||
$bot->run();
|
||||
|
|
|
@ -8,21 +8,60 @@ main>section[data-section="catalog"] {
|
|||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"] {
|
||||
position: relative;
|
||||
height: 23px;
|
||||
padding: 8px 16px;
|
||||
padding: unset;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
border-radius: 0.75rem;
|
||||
color: var(--tg-theme-button-text-color);
|
||||
background-color: var(--tg-theme-button-color);
|
||||
}
|
||||
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]:last-child {
|
||||
/* margin-bottom: unset; */
|
||||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img) {
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]>img {
|
||||
position: absolute;
|
||||
left: -5%;
|
||||
top: -5%;
|
||||
width: 110%;
|
||||
height: 110%;
|
||||
object-fit: cover;
|
||||
filter: blur(1px);
|
||||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:hover>img {
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img)>p {
|
||||
--padding: 0.7rem;
|
||||
position: absolute;
|
||||
left: var(--padding);
|
||||
bottom: var(--padding);
|
||||
right: var(--padding);
|
||||
margin: unset;
|
||||
width: min-content;
|
||||
padding: var(--padding);
|
||||
border-radius: 0.75rem;
|
||||
background: var(--tg-theme-secondary-bg-color);
|
||||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]>p {
|
||||
z-index: 100;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
main>section[data-section="catalog"][data-catalog-type="products"] {
|
||||
--column: calc((100% - var(--gap)) / 2);
|
||||
width: var(--width);
|
||||
|
@ -120,3 +159,21 @@ main>section[data-section="cart"]>i.icon.shopping.cart {
|
|||
top: -1px;
|
||||
color: var(--tg-theme-button-text-color);
|
||||
}
|
||||
|
||||
main>section[data-section="filters"] {
|
||||
--diameter: 4rem;
|
||||
z-index: 999;
|
||||
right: 5vw;
|
||||
bottom: 5vw;
|
||||
position: fixed;
|
||||
width: var(--diameter);
|
||||
height: var(--diameter);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border-radius: 100%;
|
||||
background-color: var(--tg-theme-button-color);
|
||||
}
|
||||
|
||||
main>section[data-section="filters"][data-filter="brand"] {}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
i.icon.hashtag {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(1);
|
||||
width: 8px;
|
||||
height: 16px;
|
||||
border-left: 2px solid;
|
||||
border-right: 2px solid;
|
||||
}
|
||||
|
||||
i.icon.hashtag::before {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 8px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
left: -6px;
|
||||
top: 4px;
|
||||
}
|
||||
|
|
@ -50,6 +50,21 @@ section#window>div.card>div.images {
|
|||
overflow: clip;
|
||||
}
|
||||
|
||||
section#window>div.card>div.images.extend {
|
||||
z-index: 9999999;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin: unset !important;
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
max-width: unset;
|
||||
height: 100vh;
|
||||
object-fit: contain;
|
||||
border-radius: unset;
|
||||
transition: 0s;
|
||||
cursor: zoom-out;
|
||||
}
|
||||
|
||||
section#window>div.card>div.images>img {
|
||||
margin-right: 0.5rem;
|
||||
width: 10rem;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
!example.xlsx
|
||||
import.xlsx
|
|
@ -0,0 +1,2 @@
|
|||
!.gitignore
|
||||
*
|
|
@ -0,0 +1,2 @@
|
|||
!.gitignore
|
||||
*
|
|
@ -0,0 +1,2 @@
|
|||
!.gitignore
|
||||
*
|
|
@ -3,8 +3,10 @@
|
|||
data-catalog-level="{{ level ?? 0 }}">
|
||||
{% for category in categories %}
|
||||
<a id="{{ category.getId() }}" class="category" type="button" onclick="return core.catalog.category(this);"
|
||||
data-category-name="{{ category.name }}">{{
|
||||
category.label.ru }}</a>
|
||||
data-category-identifier="{{ category.identifier }}">
|
||||
<img src="{{ category.images.0.storage }}" alt="{{ category.name[ account.language ?? settings.language ?? 'en' ] }}" ondrugstart="return false;">
|
||||
<p>{{ category.name[ account.language ?? settings.language ?? 'en' ] }}</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{% if filters is not empty %}
|
||||
<section data-section="filters" data-filter="brand">
|
||||
{% for brand in filters.brands %}
|
||||
<input class="menu" name="brand" type="radio" id="brand_{{ loop.index }}" checked>
|
||||
<label for="brand_{{ loop.index }}" class="menu option">{{ brand }}</label>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
|
@ -3,7 +3,7 @@
|
|||
~ product.dimensions.z ~ ' ' ~ product.weight ~ 'г' %}
|
||||
<article id="{{ product.getId() }}" class="product unselectable">
|
||||
<a onclick="core.catalog.product({{ product.getKey() }})">
|
||||
<img src="/images/{{ product.getKey() }}/1.jpg" alt="{{ product.title.ru }}" ondrugstart="return false;">
|
||||
<img src="{{ product.images.0.storage }}" alt="{{ product.title.ru }}" ondrugstart="return false;">
|
||||
<p class="title" title="{{ product.title.ru }}">
|
||||
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
||||
</p>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
{% include "/themes/default/catalog/elements/categories.html" %}
|
||||
{% include "/themes/default/catalog/elements/products/2columns.html" %}
|
||||
<!-- {% include "/themes/default/catalog/elements/cart.html" %} -->
|
||||
{% include "/themes/default/catalog/elements/filters.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
|
|
Loading…
Reference in New Issue