The custom menu allows you to create your own functionality for processing (adding/updating) goods, which allows you to adapt the loading of goods to any requirements and database structures and, accordingly, to any templates, modules and catalog structures.
Menus are created by editing the “user_mark_menu.json” file . This file is a JSON object containing an array of elements, each of which represents a menu item. Each menu item can contain nested items in a similar structure, which allows you to visualize the data hierarchy in the application interface and distribute the launch of product processing commands.
The file “user_mark_menu.json” is located in the application folder at the following path: “/admin/pricemaster/data/menu/custom_menu/”
[ { "name" : "<i class='fa fa-user' aria-hidden='true'></i> User menu" , "data" : { "my_custom_data" : "my_value" , "php:store_id" : "example_scripts/get_store_id.php" , "user_group_id" : "1" }, "children" : [ { "name" : "Quantity" , "data" : { "db-d:oc_test_store" : "product_id='data:product_id', store_id='data:store_id'" , "db-i:oc_test_store" : "quantity='cell_value', store_id='data:store_id', product_id='data:product_id'" } }, { "name" : "Price" , "data" :{}, "children" : "php:example_scripts/currency_menu.php" } ] }, { "name" : "Attributes" , "children" : "php:example_scripts/atrributes_menu.php" , "data" : { "php-a:res_attr_php" : "example_scripts/add_attribute.php" } } ] |
This example demonstrates the basic elements of the menu structure, the use of commands for working with the database, and the possibility of nesting menu items.
The file is in “JSON” format and contains an array of objects, each of which represents a separate menu item. Each object can include the following keys:
- “name” - The name of the menu item, which may include HTML for formatting (for example, icons).
- "data" - A data object containing commands and parameters to perform certain actions (for example, database operations).
- “children” - An array containing nested menu items of a similar structure
Each menu item can also contain a "data" object , which includes the data and commands to be executed. The "data" object can contain both simple data and commands for working with the database or executing PHP scripts, which are written in key-value format.
When you select the final menu item (via the interface), a number of commands ( SQL queries or PHP script calls ) will be written to the markup data (for the selected column). These commands are executed in order, depending on the menu structure.
The “ data ” object initially contains the product identifier under the “ product_id ” key.
Access to the "data" object is possible both at the stage of creating the menu (in the file "user_mark_menu.json" ) and at the stage of executing PHP scripts. Using special syntax, you can get and change values in "data" and create nested objects to organize the data.
"data" : { "key" : "php:path_to_file.php" } | |
Such a command will return the result of executing the file "path_to_file.php" into the "key" of the "data" object , which allows the resulting data to be used in subsequent commands. |
You can create and then access nested data structures within a data object, allowing you to organize data into hierarchical structures. This is especially useful when working with complex data that requires multiple levels of nesting.
You can create nested objects using the arrow syntax “->”:
"data" : { "key1->key2->key3" : "php:path_to_file.php" } | |
When this command is executed, the following structure will be created in the "data" object: "data" : { "key1" : { "key2" : { "key3" : "result_php_script" } } } |
Example of a database access command: "data" : { "db-i:oc_test_store" : "quantity='cell_value', product_id='data:product_id'" } | |
| |
Accessing the nested object would look like this: "data" : { "db-i:oc_test_store" : "quantity='cell_value', product_id='data:key1->key2->key3'" } | |
|
When executing PHP scripts, you will have full access to the “data” object , which will be passed as an argument to the called function. This means that you can both access and make changes to the “data” object , allowing you to dynamically manage data during product processing and adapt logic on the fly.
Commands play a key role; they determine what actions should be taken on a product when the user selects certain menu items. Commands include SQL operations such as inserting, deleting, and updating data, as well as executing specific scripts and logic through PHP scripts.
The commands will be executed sequentially, relative to the hierarchy of commands from the "data" objects from the initial to the final selected menu item, with the exception of PHP commands: “ php-b :” and “ php-a: ”
You can configure the execution of SQL commands for various database operations. These commands are executed in the context of processing each item in the price list.
Example: | |
"data" : { "db-i:oc_test_store" : "quantity='cell_value', product_id='data:product_id'" } | |
This command will insert data into the “oc_test_store” table . Given that: The SQL query would look like this: | |
INSERT INTO oc_test_store SET quantity = '100', product_id = '1727' |
Example: | |
"data" : { "db-d:oc_test_store" : "product_id='data:product_id', store_id='data:store_id'" } | |
This command deletes records from the “oc_test_store” table Given that: “ data:store_id ” = “0” The SQL query would look like this: | |
DELETE FROM oc_test_store WHERE product_id = '1727' AND store_id = '0' |
Example: | |
"data" : { "db-u:oc_test_store" : [ "quantity='cell_value'" , "product_id='data:product_id'" ] } | |
This command updates data in the “oc_test_store” table Given that: “ cell_value ” = “100” The SQL query would look like this: | |
UPDATE oc_test_store SET quantity = '100' WHERE product_id = '1727' |
Commands for calling PHP scripts can be located both in the keys and in the values of the "data" object , which affects the scope of the results and the moment the command is executed .
These commands are executed during the process of adding and updating products from the price list. In such PHP scripts, the "data" object is available , including the product identifier "product_id" , which allows you to work with specific product data.
Example command: | |
"data" : { "php:store_id" : "get_store_id.php" } | |
This command executes the script “get_store_id.php” and places the result in the “store_id” key (in the "data" object ), making this value available to other commands. In this case, the script is “get_store_id.php” should be located at: “/admin/pricemaster/data/menu/custom_menu/get_store_id.php” To organize the structure, you can create your own folders and subfolders in the root directory of the custom menu “/admin/pricemaster/data/menu/custom_menu/" . Thus, if you create a folder “new_dir” and put the file “get_store_id.php” there - the path to the file should look like this: “ new_dir/get_store_id.php ” Example file “get_store_id.php” : | |
<?php return function ( $DB , & $data , $column_name , $cell_value , $file_name , $product_data ) { $store_id = 0 ; $query = $DB -> query ( " SELECT * FROM oc_store LIMIT 1 " ); if ( count ( $query -> rows ) > 0 ) { $store_id = $query -> row [ 'store_id' ]; } $store_id = strval ( $store_id ); return $store_id ; } ?> | |
The script must return an anonymous function, which in turn returns a result that will be stored in the “store_id” key in the “data” object. In such scripts, the following function arguments are available:
|
Command execution order:
As mentioned above:
All commands will be executed sequentially, relative to the hierarchy of commands from the "data" objects from the initial to the final selected menu item, with the exception of PHP commands “ php-b :” and “ php-a: ”
php-b: - prefix before , means executing commands before all others (including the commands “db-i:”, “db-d:”, “db-u:” ). Such commands will be launched first, regardless of their location in the menu hierarchy.
php-a: — prefix after , executed at the end, after all commands: “db-i:”, “db-d:”, “db-u:”, “php:”, “php-b” ). This allows you to process the data and make last minute changes or saves to the database.
php : - will be executed relative to the menu hierarchy, from the initial to the final selected menu item.
If the command is called from a value inside a "data" object , it will be executed when the custom menu is created. Such scripts modify the user menu during its creation.
Examples:
Example of adding data to the menu:
"data" : { "current_lang_id" : "php:example_scripts/get_current_lang_id.php" } | |
This command will execute the script “example_scripts/get_current_lang_id.php” If the script returns the result “2” , then in finished form this part of the menu will look like this: | |
"data" : { "current_lang_id" : "2" } | |
Example script file: “example_scripts/get_current_lang_id.php” : | |
<?php return function () { $current_lang_id = strval ( $this -> registry -> get ( 'config' ) -> get ( 'config_language_id' )); return $current_lang_id ; } ?> | |
As you can see, this function has no arguments, but it runs in the scope of the “Model”(opencart) and this function sees the object of the “$this” class , Accordingly, you can use all methods of the “Model” class . |
Example of creating menu items:
An example of one menu item in the file “user_mark_menu.json” : | |
{ "name" : "Price" , "data" :{}, "children" : "php:example_scripts/currency_menu.php" } | |
Example file “currency_menu.php” : | |
<?php return function () { $result = array (); $query = $this -> db -> query ( " SELECT * FROM oc_currency" ); foreach ( $query -> rows as $_ => $db_row ) { $one_item = array ( "name" => "Price (" . $db_row [ 'title' ] . ")" , "data" => array ( "db-u:oc_product" => array ( "price='cell_value'" , "currency='" . $db_row [ 'currency_id' ] . "', product_id='data:product_id'" ) ) ); array_push ( $result , $one_item ); } return $result ; } ?> | |
This script takes all available currencies from the store from the “oc_currency” table and for each currency creates its own menu item with the “db-u” (UPDATE) command . Thus, we got a menu with which you can mark the price column by linking the corresponding currency to it. The menu generated by the script will look like this: |
Such values can be product attributes , categories , images , and so on; in other words, these are catalog objects that can have several values from different columns of the price list table to create one object.
For example, “Product characteristics” - each characteristic has its own name , meaning , group of characteristics and language, example:
Feature name: | "Color" | |
Characteristic value: | "Red" | |
Group of characteristics: | “Main Features” | |
Language: | "English" |
In order to correctly enter a characteristic and associate its name with its value and group of characteristics and language, we need to create a menu where there will be a choice of all these fields. To do this, in the final menu items we can use the built-in variable " {COLSMENU} " , which we can use as the value of the "children" key ( "children":"{COLSMENU}" ).
" {COLSMENU} " - will create a menu for selecting price columns in the final menu items.
For example, we will create a menu with the following structure:
" Attributes " -> "Menu for selecting an attribute group " -> " Menu for selecting columns of attribute names and values for all languages "
Example of the " Attributes " menu in the " user_mark_menu.json " file: | |
{ "name" : "Attributes" , "children" : "php:atrributes_menu.php" , "data" : { "php-a:res_attr_php" : "add_attribute.php" } } | |
This example creates a menu item with the name " Attributes " whose sub-items will be generated by the PHP script " php:attributes_menu.php ", Example PHP script “ attributes_menu.php ”: | |
<?php return function () { $menu_result = array (); $current_language_id = $this -> registry -> get ( 'config' ) -> get ( 'config_language_id' ); // Get all attribute groups $attrs_group = $this -> db -> query ( " SELECT * FROM oc_attribute_group_description WHERE language_id = ' $current_language_id '" ); // We get all active languages of the online store $shop_langs = $this -> db -> query ( " SELECT * FROM oc_language WHERE status = '1'" ); // We go through each group of attributes foreach ( $attrs_group -> rows as $_ => $attr_group ) { // Get the attribute group ID $attribute_group_id = $attr_group [ 'attribute_group_id' ]; // Create a menu item for an attribute group $one_attr_group_item = array ( "name" => $attr_group [ 'name' ], // Attribute group name "data" => array ( "attribute_group_id" => $attribute_group_id , // Attribute group ID ), "children" => array () // Array for child elements ); // We go through each active language foreach ( $shop_langs -> rows as $_ => $lang ) { $language_id = $lang [ 'language_id' ]; // Get the language ID // Create a menu item for the attribute name in the current language $one_lang_item_name = array ( "name" => "Attribute name (" . $lang [ 'name' ] . ")" , // Menu item name "data" => array ( "product_attributes->" . $language_id . "->name" => "cell_value" // Path to attribute name ), "children" => "{COLSMENU}" // Column selection menu ); // Adding a menu item to attribute groups array_push ( $one_attr_group_item [ 'children' ], $one_lang_item_name ); // Create a menu item for the attribute value in the current language $one_lang_item_value = array ( "name" => "Attribute value (" . $lang [ 'name' ] . ")" , // Menu item name "data" => array ( "product_attributes->" . $language_id . "->value" => "cell_value" // Path to attribute value ), "children" => "{COLSMENU}" // Column selection menu ); // Adding a menu item to attribute groups array_push ( $one_attr_group_item [ 'children' ], $one_lang_item_value ); } // Adding an attribute group element to the menu result array_push ( $menu_result , $one_attr_group_item ); } return $menu_result ; // Return the menu result } ?> | |
Script explanation: The script receives a list of attribute groups and a list of languages, for each attribute group it creates a menu item with the name of the attribute group and "children" which will represent the names and values of the attributes for each language. For each language, a menu item is created with the name "Attribute name (language)" and "Attribute value (language)" and the children of which will be the price list column selection menu " {COLSMENU} ". An object with the key “product_attributes” is created in the “ data ” object , in which objects of attribute names and values will be nested under the language identifier key. As a result, the created menu will look like this: | |
Next, we see that in the "data" object there is a command “php-a:res_attr_php”: "add_attribute.php" is a command that will be executed after all the commands in the “ data ” object and it will add attributes to the database. | |
<?php // Function to add attribute function add_attribute ( $DB , $attributes_data , $attribute_group_id ) { $attribute_id = false ; // Insert a new attribute into the oc_attribute table $DB -> query ( " INSERT INTO oc_attribute SET attribute_group_id = ' $attribute_group_id ', sort_order='0' " ); $attribute_id = $DB -> getLastId (); // Insert attribute description for each language foreach ( $attributes_data as $language_id => $attr_data ) { $DB -> query ( " INSERT INTO oc_attribute_description SET attribute_id = ' $attribute_id ', language_id = ' $language_id ', name = '" . $attr_data [ "name" ] . "' " ); } return $attribute_id ; } // Function to get attribute ID function get_attribute_id ( $DB , $attributes_data , $attribute_group_id ) { $attribute_id = false ; // Create arrays for attribute names and language IDs $attr_names_arr = array (); $langs_id_arr = array (); foreach ( $attributes_data as $language_id => $attr_data ) { array_push ( $attr_names_arr , $attr_data [ "name" ]); array_push ( $langs_id_arr , $language_id ); } // Concatenate attribute names and language IDs for SQL query $attr_names_concat = "'" . implode ( "','" , $attr_names_arr ) . "'" ; $langs_ids_concat = "'" . implode ( "','" , $langs_id_arr ) . "'" ; $SQL = " SELECT att_d.attribute_id, COUNT(att_d.attribute_id) AS count FROM oc_attribute_description as att_d, oc_attribute as att WHERE att.attribute_id = att_d.attribute_id AND att_d.language_id IN(" . $langs_ids_concat . ") AND att_d.name IN (" . $attr_names_concat . ") AND att.attribute_group_id = ' $attribute_group_id ' GROUP BY att_d.attribute_id ORDER BY count DESC LIMIT 1 " ; // Execute query and get attribute ID $query = $DB -> query ( $SQL ); if ( count ( $query -> rows ) > 0 ) { $attribute_id = strval ( $query -> row [ 'attribute_id' ]); } return $attribute_id ; } // Anonymous function to add attribute to product return function ( $DB , & $data , $column_name , $cell_value , $file_name , $product_data ) { $insert_result = false ; $product_id = $data [ 'product_id' ]; $attribute_group_id = $data [ "attribute_group_id" ]; // Get attribute ID or create a new one if not found $attribute_id = get_attribute_id ( $DB , $data [ "product_attributes" ], $attribute_group_id ); if ( $attribute_id === false ) { $attribute_id = add_attribute ( $DB , $data [ "product_attributes" ], $attribute_group_id ); } else { // Delete old attribute records for the product $DB -> query ( " DELETE FROM oc_product_attribute WHERE product_id = ' $product_id ' AND attribute_id = ' $attribute_id ' " ); } // Insert new attribute records for the product if ( $attribute_id !== false ) { foreach ( $data [ "product_attributes" ] as $language_id => $attr_data ) { $DB -> query ( " INSERT INTO oc_product_attribute SET product_id = ' $product_id ', attribute_id = ' $attribute_id ', language_id = ' $language_id ', text = '" . $attr_data [ "value" ] . "' " ); } $insert_result = true ; } return $insert_result ; } ?> | |
Script explanation:
As well as an anonymous function that adds an attribute to a product, in this function the attribute is added to the product; if the attribute already exists, it will be updated. | |