In the site management system UMI.CMS, the division into the main engine of the site, which is not touched by the web developer (and which is overwritten when the system is updated), and the additional (custom) functionality, which the site developer already adapts for himself: the design templates, macros (PHP functions called from templates), own modules, if necessary.
However, when developing your site, there are situations when it is necessary to change the existing functionality of the site:
- add your own logic to import data from XML;
- perform some actions when importing data;
- perform some actions when creating or changing an order;
- perform some actions on a schedule;
- … and so on.
In this case, you have to either edit the system code of the engine (which immediately adds problems when updating the CMS), or use the built-in event functionality. In the
documentation or
on third-party resources, this issue is considered, however, in my opinion, not sufficiently detailed. This article is an attempt to put together information about working with events in UMI.CMS, as well as to show, using an example, how the system can be extended with the help of event processing.
According to the documentation in UMI.CMS, there are two types of event handlers:
- system - these are pre-installed handlers, which are written when developing a module. These handlers are written in the events.php file, which lies in the module directory. For the modules included in the UMI.CMS distribution, this file cannot be changed.
- custom - these handlers must be in the custom_events.php file in the module directory.
When an event occurs, all handlers assigned to it (both system and user) are called. In addition (this is not written in the documentation), system event handlers are executed
after the user. This must be considered when developing its functionality.
Events can be triggered before the object changes (so-called
before mode) and after the object changes (
after mode). Not all events can be “before” and “after”, which ones, you should look at the link below.
')
A list of internal events can be found in the
documentation . It must be remembered that when handling the same object in different places, different event handlers will be called. This causes certain inconveniences:
Suppose, for example, we are developing an online store based on UMI, and we need to perform some actions when changing the status of an order (for example, send additional emails, set some other fields in the order, etc., depending on the status). We look where we can "catch" events of change of the status of the order:
- status change when a user creates a site order - the order-status-changed event of the Emarket module;
- status change from admin page on order details page - system event systemModifyObject ;
- status change from admin page on the list of all orders - system event systemModifyPropertyValue ;
- status change when importing it via XML (for example, when unloading from 1C) - the exchangeOnUpdateObject event.
Thus, in order to take into account all the places where the order status changes, you will have to write 4 handlers.
Let us dwell on the
systemModifyPropertyValue system event. I did not find any mention of him in the documentation, I saw his call only when analyzing the system code, and it’s quite difficult to manage without him in the described situation, since the site administrator (manager) can change the order data in two ways:
- on the main page of the Online Store module in the list of all orders - UMI allows editing status and other order data directly in this list;
- in the window of detailed information about the order, which opens after clicking on the order.
And if you use only the
systemModifyObject event
handler , you will have to explain to all future administrators of the site that they should in no case change the order data from the list of orders, but just have to go into each order and change something there. Which, of course, is very inconvenient and leaves a great opportunity to make a mistake.
The
systemModifyPropertyValue system event has the following parameters:
- entity - reference to the object whose property is being changed;
- property - the name of the property being changed;
- oldValue - old property value;
- newValue is the new value of the property;
This event handler can be used not only when editing the list of orders in the Online Store module, but also in other similar lists of objects in the UMI.CMS admin panel.
How an event handler is assigned can be viewed, for example,
here or
here . I want to show a more complicated example: how to add the missing functionality to the UMI using the creation of a data import event handler — to teach the system to import optional product properties.
Let, for example, we develop an online store selling t-shirts. We have established 1C "Trade Management", in which we are going to keep records of goods and orders. In 1C, all the necessary range of products has been entered and it is desirable that it be downloaded to the site with minimal actions.
The specificity of the sale of such goods is such that we have, for example, the Dolce T-shirt model, which has specific units sold:
- Dolce White T-shirt, 40 size;
- Dolce White T-shirt, 48 size;
- T-shirt Dolce Red, 44 size;
- … and so on.
That is, the set of positions of this model, differing in color and size (and possibly price, for certain combinations). In terms of 1C, these are “characteristics of the nomenclature”. And from the point of view of the online store, we want us to have one page of the “Dolce T-shirt” model, on which the buyer could choose his color and size and create an order with them.
From the point of view of 1C, everything seems simple. We put a tick "characteristics" in the settings of the nomenclature. We first upload the goods to the disk, look at the resulting XML file (offers.xml), we need these offers, rejoice and upload to the site. And here we understand that we rejoiced early. The goods themselves were added, but their characteristics (that a particular T-shirt has a dozen sentences with different combinations of colors and sizes) are not.
In the UMI system, the functionality we need is implemented using
optional properties . That is, at first glance, everything is there. However, after further digging in the source code and documentation, it turns out that in the current version of UMI (2.8.6) the Data Exchange module does not support the import of optional properties. So we will add the necessary functionality independently.
The specifics of importing from 1C to UMI are described in the
documentation . To add your functionality when importing data, you must modify the import template /xsl/import/custom/commerceML2.xsl, and add your own import event handler.
Modify the import template:
Import template<xsl:template match=""> ... <type id="charateristics-kinds" title=' ""' parent-id="root-guides-type" guide="guide"> <base/> <fieldgroups> <group name="charateristics_kinds"> <field name="1c_id" title=" 1" visible="visible"> <type name="" data-type="string"/> </field> <field name="color" title="" field-type-id="3" visible="visible" required="required" > <type id="3" name="" data-type="string"/> </field> <field name="size" title="" field-type-id="3" visible="visible" required="required" > <type id="3" name="" data-type="string"/> </field> </group> </fieldgroups> </type> <type id="shirts" title='1C: ' parent-id="root-catalog-object-type"> <base module="catalog" method="object"> </base> <fieldgroups> ... <group name="optioned_properties" title=" "> <field name="charateristics" title="" visible="visible" guide-id="charakteristics-kinds"> <type name="" data-type="optioned" multiple="multiple" /> </field> </group> </fieldgroups> </type> ... </xsl:template> <xsl:template match="/"> ... <page id="{}" parentId="{$group_id}" type-id="shirts"> ... </page> ... </xsl:template> <xsl:template match=""> <meta> <source-name>commerceML2</source-name> </meta> <objects> <xsl:apply-templates select="/" mode="objects"/> </objects> <pages> <xsl:apply-templates select="/" mode="items"/> </pages> </xsl:template> <xsl:template match="/" mode="objects"> <object id="{substring-after(,'#')}" name="{}" type-id="charakteristics-kinds"> <properties> <group name="charateristics_kinds"> <property name="1c_id" type="string" is-public="1" visible="visible"> <title> 1</title> <value><xsl:value-of select="" /></value> </property> <xsl:apply-templates select="/[ = '']" mode="color" /> <xsl:apply-templates select="/[ = '']" mode="size" /> </group> </properties> </object> </xsl:template> <xsl:template match="/" mode="color" > <property name="color" type="string" is-public="1" visible="visible"> <title><xsl:value-of select="" /></title> <value><xsl:value-of select="" /></value> </property> </xsl:template> <xsl:template match="/" mode="size" > <property name="size" type="string" is-public="1" visible="visible"> <title><xsl:value-of select="" /></title> <value><xsl:value-of select="" /></value> </property> </xsl:template> <xsl:template match="/" mode="items"> <page id="{substring-before(,'#')}" update-only="1"> <properties> <group name="optioned_properties" title=" "> <property name="charateristics" type="optioned" is-public="1" visible="visible"> <title></title> <value> <option int="{}" float="{//}"> <object id="{substring-after(,'#')}" name="{}" type-id="charakteristics-kinds" /> </option> </value> </property> </group> </properties> </page> </xsl:template>
Using this template, a separate directory is created (Reference book for the "Characteristics" field), in which all received characteristics options from 1C are written. In addition, the type for the catalog object (product) is modified to add optional properties there. Thus, we have already uploaded the characteristics to the site, it remains only to write a code that would match the characteristics of the product.
Add your event handler. To do this, create a custom_events.php file in the / classes / modules / exchange folder with the code
<?php new umiEventListener("exchangeOnUpdateElement", "exchange", "onImportElement"); new umiEventListener("exchangeOnAddElement", "exchange", "onImportElement"); ?>
Thus, we specified that both when creating and updating an item (product page) during import, the onImportElement method will be called. Let's write the code of this method in the __custom.php file:
Event handler code /** * 1 * @param e - */ public function onImportElement($e) { if($e->getMode() == "after") { // $this->addOptionedProperties($e); } } /** * . * UMI * @param e - */ function addOptionedProperties($e) { $hierarchy = umiHierarchy::getInstance(); $element = $e->getRef('element'); if (!$element instanceof umiHierarchyElement || $element->getMethod() != 'object') { // return false; } $object_id = $element->objectId; //XML DOM node $element_info = $e->getParam('element_info'); $properties = $element_info->getElementsByTagName('property'); $propertiesSize = $properties->length; $types = umiObjectTypesCollection::getInstance(); // XML foreach($properties as $key => $info) { $old_name = $info->getAttribute('name'); // $name = self::translateName($old_name); $nl = $info->getElementsByTagName("value"); if (!$nl->length) { // XML continue; } $value_node = $nl->item(0); // $type_id = ($element instanceof umiHierarchyElement) ? $element->getObjectTypeId() : $element->getTypeId(); $type = umiObjectTypesCollection::getInstance()->getType($type_id); $field_id = $type->getFieldId($name, false); $field = umiFieldsCollection::getInstance()->getField($field_id); if (!$field instanceof umiField) { continue; } switch($field->getDataType()) { // , UMI case "optioned": //storing old settings $oldForce = umiObjectProperty::$USE_FORCE_OBJECTS_CREATION; umiObjectProperty::$USE_FORCE_OBJECTS_CREATION = false; // , $objectsCollection = umiObjectsCollection::getInstance(); $guideItems = $objectsCollection->getGuidedItems($field->getGuideId()); $options = $value_node->getElementsByTagName("option"); $items = Array(); foreach($options as $option) { // int $int = $option->hasAttribute("int") ? $option->getAttribute("int") : null; // float $float = $option->hasAttribute("float") ? $option->getAttribute("float") : null; $objects = $option->getElementsByTagName("object"); foreach($objects as $object) { $objectId = $object->hasAttribute("id") ? $object->getAttribute("id") : null; $objectName = $object->hasAttribute("name") ? $object->getAttribute("name") : null; $objectTypeId = $object->hasAttribute("type-id") ? $object->getAttribute("type-id") : null; // $item = Array(); $item["int"] = (int)$int; $item["float"] = (float)$float; $item["varchar"] = $objectName; // , property foreach($guideItems as $key => $value) { if($value == $objectName) { // id int, $item["rel"] = (int)$key; break; } } $items[] = $item; } } // $entityId = $element->getId(); if($element instanceof umiHierarchyElement) { $entityId = $element->getObject()->getId(); } $pageObject = $objectsCollection->getObject($entityId); // , $existingItems = $pageObject->getValue($name); $newItems = Array(); if($existingItems) { // , foreach($existingItems as $existingItem) { $found = false; foreach($items as $item) { if($item["rel"] == $existingItem["rel"]) { $found = true; break; } } if(!$found) { $newItems[] = $existingItem; } } } // foreach($items as $item) { $newItems[] = $item; } $pageObject->setValue($name, $newItems); $pageObject->commit(); //restoring settings umiObjectProperty::$USE_FORCE_OBJECTS_CREATION = $oldForce; break; } } } /** * UMI- */ protected static function translateName($name) { $name = umiHierarchy::convertAltName($name, "_"); $name = umiObjectProperty::filterInputString($name); if(!strlen($name)) $name = '_'; $name = substr($name, 0, 64); return $name; }
In the above code,
after creating or adding a product (checking for after), we read the product properties from the XML and, if there are optional properties, add the corresponding optional properties to the product from the Reference book for the Characteristics field.
After adding the specified code and re-unloading goods to the site, we see that each product has positions, each with its own color and size. That is, the indicated task is dostingut, and now we have the UMI.CMS system able to import optional product properties from 1C.
Thus, events in UMI are a very powerful tool, and with the help of well-added event handlers, you can significantly extend the functionality of the site, without changing a single line of the CMS system code and retaining the possibility of system updates.