📜 ⬆️ ⬇️

Adding your functionality to UMI.CMS using event handlers

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:

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:

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:

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:

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:

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:

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.

Source: https://habr.com/ru/post/166647/


All Articles