1. Как работают триггерные рассылки.
2. Что делают триггеры.
3. Как сделать свой триггер.
3.1. Простейший триггер
3.2. Получение данных события
3.3. Фильтрация в триггере
3.4. Свои поля персонализации
3.5. Фильтрация по сайту
4. Триггер для ненаступивших событий
5. Делаем готовую рассылку
6. Итоги.
[spoiler]
Все примеры в статье
Распакуйте его, затем папку sender скопируйте в /bitrix/php_interface/
А затем в init.php подключите пример:
include_once($_SERVER['DOCUMENT_ROOT'] . "/bitrix/php_interface/sender/handlers.php"); |
1. Как работают триггерные рассылки.
При наступлении события срабатывает триггер. Он определяет, отправлять ли рассылку и кому именно отправлять.
Затем, для каждого адреса рассылки перед запуском проверяется:
- не отписан ли адрес от рассылки;
- двойной запуск; не запущена ли рассылка для одного адреса параллельно.
К примеру, рассылка запускается по событию оплачен заказ.
Если в рассылке указаны три письма, уходящие с промежутком 1 час, то если один и тот же человек оплатит два заказа с разницей в 10 минут,
то по первому заказу рассылка запустится, вторая - не запустится.
Рассылка отмечается завершенной для адреса после:
- или отправки всех писем;
- или после выполнения целевого действия получателем.
После завершения для конкретного адреса рассылка может запуститься еще раз.
Один и тот же триггер можно использовать в нескольких рассылках.
И может запуститься для всех них.
2. Что делают триггеры.
Триггеры - это
Поэтому, все доступные данные в обычных обработчиках события доступны и в триггере.
Они находят адреса получателей рассылки.
К примеру, из данных события: событие добавления комментария в блог возвращает AUTHOR_ID - код пользователя-автора комментария.
В триггере можно по этому коду выбрать поля пользователя - имя и емайл.
Их и вернуть в качестве данных получателя.
Они разрешают или запрещают запуск рассылки для конкретного адреса.
Запуск рассылки можно запрограммировать безусловным, либо в зависимости от данных события, либо вашей бизнес-логики.
К примеру, проверять:
- на каком сайте произошло событие;
- в админке или публичке;
- время днем или ночью.
3. Как сделать свой триггер.
3.1. Простейший триггер
Для начала выберем событие, к примеру в блоге добавление комментария OnCommentAdd
Теперь создадим класс SenderTriggerBlogComment,
который обязательно наследуется от класса \Bitrix\Sender\Trigger
<? class SenderTriggerBlogComment extends \Bitrix\Sender\Trigger { public function getName() { return 'Добавлен комментарий'; } public function getCode() { return "my_blog_comment"; } public function getEventModuleId() { return 'blog'; } public function getEventType() { return "OnCommentAdd"; } public function getRecipient() { return array( 'NAME' => 'Иван', 'EMAIL' => 'ivan@example.com', 'USER_ID' => '3' ); } public function filter() { // безусловно разрешаем запустить рассылку return true; } public function getForm() { return ''; } } |
getName() - возвращает название триггера.
Оно выводится в списке целей и условий запуска.
getCode() - возвращает код триггера, уникальный в рамках модуля.
Желательно указывать свой префикс, чтобы коды не пересеклись.
getEventModuleId() - возвращает модуль, который генерирует событие, на которое срабоает триггер.
getEventType() - возвращает событие, на которое должен сработать триггер.
getRecipient() - возвращает массив или объект одного адреса или списка адресов.
Результат может быть в в виде массива, или \Bitrix\Main\DB\Result, или старый добрый CDBResult.
filter() - возвращает true или false.
Результат указывает запускать ли рассылку или нет.
В функции можно указывать свои условия фильтрации события, свои выборки адресов.
getForm() - возвращает форму настройки триггера.
Форма выведется на странице указания событий запуска и цели.
Теперь подключим триггер.
Для этого, сохраним класс триггера в файл /bitrix/php_interface/trigger_blog_comment.php
Далее, нам нужно повесить обработчик события запроса списка триггеров.
Для этого в /bitrix/php_interface/init.php добавим код:
AddEventHandler("sender", "OnTriggerList", array("MySenderEventHandler","onTriggerList")); class MySenderEventHandler { public static function onTriggerList($data) { require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/php_interface/trigger_blog_comment.php'); $data['TRIGGER'] = 'SenderTriggerBlogComment'; return $data; } } |
3.2. Получение данных события
Чтобы получить данные, передаваемые в событии, нужно воспользоваться методом getParam('EVENT'):
$event = $this->getParam('EVENT'); |
Она вернет массив параметров, передаваемых в функцию обработчик.
К примеру, в событии OnOrderAdd модуля sale передаются два параметра:
ID(Идентификатор заказа) и arFields(Массив полей заказа)
Тогда первый будет доступен по ключу 0, второй по ключу 1:
$event = $this->getParam('EVENT'); $ID = $event[0]; $arFields = $event[1]; |
Ну а теперь вернемся к нашему триггеру.
Как видим, в getRecipient прописан вручную адрес ivan@example.com
Давайте отправлять рассылку автору комментария.
При событии добавления комментария в функцию-обработчик передается два параметра.
Первый - код добавленного коммента.
Второй - массив, переданный в CBlogComment::Add.
Таким образом, мы в триггере можем получить код автора:
$event = $this->getParam('EVENT'); $authorId = $event[1]['AUTHOR_ID']; |
Теперь по этому коду, мы можем получить емайл и имя пользователя.
Изменим метод getRecipient:
public function getRecipient() { // получим данные из события $event = $this->getParam('EVENT'); // Во втором параметре события есть код пользователя AUTHOR_ID $authorId = $event[1]['AUTHOR_ID']; if($authorId) { // получим email и имя пользователя, оставившего комментарий $user = \Bitrix\Main\UserTable::getRowById($authorId); if($user) { // возвращаем данные адресата в виде массива return array( 'EMAIL' => $user['EMAIL'], 'NAME' => $user['NAME'], 'USER_ID' => $user['ID'], ); } } // если не нашли данные адресата, то некому и рассылать return false; } |
3.2. Форма настройки триггера
В типовом решении магазина комментарии блогов используются в качестве комментариев к товарам.
Допустим, мы хотим запускать рассылку только при добавлении комментариев в конкретном одном блоге.
Для этого сделаем форму настройки:
public function getForm() { $blogId = $this->getFieldValue('BLOG_ID'); $blogList = array(); $blogDb = CBlog::GetList( array('ID' => 'DESC'), array('ACTIVE' => 'Y'), false, false, array('NAME', 'ID') ); while($blog = $blogDb->Fetch()) { $blog['SEL ECTED'] = ($blog['ID'] == $blogId ? true : false); $blogList[] = $blog; } ob_start(); ?> <table> <tr> <td>Блог:</td> <td> <sel ect name="<?=$this->getFieldName('BLOG_ID');?>"> <option value="">(любой)</option> <?foreach($blogList as $blog):?> <option value="<?=htmlspecialcharsbx($blog['ID'])?>" <?=($blog['SEL ECTED'] ? 'sel ected' : '')?>> <?=htmlspecialcharsbx($blog['NAME'])?> </option> <?endforeach?> </select> </td> </tr> </table> <? $resultForm = ob_get_clean(); return $resultForm; } |
getFieldName() - функция-обертка для получения имени поля для возможности его сохранения.
getFieldValue() - функция возвращает текущее сохраненное значение настройке.
3.3. Фильтрация в триггере
В форме настроек триггера появился список блогов, но когда добавляем коммент в другой блог, рассылка все равно запускается.
Теперь в методе filter() сравним блог, выбранный в настройках, с блогом, в который добавлен коммент:
public function filter() { // получим код блога, в который добавлен комментарий $event = $this->getParam('EVENT'); $eventBlogId = $event[1]['BLOG_ID']; // Сравним, что комментарий добавлен в блоге, // который указан в настройках формы. Если они одинаковы, запускаем рассылку. // Если в настройках не указан блог, то запускаем рассылку. $blogId = $this->getFieldValue('BLOG_ID'); if($blogId && $blogId != $eventBlogId) { // запрещаем запускать рассылку return false; } // разрешаем запустить рассылку return true; } |
3.4. Свои теги персонализации
#EMAIL#, #NAME#, #USER_ID# - маловато полей персонализации.
Как сделать больше полей?
Давайте добавим еще три тега: код комментария, код поста, код блога.
public function getPersonalizeFields() { $eventData = $this->getParam('EVENT'); return array( 'BLOG_COMMENT_ID' => $eventData[0], 'BLOG_POST_ID' => $eventData[1]['POST_ID'], 'BLOG_ID' => $eventData[1]['BLOG_ID'], ); } |
Теперь теги доступны для использования в теле писем: #BLOG_COMMENT_ID#, #BLOG_POST_ID#, #BLOG_ID#.
Причем, доступны только в письмах тех рассылок, в которых триггер указан в качестве условия запуска.
Но чтобы эти теги показывались в списке тегов, нужно добавить их описание:
public static function getPersonalizeList() { return array( array( 'CODE' => 'BLOG_ID', // код тега, доступен как #BLOG_ID# 'NAME' => 'Код блога', // выводимое название тега 'DESC' => 'Это код блога, с которым связано событие' // описание ), array( 'CODE' => 'BLOG_POST_ID', 'NAME' => 'Код поста блога', 'DESC' => 'Это код поста блога, с которым связано событие' ), array( 'CODE' => 'BLOG_COMMENT_ID', 'NAME' => 'Код комментария', 'DESC' => 'Это код комментария, с которым связано событие' ), ); } |
В скором времени можно будет указать свои поля в результате функции getRecipient,
но сейчас это еще не доступно, как и свои поля в коннекторах адресов.
3.5. Фильтрация по сайту
При создании/редактировании рассылки обязательно задается привязка к сайту.
Вы можете получить код сайта методом getSiteId()
$siteId = $this->getSiteId(); |
И уже использовать для сравнения с кодом сайта из события, или выбрав из базы привязку к сайту,
или текущим сайтом(с константой SITE_ID).
4. Триггер для ненаступивших событий
Ну хорошо, со срабатыванием триггера по событию разобрались.
А как быть, если нет события?
К примеру, не заходил на сайт 30 дней? Или бросил корзину 7 дней назад?
Только по прошествии времени мы узнаем, что у нас какой-то период времени что-то не происходит:
нет оформления заказа, нет захода на сайт.
На этот случай у нас есть другой тип триггера.
Он запускается раз в день, в указанное время. И ему не нужно событие.
Допустим, у нас есть задача: запускать рассылку с купонами на скидку всем, кто накануне просмотрели товары.
Создадим свой триггер, отнаследовавшись уже от другого класса:
\Bitrix\Sender\TriggerConnectorClosed
и определим логику в методе filter():
<? class SenderTriggerViewedProduct extends \Bitrix\Sender\TriggerConnectorClosed { /* * @return string * * Название триггера */ public function getName() { return 'Просмотрел товары и ушел'; } /* * @return string * * Уникальный код триггера */ public function getCode() { return "my_viewed_product"; } /* * @return bool * * Может ли триггер использоваться как цель, * а не только для запуска */ public static function canBeTarget() { return false; } /* * @return bool * * Функция, которая сообщает, запускать ли рассылку для данного события. * */ public function filter() { \Bitrix\Main\Loader::includeModule('sale'); \Bitrix\Main\Loader::includeModule('catalog'); $days = $this->getFieldValue('DAYS_VIEW'); if(!is_numeric($days)) { $days = 1; } $dateFr om = new \Bitrix\Main\Type\DateTime; $dateTo = new \Bitrix\Main\Type\DateTime; $dateFr om->setTime(0, 0, 0)->add('-' . $days.' days'); $dateTo->setTime(0, 0, 0)->add('-' . ($days - 1) . ' days'); $userListDb = \Bitrix\Catalog\CatalogViewedProductTable::getList(array( 'select' => array('USER_ID' => 'FUSER.USER.ID', 'EMAIL' => 'FUSER.USER.EMAIL', 'NAME' => 'FUSER.USER.NAME'), 'filter' => array( '=SITE_ID' => $this->getSiteId(), '>DATE_VISIT' => $dateFr om, '<DATE_VISIT' => $dateTo, ), 'group' => array('USER_ID', 'EMAIL', 'NAME'), 'order' => array('FUSER.USER.ID' => 'ASC') )); if($userListDb->getSelectedRowsCount() > 0) { // есть просмотренные товары // сохраняем список адресатов $this->recipient = $userListDb; // запускаем рассылку return true; } else { // нет просмотренных товаров, не запускаем рассылку return false; } } /* * @return string * * Форма настройки триггера */ public function getForm() { return ' <table> <tr> <td>Сколько дней назад:</td> <td> <input size=3 type="text" name="'.$this->getFieldName('DAYS_VIEW').'" value="'.htmlspecialcharsbx($this->getFieldValue('DAYS_VIEW', 90)).'"> </td> </tr> </table> '; } /* * @return array|\Bitrix\Main\DB\Result|\CDBResult * * Функция, которая из данных события * вернет данные о получателе рассылки */ public function getRecipient() { // возвращаем сохраненные адресаты return $this->recipient; } } |
Здесь мы фильтруем список просмотров по дате просмотра за период в один день.
От текущего дня отнимаем указанное количество дней.
Таким образом, если у нас указано в настройках триггера 1 день,
то фильтр по дате на момент запуска будет выбирать записи за предыдущий день.
И так каждый день.
Если нам нужна возможность обработать старые данные, то есть отправить письма не только "за вчера",
но еще один раз отправить по всем пользователям, кто просматривал ранее, то
сообщим, что триггер такое умеет делать:
public static function canRunForOldData() { return true; } |
а теперь реализуем такую возможность в методе filter().
Для этого, если метод isRunForOldData() возвращает true,
фильтруем все что ранее [текущая_дата - указанное количество дней в настройках]:
if($this->isRunForOldData()) { $filter = array( '!DATE_VISIT' => null, '<DATE_VISIT' => $dateTo, ); } else { $filter = array( '>DATE_VISIT' => $dateFr om, '<DATE_VISIT' => $dateTo, ); } $filter['=SITE_ID'] = $this->getSiteId(); $userListDb = \Bitrix\Catalog\CatalogViewedProductTable::getList(array( 'select' => array('USER_ID' => 'FUSER.USER.ID', 'EMAIL' => 'FUSER.USER.EMAIL', 'NAME' => 'FUSER.USER.NAME'), 'filter' => $filter, 'group' => array('USER_ID', 'EMAIL', 'NAME'), 'order' => array('FUSER.USER.ID' => 'ASC') )); |
5. Делаем готовую рассылку
Готовая рассылка - это набор писем и настроек.
Из готовой рассылки можно создать на сайте рассылку.
Если у вас есть полезная рассылка, которая пригодится и на других сайтах, то можно сделать готовую рассылку.
Она будет всегда доступна как шаблон
Нужно сделать обработчик события OnPresetMailingList, в котором вернуть массив, описывающий рассылку.
Ключи массива:
TYPE - название раздела готовых рассылок при показе.
CODE - уникальный символьный код рассылки
DESC - описание для пользователя в админке, показывается в списке готовых рассылок
NAME - название рассылки
DESC_USER - описание для получателя, которое выведется при отписке
CHAIN - массив, описывающий письма
TIME_SHIFT - через сколько отправить письмо в минутах
SUBJECT - тема письма
MESSAGE - тело письма, может содержать html, теги персонализации, php-код
TRIGGER - массив, описывающий триггер запуска и его настройки, триггер цели и его настройки.
MODULE_ID - модуль триггера.
Триггер становится доступным для использования, если название его класса сообщается в обработчике OnTriggerList.
Если обработчик OnTriggerList установлен функцией AddEventHandler, то MODULE_ID не указывается.
Если функцией RegisterModuleDependences - то в MODULE_ID указывается тот же модуль.
CODE - код триггера, обязателен для указания
FIELDS - массив предустановленных настроек триггера
Далее пример рассылки из трех писем, отправляемых через сутки каждое.
Запускается по нашему новому триггеру просмотренных товаров.
Целью является оплата заказа. Так как триггер находится в модуле sale, то модуль будет указан.
AddEventHandler("sender", "OnPresetMailingList", array("MySenderEventHandler", "onPresetMailingList")); class MySenderEventHandler { public static function onPresetMailingList() { $result = array(); $result[] = array( 'TYPE' => 'Мои рассылки', 'CODE' => 'my_sale_view_prod', 'NAME' => 'Пример', 'DESC_USER' => 'Это автоматическая рассылка, предлагающая скидки, если вы просмотрели товар, но не решились купить его.', 'DESC' => 'Рассылка, запускаемая через день для тех, кто просмотрел на сайте товары.', 'TRIGGER' => array( 'START' => array( 'ENDPOINT' => array( 'MODULE_ID' => '', 'CODE' => 'my_viewed_product', 'FIELDS' => array('DAYS_VIEW' => 1) ) ), 'END' => array( 'ENDPOINT' => array( 'MODULE_ID' => 'sale', 'CODE' => 'order_paid', 'FIELDS' => array() ) ), ), 'CHAIN' => array( array( 'TIME_SHIFT' => 0, 'SUBJECT' => 'Письмо #1', 'MESSAGE' => 'Здравствуйте, #NAME#<br><br>Это письмо #1.', ), array( 'TIME_SHIFT' => 1440, 'SUBJECT' => 'Письмо #2', 'MESSAGE' => 'Здравствуйте, #NAME#<br><br>Это письмо #2.', ), array( 'TIME_SHIFT' => 1440, 'SUBJECT' => 'Письмо #3', 'MESSAGE' => 'Здравствуйте, #NAME#<br><br>Это письмо #3.', ), ) ); return $result; } } |
Данный код можно добавить в файл /bitrix/php_interface/init.php и готовая рассылка появится в списке.
Перед выводом готовой рассылки в списке, проверяется зарегистрированы ли указанные триггеры.
Если не зарегистрированы, то готовая рассылка не будет показана.
6. Итоги.
Теперь вы знаете как создать свои триггеры, как собрать готовую рассылку.
Расскажите, что вам не хватает в них.
sender.zip
(4.35 КБ)
В этом случае простые триггеры(в том числе и указанные вами) будут доступны для использования в рассылках контентщику без привлечения разработчиков.
т.е. если товары есть - то проводить рассылку, если товаров нет - то не проводить!!!
спасибо!
сейчас выводится просто ИМЯ а надо имя в определенном склонении (чтобы подходило по теме)
Добавим событие, по которому можно будет изменить/добавить теги во всех рассылках сразу.
т.е. делать как универсально
а не так как сейчас события для рассылок...
Создавать события для каждой - только запутываться.
Или вы про триггерная/обычная?
конечно событие одно - нос возможностью внутри проверять с какой рассылки пришло!
вообще событийная модель великая вещь - но как часто бывает не всегда поворотливая
например BeforePostingSendMail
внутри я не могу проверить с какой рассылки отправляется этот выпуск!!!!
Спасибо, очень детальная статья.
Единственное непонятно по тегам персонолизации. В вашем примере это совсем не теги персонализации, а статичные данные связанные текущим событием.
А как в методе getPersonalizeFields задать данные пользователя?
У меня триггер для не наступивших событий и мне надо, чтобы в каждом отправляемом письме заместо тегов подставлялись данные связанные с получателем письма, например, кол-во просмотров его поста.
В данном случае в "тег персонализации" удалось вывести идентификатор заказа,и, соответственно, использовать всякие компоненты.
При этом, модуль триггерных рассылок обращается в Вашей ф-ции getFetchDataModifier, передавая ей поля из результата запроса. А getFetchDataModifier уже переименовывает их так, как ожидается в шаблонах писем.
Зачем столько возни с BUYER_USER_NAME и BUYER_USER_SECOND_NAME ? Возможно, это только у меня так, но в триггерных рассылках макрос #NAME# вместо имени пользователя почему-то выводит часть email
подскажите, пжт:
1. как сгенерить ссылку "отписаться от рассылки"?
2. как можно удалить из списка пользователя вручную?
3. в таблице "список адресов" есть "прочитал", "переход по ссылке", "отписался" - как пользователя отметить в этих полях?
Скажите пожалуйста, вы можете сделать в стандартных шаблонах выбор, какой группе пользователей будет отправляться каждая из рассылок?
Допустим, у меня есть группы пользователей: оптовые и розничные покупатели. И тем и тем нужны разного рода рассылки.
Сам я настроить это никак не смогу, по этому прошу вас внедрить этот выбор группы пользователей в модули триггерных рассылок.
Также мне интересно, если клиент купил один раз, ему приходит цепочка писем, он покупает еще раз - и ему снова начинает приходить эта же цепочка писем. Разве это правильно?
Например мне нужно отправлять письма для условия "Забытая корзина" только определенной группе пользователей.
Я могу поменять фильтр для "Забытая корзина" или нужно создавать свое условие триггер?
в данной инструкции написано
/bitrix/php_interface/trigger_blog_comment.php - сам триггер
/bitrix/php_interface/init.php - для вывода триггера в списке
если у меня /local/php_interface/events/trigger.php - сам триггер, его возможно оттуда запустить? (сейчас скрипт не отрабатывает и даже лог не записывает)
У нас так и не появился раздел "тригерные рассылки" ни в меню маркетинг, ни где то еще. Установлены последние стабильные обновления. В чем может быть проблема? В журнале обновлений указано: Updated: sender (15.5.0)
Доступны триггерные рассылки.
В продукт включены семь готовых триггерных рассылок, которые можно использовать:
- "Забытая корзина" - попытка вернуть клиента и завершить покупку.
- "Отмененный заказ" - выяснить причину отмены и вернуть клиента в магазин.
- "Письма вдогонку" - попытка получения отзыва на сайте и предложения скидки на следующий заказ.
- "Будильник-90" - клиент не делал покупок три месяца, предлагается скидка, попытка вернуть на сайт.
- "Хулиганство" - шуточная триггерная рассылка, якобы от лица постового робота интернет-магазина.
- "Будильник-180" - клиент не делал покупок полгода, предлагаются заманчивые скидки и специальные условия.
- "Будильник-360" - клиент не делал покупок год, предлагаются самые высокие скидки и заманчивые предложения.
Модуль переехал в административное меню Маркетинг.
Появился компонент "Генерация купона на товар для почты" - генерирует купон на скидку с настраиваемыми параметрами
Например мне нужно отправлять письма для условия "Забытая корзина" только определенной группе пользователей.
Для каждого письма проверяется "не выполнено ли целевое событие", например "оплата заказа". Если выполнено - то конкретному получателю следующие письма не отправляются.
Исправьте, пожалуйста.
но сейчас это еще не доступно, как и свои поля в коннекторах адресов. "
Когда это время наступит????
Можете как-то прокомментировать данный момент?
Как сделать подписку на новые товары по фильтру?
пример с авто ру я выбрал в фильтре бмв-автомат-пробег до 100 тыс
сохранил поиск
и соответственно если в каталоге добавится новая машина которая подходит под этот фильтр - мне придет письмо, вот как это сделать на bitrix как отловить что в каталоге появилась позиция которая подходит под этот фильтр.. возможно ли это сделать через триггерные рассылки?
Т.е. сделать по аналогии с стандартным тригером "Забытая корзина", когда каждому клиенту отправляется именного его набор товаров из корзины.
Если да, то подскажите, пожалуйста, где Вы подгружали данный список товаров?
Теперь разобраться будет куда проще
нужно запускать "забытую корзину" через час как клиент её забыл, а не на следующий день...