События (хуки)
Система событий позволяет разработчикам взаимодействовать с классами ядра HostCMS и вносить изменения в стандартное поведение контроллеров и моделей.
Добавление обработчика события
Добавление обработчика осуществляется методом Core_Event::attach($eventName, $function), первым аргументом передается название события, вторым - название функции. Если вызываете статичный метод класса, то $function задается в виде массива array('имя-класса', 'статичный-метод').
Методом attach обработчики события добавляются одним за одним и будут вызваны в порядке добавления. Добавить обработчик события в начале очереди можно через Core_Event::attachFirst($eventName, $function).
Дополнительно третьим параметром вы можете передавать в обработчик собственные данные, например, Core_Event::attach($eventName, $function, array('foo' => 'bar')) и получать их в обработчике третьим параметром.
Удаление обработчика события
Удаление обработчика осуществляется методом Core_Event::detach($eventName, $function), первым аргументом передается название события, вторым - название функции. Если вызываете статичный метод класса, то $function задается в виде массива array('имя-класса', 'статичный-метод').
Временное отключение хуков
Методы Core_Event::off($eventName) или Core_Event::disable($eventName) позволяют временно отключить выполнение хуков с заданных названием (например, для избежания рекурсии). Обратное включение выполнения хуков осуществляется методами Core_Event::on() или Core_Event::enable().
Примеры использования хуков
Манипулирование XML
Добавление своего тега в XML всех объектов Shop_Item
1. Пишем класс наблюдателя, размещаем его в modules/shop/item/observer.php
class Shop_Item_Observer { static public function onBeforeGetXml($object, $args, $options) { // Добавить новый тег, равный price * 2 $object->addXmlTag('myTag', $object->price * 2); } }
2. Добавляем наблюдателя в bootstrap.php
// Add shop_item observer Core_Event::attach('shop_item.onBeforeGetXml', array('Shop_Item_Observer', 'onBeforeGetXml'));
Ограничение выводимых тегов инфоэлемента для значений дополнительного свойства типа инфосистема
class Property_Value_Int_Observer { static public $aForbiddenTags = array('shortcut_id', 'shop_tax_id', 'shop_id', 'active', 'sorting', 'path', 'indexing', 'image_small_height', 'image_small_width', 'user_id', 'guid', 'siteuser_group_id', 'siteuser_id', 'shop_seller_id', 'shop_currency_id', 'shop_group_id', 'shop_producer_id', 'shop_producer','shop_measure_id', 'vendorcode', 'description', 'weight', 'seo_title', 'seo_description', 'seo_keywords', 'image_large_height', 'image_large_width', 'yandex_market', 'rambler_pokupki', 'yandex_market_bid', 'yandex_market_cid', 'yandex_market_sales_notes', 'showed', 'deleted', 'date', 'rest'); static public function onBeforeGetXml($object, $args) { $oProperty = $object->Property; // Property type is informationsystem if ($oProperty->type == 5 && $object->value != 0 && Core::moduleIsActive('informationsystem')) { $oInformationsystem_Item = $object->Informationsystem_Item; foreach (self::$aForbiddenTags as $sForbiddenTag) { $oInformationsystem_Item->addForbiddenTag($sForbiddenTag); } } } } Core_Event::attach('property_value_int.onBeforeGetXml', array('Property_Value_Int_Observer', 'onBeforeGetXml'));
Добавление в структуре сайта к пункту меню с кодом 123 логина текущего пользователя
class Structure_Observer
{
static public function onBeforeGetXml($object, $args)
{
if ($object->id == 123 && Core::moduleIsActive('siteuser'))
{
$oSiteuser = Core_Entity::factory('Siteuser')->getCurrent();
if ($oSiteuser)
{
$object
// Запрещаем вывод в XML стандартного значения тега name
->addForbiddenTag('name')
// Добавляем свое значение
->addXmlTag('name', $object->name . ' ' . htmlspecialchars($oSiteuser->login));
}
}
}
}
// Add structure observer
Core_Event::attach('structure.onBeforeGetXml', array('Structure_Observer', 'onBeforeGetXml'));
ORM
Добавление обработчиков несуществующих методов модели
Событие формируется по схеме имя_модели.onCallимя_метода, например для метода xxx() необходимо событие "shop_item.onCallxxx".
Может использоваться для добавления функции обратного вызова для вывода полей в центре администрирования или других целей.
Пример добавления вывода названия производителя в списке товаров.
1. Создаем поле формы центра администрирования для источника 1, указываем имя producer и тип "Функция обратного вызова".
2. В bootstrap.php
добавляем обработчик метода Core_Event::attach('shop_item.onCallxxx', array('Shop_Item_Observer', 'onCallxxx'));
3. Обработчик
class Shop_Item_Observer { static public function onCallxxx($object, $args) { return $object->Shop_Producer->name; } }
Сохранение и чтение несуществующих свойств модели
Событие формируется по схеме имя_модели.onCallимя_свойства, например для несуществующего свойства login необходимо событие "document.onCalllogin".
В bootstrap.php
добавляем обработчик метода Core_Event::attach('document.onCalllogin', array('My_Observer', 'onCalllogin'));
Обработчик
class My_Observer { /** * Сохраненные значения отсутствующего свойства */ static protected $_values = array(); static public function onCalllogin($object, $args) { $pkv = $object->getPrimaryKey(); $modelName = $object->getModelName(); // Write value if (count($args)) { self::$_values[$modelName][$pkv] = $args[0]; } // Read value elseif (isset(self::$_values[$modelName][$pkv])) { return self::$_values[$modelName][$pkv]; } } }
Центр администрирования
Скрытие полей форм центра администрирования (для контроллеров с определенным методом _prepareForm())
Генерация стандартной формы редактирования осуществляется методом _prepareForm()
контроллера Admin_Form_Action_Controller_Type_Edit
. Далее в методе класса потомка, например Shop_Controller_Edit
, осуществляется перемещение полей по вкладкам и для корректировки уже этого перемещения необходимо использовать событие Admin_Form_Action_Controller_Type_Edit.onAfterRedeclaredPrepareForm
.
Порядок действий:
1. В файле bootstrap.php навешиваем нового наблюдателя на событие:
Core_Event::attach('Admin_Form_Action_Controller_Type_Edit.onAfterRedeclaredPrepareForm', array('Shop_Controller_Edit_Observer', 'onAfterRedeclaredPrepareForm'));
2. Размещаем файл observer.php в директории modules/shop/controller/edit/
Имя файла и директория зависит от имени самого наблюдателя!
3. В этом файле указываем следующий код, который удаляет поле "Описание" для формы редактирования интернет-магазина:
<?php defined('HOSTCMS') || exit('HostCMS: access denied.'); class Shop_Controller_Edit_Observer
{
static public function onAfterRedeclaredPrepareForm($controller, $args)
{
list($object, $Admin_Form_Controller) = $args;
// Данное событие будет вызываться для всех форм, определяем с каким контроллером работаем
switch (get_class($controller))
{
case 'Shop_Controller_Edit':
$oMainTab = $controller->getTab('main');
$oMainTab->delete($controller->getField('description'));
break;
}
}
}
Скрытие полей форм центра администрирования (для контроллеров без метода _prepareForm())
Порядок действий:
1. В файле bootstrap.php навешиваем нового наблюдателя на событие:
Core_Event::attach('Admin_Form_Action_Controller_Type_Edit.onBeforeExecute', array('Constant_Controller_Edit_Observer', 'onBeforeExecute'));
2. Размещаем файл observer.php в директории modules/constant/controller/edit/
Имя файла и директория зависит от имени самого наблюдателя!
3. В этом файле указываем следующий код, который удаляет поле description для формы редактирования константы:
class Constant_Controller_Edit_Observer
{
static public function onBeforeExecute($controller, $args)
{
list($operation, $Admin_Form_Controller) = $args;
if (is_null($operation))
{
// Данное событие будет вызываться для всех форм, определяем с каким контроллером работаем
switch (get_class($controller))
{
case 'Constant_Controller_Edit':
$oMainTab = $controller->getTab('main');
$oMainTab->delete($controller->getField('description'));
break;
}
}
}
}
Отключение визуального редактора для товаров, информационных элементов и групп определенных магазинов и информационных систем
Core_Event::attach('Admin_Form_Action_Controller_Type_Edit.onAfterRedeclaredPrepareForm', array('My_Observer', 'onAfterRedeclaredPrepareForm')); class My_Observer { static public function onAfterRedeclaredPrepareForm($controller, $args) { list($object, $Admin_Form_Controller) = $args; // Данное событие будет вызываться для всех форм, определяем с каким контроллером работаем switch (get_class($controller)) { case 'Shop_Item_Controller_Edit': case 'Informationsystem_Item_Controller_Edit': $oMainTab = $controller->getTab('main'); $modelName = $object->getModelName(); // Отключаем для магазина с номером 1 и инфосистем с номерами 5 и 7 if ($modelName == 'shop_item' && in_array($object->shop_id, array(1)) || $modelName == 'informationsystem_item' && in_array($object->informationsystem_id, array(5, 7))) { $controller->getField('description')->wysiwyg(FALSE); $controller->getField('text')->wysiwyg(FALSE); } break; } } }
Добавление дополнительного пункта меню в выпадающее меню центра администрирования
class Observer_Admin_Form_Controller { public static function onBeforeAddEntity($object, $args) { // Форма списка товаров и групп магазина if ($object->getAdminForm()->id == 65) { if (strpos(get_class($args[0]), 'Admin_Form_Entity_Menus') !== FALSE) { $aChildren = $args[0]->getChildren(); // Добавляем потомка первому элементу меню $aChildren[0]->add( Admin_Form_Entity::factory('Menu') ->name('Мой пункт меню') ->img('/admin/images/page_add.gif') ->href('ссылка') ->onclick('onclick') ); } } } } Core_Event::attach('Admin_Form_Controller.onBeforeAddEntity', array('Observer_Admin_Form_Controller', 'onBeforeAddEntity'));
Добавление полей из других таблиц в формы центра администрирования
Форма содержит данные из одного или нескольких Dataset, добавляемых в файле центра администрирования, например, /admin/document/index.php
// Добавляем источник данных контроллеру формы // Источник данных 1 $oAdmin_Form_Dataset = new Admin_Form_Dataset_Entity( Core_Entity::factory('Document') ); // Ограничение источника 1 по родительской группе $oAdmin_Form_Dataset->addCondition( array('where' => array('document_dir_id', '=', $document_dir_id) ) )->addCondition( array('where' => array('site_id', '=', CURRENT_SITE) ) ); // Добавляем источник данных контроллеру формы $oAdmin_Form_Controller->addDataset( $oAdmin_Form_Dataset ); // Показ формы $oAdmin_Form_Controller->execute();
У метода addDataset()
есть событие:
Core_Event::notify('Admin_Form_Controller.onBeforeAddDataset', $this, array($oAdmin_Form_Dataset));
позволяющее навесить обработчик, в котором можно определить, что это за форма, получив у датасета объект через getEntity() и определив его класс через get_class():
Core_Event::attach('Admin_Form_Controller.onBeforeAddDataset', array('My_Observer', 'onBeforeAddDataset')); class My_Observer { static public function onBeforeAddDataset($object, $args) { list($oAdmin_Form_Dataset) = $args; $oEntity = $oAdmin_Form_Dataset->getEntity(); switch (get_class($oEntity)) { case 'Document_Model': // do smth break; } } }
После прохождения проверки вы можете добавить новые объединения с другими таблицами. Не забудьте, что для выбираемых значений из объединяемых таблиц должны быть соответствующие поля в моделях, иначе будет ошибка в конструкторе (см. "Сохранение и чтение несуществующих свойств модели". Объединим документы с таблицей пользователей и выведим логин пользователя:
class My_Observer { static public function onBeforeAddDataset($object, $args) { list($oAdmin_Form_Dataset) = $args; $oEntity = $oAdmin_Form_Dataset->getEntity(); switch (get_class($oEntity)) { case 'Document_Model': $oAdmin_Form_Dataset->addCondition( array('select' => array('documents.*', 'users.login') ) )->addCondition( array('join' => array('users', 'documents.user_id', '=', 'users.id') ) ); break; } } }
Особое внимание обратите на необходимость указания в select списка выбираемых полей, в нашем случае все поля из documents и только поле login из users.
Пример вывода значения дополнительного свойства списочного типа в таблице товаров
В форму центра администрирования добавляется поле текстового типа с названием dataListValue, тип фильтрации устанавливается HAVING.
class My_Observer { static public function onBeforeAddDataset($object, $args) { list($oAdmin_Form_Dataset) = $args; $oEntity = $oAdmin_Form_Dataset->getEntity(); switch (get_class($oEntity)) { case 'Shop_Group_Model': // Заглушка для групп, чтобы было поле zzz для HAVING-а $oAdmin_Form_Dataset->addCondition( array('select' => array(array(Core_QueryBuilder::expression('\' \''), 'dataListValue')) ) ); break; case 'Shop_Item_Model': $oAdmin_Form_Dataset->addCondition( array('select' => array(array('list_items.value', 'dataListValue')) ) ) ->addCondition( array('leftJoin' => array('property_value_ints', 'shop_items.id', '=', 'property_value_ints.entity_id', array( array('AND' => array('property_value_ints.property_id', '=', 19)) ) ) ) ) ->addCondition( array('leftJoin' => array('list_items', 'property_value_ints.value', '=', 'list_items.id') ) ); break; } } }
Core_Event::attach('Admin_Form_Controller.onBeforeAddDataset', array('My_Observer', 'onBeforeAddDataset'));
Вывод свойства заказа в список заказов
Для вывода значения свойства необходимо добавить в dataset объединение с нужной таблицей, для этого будем использовать событие Admin_Form_Controller.onBeforeAddDataset. Так как предполагается кроме вывода значения свойства еще и его изменение, то вместо использования data-полей, достаточных для вывода информации, мы будем использовать событие onCallxxx у модели shop_order.
Создайте поле PropertyListField, укажите списочный тип и фильтрацию через HAVING.
class Shop_Order_Form_Observer { /** * Идентификатор выводимого свойства заказа */ static protected $_propertyId = 139; static public function onBeforeAddDataset($object, $args) { list($oAdmin_Form_Dataset) = $args; $oEntity = $oAdmin_Form_Dataset->getEntity(); if (get_class($oEntity) == 'Shop_Order_Model' && Core::moduleIsActive('list')) { $oAdmin_Form_Dataset ->addCondition( array('select' => array('shop_orders.*', array('property_value_ints.value', 'PropertyListField'))) ) ->addCondition( array('leftJoin' => array('property_value_ints', 'shop_orders.id', '=', 'property_value_ints.entity_id', array( array('AND' => array('property_value_ints.property_id', '=', self::$_propertyId)) ) ) ) ); $oProperty = Core_Entity::factory('Property', self::$_propertyId); if ($oProperty->type == 3 && $oProperty->list_id != 0) { $oListItems = $oProperty->List->List_Items; $oListItems->queryBuilder() ->where('active', '=', 1) ->orderBy('sorting', 'ASC'); $aListItems = $oListItems->findAll(); $aList = array(); $aList[0] = "—"; foreach ($aListItems as $oListItem) { $aList[$oListItem->id] = $oListItem->value; } $oAdmin_Form_Dataset->changeField('PropertyListField', 'list', $aList); } } } static public function onCallPropertyListField($object, $args) { $entityId = $object->getPrimaryKey(); $read = count($args) == 0; $oProperty = Core_Entity::factory('Property', self::$_propertyId); $aProperty_Values = $oProperty->getValues($entityId, FALSE); $oProperty_Value = Core_Array::get($aProperty_Values, 0); // Read value if ($read) { return empty($oProperty_Value) ? 0 : $oProperty_Value->value; } // Write value if (is_null($oProperty_Value)) { $oProperty_Value = $oProperty->createNewValue($entityId); } $oProperty_Value->value = $args[0]; $oProperty_Value->save(); } } Core_Event::attach('Admin_Form_Controller.onBeforeAddDataset', array('Shop_Order_Form_Observer', 'onBeforeAddDataset')); Core_Event::attach('shop_order.onCallPropertyListField', array('Shop_Order_Form_Observer', 'onCallPropertyListField'));
Обработка действий формы центра администрирования
Добавление собственных действий в стандартные формы центра администрирования требует возможности их обработки, которая возможна через действие Admin_Form_Controller.onCallxxx
, где xxx - название действия.
Пример обработки действия markDeleted формы списка товаров.
class Admin_Form_Controller_Observer
{
static public function onCallmarkDeleted($oAdmin_Form_Controller, $args)
{
list($datasetKey, $oObject, $operation) = $args;
$oAdmin_Form = $oAdmin_Form_Controller->getAdminForm();
// Проверка на GUID нужной формы
if ($oAdmin_Form->guid == '5AC4B52F-EB2E-1978-5232-8C6FDD8625A0')
{
// do smth
$return = TRUE;
$message = Core_Message::get('Сообщение');
$content = 'Текст формы, если нужно вывести только сообщение, то установить в NULL, а $return в FALSE';
return array($return, $message, $content);
}
}
}
Core_Event::attach('Admin_Form_Controller.onCallmarkDeleted', array('Admin_Form_Controller_Observer', 'onCallmarkDeleted'));
Изменение отображения фильтра для поля центра администрирования
В событие Admin_Form_Controller.onBeforeExecute добавляется callback-функция, которая отобразит фильтр:
class Admin_Form_Controller_Observer { static public function onBeforeExecute($oAdmin_Form_Controller, $args) { $oAdmin_Form = $oAdmin_Form_Controller->getAdminForm(); // Проверка на GUID нужной формы if ($oAdmin_Form->guid == '5AC4B52F-EB2E-1978-5232-8C6FDD8625A0') { // zzz - имя поля $oAdmin_Form_Controller->addFilter('zzz', array('Admin_Form_Controller_Observer', 'zzzFilter')); } } /** * Backend function * @param mixed $value value * @param Admin_Form_Field $oAdmin_Form_Field * @return string */ static public function zzzFilter($value, $oAdmin_Form_Field, $filterPrefix, $tabName) { ob_start(); Core::factory('Core_Html_Entity_Select') ->options( array( 0 => 'AAA', 1 => 'BBB', 2 => 'CCC' ) ) ->value(intval($value)) ->name($filterPrefix . $oAdmin_Form_Field->id) ->execute(); return ob_get_clean(); } } Core_Event::attach('Admin_Form_Controller.onBeforeExecute', array('Admin_Form_Controller_Observer', 'onBeforeExecute'));
Интернет-магазин
Ограничение выборки модификаций для товара
Выбираем модификации с остатком на складе больше 0.
static public function onBeforeShowXmlModifications($object, $args) { $args[0]->queryBuilder() ->select('shop_items.*') ->leftJoin('shop_warehouse_items', 'shop_warehouse_items.shop_item_id', '=', 'shop_items.id') ->groupBy('shop_items.id') ->having(Core_QueryBuilder::expression('SUM(shop_warehouse_items.count)'), '>', 0); }
Добавление цен в других валютах в карточке товара
Обработчик размещаем в коде типовой динамчиеской страницы.
// Если идет показ товара if ($Shop_Controller_Show->item) { class Shop_Item_Observer_Prices { static public function onBeforeGetXml($object, $args) { $oPrices = Core::factory('Core_Xml_Entity') ->name('prices'); $oShop = $object->Shop; $aShop_Currencies = Core_Entity::factory('Shop_Currency')->findAll();
// Prices
$oShop_Item_Controller = new Shop_Item_Controller();
if (Core::moduleIsActive('siteuser'))
{
$oSiteuser = Core_Entity::factory('Siteuser')->getCurrent();
$oSiteuser && $oShop_Item_Controller->siteuser($oSiteuser);
}
//$oShop_Item_Controller->count($this->_cartQuantity);
//$aPrices = $oShop_Item_Controller->getPrices($object);
$aPrices = $oShop_Item_Controller->calculatePriceInItemCurrency($object->price, $object);
foreach ($aShop_Currencies as $oShop_Currency) { $fCurrencyCoefficient = $object->Shop_Currency->id > 0 && $oShop_Currency->id > 0 ? Shop_Controller::instance()->getCurrencyCoefficientInShopCurrency( $object->Shop_Currency, $oShop_Currency ) : 0; $oPrices->addEntity( Core::factory('Core_Xml_Entity') ->name('price') ->addEntity( Core::factory('Core_Xml_Entity') ->name('value') ->value( Shop_Controller::instance()->round($aPrices['price_discount'] * $fCurrencyCoefficient) ) ) ->addEntity( $oShop_Currency ) ); } $object->addEntity($oPrices); } } Core_Event::attach('shop_item.onBeforeGetXml', array('Shop_Item_Observer_Prices', 'onBeforeGetXml')); }
Вывод в XML товара цен для групп пользователей
class Shop_Item_Observer { static public function onBeforeGetXml($object, $args) { $aShop_Item_Prices = $object->Shop_Item_Prices->findAll(); $object->addEntities( $aShop_Item_Prices ); } } Core_Event::attach('shop_item.onBeforeGetXml', array('Shop_Item_Observer', 'onBeforeGetXml'));
Включение вывода сопутствующих товаров для модификаций
По умолчанию для модификаций вывод в XML сопутствующих товаров отключен, вы можете включить его через событие shop_item.onBeforeAddModification:
class Shop_Item_Observer { static public function onBeforeAddModification($object, $args) { $oModification = $args[0]; $oModification->showXmlAssociatedItems(TRUE); } } Core_Event::attach('shop_item.onBeforeAddModification', array('Shop_Item_Observer', 'onBeforeAddModification'));
Вывод сообщения при создании товара с существующим артикулом
1. Класс наблюдателя, размещаем его в modules/shop/item/marking.php
class Shop_Item_Marking { static public function onBeforeCreate($object, $args) { $oSetObject = $object->Shop->Shop_Items->getByMarking($object->marking); if(!is_null($oSetObject)) { Core_Message::show('Товар с таким артикулом существует', 'error'); } } }
2. Добавляем наблюдателя в bootstrap.php
Core_Event::attach('shop_item.onBeforeCreate', array('Shop_Item_Marking', 'onBeforeCreate'));
Запрет создания товара с существующим артикулом
1. Класс наблюдателя, размещаем его в modules/shop/item/marking.php
class Shop_Item_Marking { static public function onBeforeExecute($controller, $args) { list($operation, $Admin_Form_Controller) = $args; if (!is_null($operation)) { // Данное событие будет вызываться для всех форм, определяем с каким контроллером работаем switch (get_class($controller)) { case 'Shop_Item_Controller_Edit': $object = $controller->getObject(); $marking = Core_Array::getPost('marking'); $oSetObject = $object->Shop->Shop_Items->getByMarking($marking); $modelName = $object->getModelName(); if ($modelName == 'shop_item' && !is_null($oSetObject) && $oSetObject->id != $object->id) { Core_Message::show('Товар с таким артикулом существует', 'error'); return TRUE; } break; } } } }
2. Добавляем наблюдателя в bootstrap.php
Core_Event::attach('Admin_Form_Action_Controller_Type_Edit.onBeforeExecute', array('Shop_Item_Marking', 'onBeforeExecute'));
Отключение применения скидок для цен пользователей из определенной группы
// Отключение скидки для оптовых цен class Shop_Discount_Observer { static public function onAfterCalculatePrice($object, $args) { // ID группы пользователей сайта, для которых не нужно применять скидки $iSiteuserGroupId = 8; $oSiteuser = Core_Entity::factory('Siteuser')->getCurrent(); // Пользователь авторизован if ($oSiteuser) { $aSiteuser_Groups = $oSiteuser->Siteuser_Groups->findAll(); foreach ($aSiteuser_Groups as $oSiteuser_Group) { if ($oSiteuser_Group->id == $iSiteuserGroupId) { $aPrice = $object->getAPrice(); if (isset($aPrice['discount']) && $aPrice['discount'] > 0) { $aPrice['price_discount'] += $aPrice['discount']; $aPrice['discount'] = 0; $object->setAPrice($aPrice); } break; } } } } } Core_Event::attach('Shop_Item_Controller.onAfterCalculatePrice', array('Shop_Discount_Observer', 'onAfterCalculatePrice'));
Округление цен в интернет-магазине
см. страницу.
Отправка письма пользователю и администратору при смене статуса заказа
Решение не зависит от используемой платежной системы. В данном случае можно обойтись и без использования хука, добавив дополнительные проверки в обработчики платежных систем (метод changedOrder($mode)). Вариант с использованием хука:
class Shop_Order_Status_Observer
{
static public function onBeforeChangedOrder($object, $args)
{
$mode = $args[0];
$oShop = $object->getShopOrder()->Shop;
if (in_array($mode, array('changeStatusPaid', 'edit', 'apply')))
{
// Изменился статус заказа
if ($object->getShopOrderBeforeAction()->shop_order_status_id != $object->getShopOrder()->shop_order_status_id)
{
$date_str = Core_Date::sql2datetime($object->getShopOrder()->datetime);
// Тема письма администратору
$object->adminMailSubject(
sprintf($oShop->confirm_admin_subject, $object->getShopOrder()->invoice, $oShop->name, $date_str)
);
// Тема письма пользователю
$object->siteuserMailSubject(
sprintf($oShop->confirm_user_subject, $object->getShopOrder()->invoice, $oShop->name, $date_str)
);
// Установка XSL-шаблонов в соответствии с настройками в узле структуры
$object->setXSLs();
// Отправка писем клиенту и пользователю
$object->send();
}
}
}
}
// Add observer
Core_Event::attach('Shop_Payment_System_Handler.onBeforeChangedOrder', array('Shop_Order_Status_Observer', 'onBeforeChangedOrder'));
Ограничение скидок от суммы заказа, применение только наибольшей расчитанной скидки
В корзине система управления для заказа рассчитывает различные скидки от суммы заказа, в том числе накопительные и скидку по купону. Соответственно в корзине к заказу могут применяться несколько скидок. С помощью события Shop_Purchase_Discount_Controller.onAfterGetDiscounts
мы можем ограничить скидки только наибольшей из рассчитанных.
class Shop_Purchase_Discount_Controller_Observer
{
static public function onAfterGetDiscounts($object, $args)
{
// Массив рассчитанных скидок
$aDiscounts = $object->getReturn();
if (count($aDiscounts))
{
// Сумма предыдущей скидки
$fDiscountAmount = 0;
// Возвращаемая скидка
$oReturnDiscount = NULL;
foreach ($aDiscounts as $oShop_Purchase_Discount)
{
// Сумма текущей скидки больше ранее сохраненной
if ($oShop_Purchase_Discount->getDiscountAmount() > $fDiscountAmount)
{
$oReturnDiscount = $oShop_Purchase_Discount;
$fDiscountAmount = $oShop_Purchase_Discount->getDiscountAmount();
}
}
// Заменяем возвращаемый массив скидок на массив с одной (максимальной) скидкой
!is_null($oReturnDiscount) && $object->setReturn(array($oReturnDiscount));
}
}
}
Core_Event::attach('Shop_Purchase_Discount_Controller.onAfterGetDiscounts', array('Shop_Purchase_Discount_Controller_Observer', 'onAfterGetDiscounts'));
Отключение товара при обмене с 1С, у которого остаток равен 0
class Shop_Item_Import_Cml_Controller_Observer
{
static public function onAfterOffersShopItem($object, $args)
{
$oShop_Item = $args[0];
if ($oShop_Item->getRest() == 0)
{
// Отключаем товар
$oShop_Item->active = 0;
$oShop_Item->save();
}
}
}
Core_Event::attach('Shop_Item_Import_Cml_Controller.onAfterOffersShopItem', array('Shop_Item_Import_Cml_Controller_Observer', 'onAfterOffersShopItem'));
Добавления блока <isbn> при экспорте в Яндекс.Маркет. Значение берется из дополнительного свойства товара (с названием "ISBN" тега XML)
Вносим наблюдается в код типовой дин. страницы экспорта в Яндекс.Маркет.
class Shop_Controller_YandexMarket_Observer { static public function onAfterOffer($object, $args) { $oShop_Item = $args[0]; $linkedObject = Core_Entity::factory('Shop_Item_Property_List', $oShop_Item->shop_id); $oProperty = $linkedObject->Properties->getByTag_name('ISBN'); if ($oProperty) { $aPropertyValues = $oProperty->getValues($oShop_Item->id); if (isset($aPropertyValues[0])) { $object->write('<isbn>' . Core_Str::xml(trim($aPropertyValues[0]->value)) . '</isbn>'. "\n"); } } } } Core_Event::attach('Shop_Controller_YandexMarket.onAfterOffer', array('Shop_Controller_YandexMarket_Observer', 'onAfterOffer'));
Вставка собственного поля в экспорт CSV магазина на примере вставки названия родительского товара модификации
class Shop_Item_Export_Csv_Observer { // Позиция, в которой делать вставку в массив базовых значений товара static protected $_position = 4; static public function onGetItemTitles($object, $args) { $return = $args[0]; $return = array_merge( array_slice($return, 0, self::$_position), array('Название родительского товара для модификации'), array_slice($return, self::$_position) ); return $return; } static public function onAfterItemBasicData($object, $args) { $return = $args[0]; $oShopItem = $args[1]; // Формируем значение поля $value = $oShopItem->modification_id ? $object->prepareString($oShopItem->Modification->name) : ''; $return = array_merge( array_slice($return, 0, self::$_position), array($value), array_slice($return, self::$_position) ); return $return; } } Core_Event::attach('Shop_Item_Export_Csv_Controller.onGetItemTitles', array('Shop_Item_Export_Csv_Observer', 'onGetItemTitles')); Core_Event::attach('Shop_Item_Export_Csv_Controller.onAfterItemBasicData', array('Shop_Item_Export_Csv_Observer', 'onAfterItemBasicData'));
Удаление товаров после авторизации из корзины пользователя, оставшихся после предыдущего сеанса
class Shop_Cart_Clear
{
static public function onBeforeMoveTemporaryCart($controller, $args)
{
if (Core::moduleIsActive('siteuser'))
{
$oSiteuser = Core_Entity::factory('Siteuser')->getCurrent();
if ($oSiteuser)
{
$aShop_Carts = Core_Entity::factory('Shop_Cart')->getAllBySiteuser_id($oSiteuser->id, FALSE);
foreach ($aShop_Carts as $oShop_Cart)
{
$oShop_Cart->delete();
}
}
}
}
}
Core_Event::attach('Shop_Cart_Controller.onBeforeMoveTemporaryCart', array('Shop_Cart_Clear', 'onBeforeMoveTemporaryCart'));
Сортировка товаров по названию в корзине и заказе
Для сортировки будем использовать собственную функцию _cmp, сравнивающую названия товаров, код вносим в bootstrap.php.
class Shop_Cart_Controller_Observer { static public function onAfterGetAll($object, $args, $options) { if (count($args[0])) { usort($args[0], 'self::_cmp'); return $args[0]; } } static protected function _cmp($a, $b) { return strcmp($a->Shop_Item->name, $b->Shop_Item->name); } } Core_Event::attach('Shop_Cart_Controller.onAfterGetAll', array('Shop_Cart_Controller_Observer', 'onAfterGetAll'));
HelpDesk
Оповещение о новых запросах (сообщения), поступивших в Helpdesk через почту или с сайта
В bootstrap.php
добавляем:
/** * Helpdesk Message observer * * @package HostCMS 6 * @version 6.x * @author Hostmake LLC * @copyright © 2005-2012 ООО "Хостмэйк" (Hostmake LLC), https://www.hostcms.ru */ class Helpdesk_Message_Observer { static public function onAfterCreate($object, $args) { if ($object->inbox) { // Отправка письма $message = "Доброе время суток, уважаемый куратор!\n\nВ службу поддержки поступило новое сообщение:\n"; $message .= "Тема: " . ($object->subject ? $object->subject : '<Без темы>') . "\n"; $message .= "Сообщение: " . $object->message . "\n"; $message .= "E-mail: " . $object->Helpdesk_Ticket->email . "\n"; $message .= "Дата: " . Core_Date::sql2datetime($object->datetime) . "\n"; $oCore_Mail_Driver = Core_Mail::instance() ->to(EMAIL_TO) ->from(EMAIL_TO) ->subject('HostCMS Helpdesk: Новое сообщение # ' . $object->id) ->message($message) ->contentType('text/plain') ->send(); } } } // Add observer Core_Event::attach('helpdesk_message.onAfterCreate', array('Helpdesk_Message_Observer', 'onAfterCreate'));
Поисковая система
Ограничение индексации товара только по названию
class Shop_Item_Observer { static public function onAfterIndexing($object, $args) { $oSearch_Page = $args[0]; $oSearch_Page->text = $object->name; } } Core_Event::attach('shop_item.onAfterIndexing', array('Shop_Item_Observer', 'onAfterIndexing'));
Документы
Вывода названия документа перед показом версии документа
class Document_Version_Observer
{
static public function onBeforeExecute($object, $args)
{
?><h1><?php echo htmlspecialchars($object->Document->name)?></h1><?php
}
}
// Add document_version observer
Core_Event::attach('document_version.onBeforeExecute', array('Document_Version_Observer', 'onBeforeExecute'));
Замена подстроки при показе статичной страницы
class Document_Version_Observer
{
static public function onBeforeExecute($object, $args)
{
$content = $object->getContent();
$content = str_replace('%%%', DISCONT, $content);
$object->setContent($content);
}
}
// Add document_version observer
Core_Event::attach('document_version.onBeforeExecute', array('Document_Version_Observer', 'onBeforeExecute'));
Структура сайта
Смена файла, подключаемого контроллером для узла структуры сайта (стандартно подключается документ, ТДС или дин. страница)
В обработчике вы можете подменять объект на любой объект, который имеет метод execute()
с выполнением нужного подключения файлов.
class Structure_Observer { static public function onAfterGetRelatedObjectByType($object, $args) { if ($object->id == 1) { $args[0] = Core_Entity::factory('Document_Version', 5); } } } Core_Event::attach('structure.onAfterGetRelatedObjectByType', array('Structure_Observer', 'onAfterGetRelatedObjectByType'));
Почтовые рассылки
Дополнительные подстановки в выпуск почтовой рассылки
Для внесения дополнительных изменений или подстановок в выпуск рассылки добавлены события maillist_fascicle.onBeforeApplyFascicleTemplate
, maillist_fascicle.onAfterApplyFascicleTemplate
class Maillist_Fascicle_Observer { static public function onAfterApplyFascicleTemplate($object, $args) { $oSiteuser = $args[1]; $args[0] = str_replace('{MY_TEXT}', $myValue, $args[0]); } } Core_Event::attach('maillist_fascicle.onAfterApplyFascicleTemplate', array('Maillist_Fascicle_Observer', 'onAfterApplyFascicleTemplate'));
RSS
Добавление в RSS блока <enclosure type="video/x-flv" />
Тег добавляется для информационного элемента, у которого заполнено дополнительное свойство 15. Используется при экспорте в Яндекс.Новости.
class Rss_Observer
{
static public function onBeforeAddItem($object, $args)
{
list($oInformationsystem_Item, $currentItem) = $args;
$oProperty = Core_Entity::factory('Property', 15);
$aProperty_Values = $oProperty->getValues($oInformationsystem_Item->id);
if (count($aProperty_Values))
{
$oProperty_Value = $aProperty_Values[0];
if (strlen($oProperty_Value->value))
{
$currentItem[] = array(
'name' => 'enclosure',
'value' => NULL,
'attributes' => array(
'url' => $currentItem['link'],
'type' => 'video/x-flv'
)
);
$object->setCurrentItem($currentItem);
}
}
}
}
Core_Event::attach('Informationsystem_Controller_Rss_Show.onBeforeAddItem', array('Rss_Observer', 'onBeforeAddItem'));
История изменений
Версия 7.0.7
Добавлены методы Core_Event::off()/Core_Event::disable() и Core_Event::on()/Core_Event::enable().
Версия 6.9.6
Добавлена передача третьего параметра с аргументами для Core_Event::attach($eventName, $function, array('foo' => 'bar')).
Версия 6.9.5
Добавлен метод Core_Event::attachFirst($eventName, $function).
Комментарии
-
Без темы
А где взять список всех возможных событий?
15.06.2016 22:42:05Без темы
в API для методов, имеющих события, указывается блок Hostcms-event со списком событий.
Без темы
Тоже задавал этот вопрос. Вот что поддержка ответила. Может кому то надо: - "открываете файл в который хотите внедрится и ищете по event"