Реализация e-mail-оповещений в ImageCMS


    В этой публикации мы расскажем о том, как среднестатистический ведущий программист ImageCMS Андрюша реализовал удобную систему e-mail-оповещений пользователей Интернет-магазина. Сам он теперь утверждает, что не программист, а фея.

    В чем заключалась проблема до реализации нового функционала?
    Немало огорчало, что отправка e-mail пользователям не имела централизованного места управления, ведь это создавало определенные неудобства администратору и наличие множества лишнего кода, который дублировался.
    Смотрим-с:

    /**
     * Send email to user.
     *
     * @param SOrders $order
     * @return bool
     */
    protected function _sendMail(SOrders $order)
    {
        //Check setting to send message
        if (ShopCore::app()->SSettings->ordersSendMessage == 'false')
            return;
        //Array of parameters to send
        $replaceData = array(
            '%userName%' => $order->getUserFullName(),
            '%userEmail%' => $order->getUserEmail(),
            '%userPhone%' => $order->getUserPhone(),
            '%userDeliver%' => $order->getUserDeliverTo(),
            '%orderId%' => $order->getId(),
            '%orderKey%' => $order->getKey(),
            '%orderLink%' => shop_url('cart/view/' . $order->getKey()),
        );
    
        //Use function encode for every element from $replaceData
        $replaceData = array_map('encode', $replaceData);
    
        //Get settings for sending
        $fromEmail = ShopCore::app()->SSettings->ordersSenderEmail;
        $shopName = ShopCore::app()->SSettings->ordersSenderName;
        $theme = ShopCore::app()->SSettings->ordersMessageTheme;
        //Formating message
        $message = str_replace(array_keys($replaceData), $replaceData, ShopCore::app()->SSettings->ordersMessageText);
    
        //Load CodeIgniter Email library
        $this->load->library('email');
        $config['mailtype'] = ShopCore::app()->SSettings->ordersMessageFormat;
        //Initialize library configurations
        $this->email->initialize($config);
    
        //Sending message	
        $this->email->from($fromEmail, $shopName);
        $this->email->to($order->getUserEmail());
        $this->email->subject($theme);
        $this->email->message($message);
        $this->email->send();
    }
    
    protected function _sendNewMail(SOrders $order)
    {
        //Check setting to send message
        if (ShopCore::app()->SSettings->ordersSendMessage == 'false')
            return;
    
        //Array of parameters to send
        $replaceData['variables'] = array(
            '%userName%' => $order->getUserFullName(),
            '%userEmail%' => $order->getUserEmail(),
            '%userPhone%' => $order->getUserPhone(),
            '%userDeliver%' => $order->getUserDeliverTo(),
            '%orderId%' => $order->getId(),
            '%orderKey%' => $order->getKey(),
            '%orderLink%' => shop_url('cart/view/' . $order->getKey()),
        );
    
        $replaceData['to'] = $order->getUserEmail();
    
        //Load CodeIgniter Email library
        $this->load->library('email');
    
        //Sending message	
        $this->email->sendMail('toUserOrderNotification', $replaceData);
    }
    
    


    Что предпринял Андрюша?

    Для централизации всей системы создания и отправки оповещений, Фея написал отдельный модуль. Изначально вместе с модулем идет 6 интегрированных в систему шаблонов, наиболее часто используемых в работе сайта:
    1. регистрация пользователя;
    2. восстановление пароля;
    3. изменение пароля;
    4. уведомление покупателя о совершении заказа;
    5. смена статуса заказа;
    6. уведомление о появлении.



    Были применены неймспейсы, что позволяет вызывать метод с любого места:
            namespace cmsemail\classes;
    


    Главное препятствие на пути к достижению цели Андрея заключалось далеко не в количестве кода, а именно в необходимости найти все места в коде, где идет отправка e-mail, и заменить их новым методом. Наш герой не отчаивался… Фея заткнул уши группой Korn и абстрагировал обработку всей логики отправки писем пользователю и администратору в небольшой метод, что можно увидеть на следующем полотне.
    Смотрим-с:

    //Creatind link to check for administrator
    $checkLink = site_url() . "admin/components/run/shop/orders/createPdf/" . trim($order->getId());
    //Array of parameters to send
    $emailData = array(
        'userName' => $order->user_full_name,
        'userEmail' => $order->user_email,
        'userPhone' => $order->user_phone,
        'userDeliver' => $order->user_deliver_to,
        'orderLink' => shop_url('cart/view/' . $order->key),
        'products' => $productsForEmail,
        'deliveryPrice' => $deliveryPrice,
        'checkLink' => $checkLink,
    );
    
    //Sending emeils
    \cmsemail\email::getInstance()->sendEmail($order->user_email, 'make_order', $emailData);
    


    Так этот метод выглядит внутри:

    /**
     * send email
     *
     * @param string $send_to - recepient email
     * @param string $patern_name - email patern  name
     * @param array $variables - variables to raplase in message:
     *   $variables = array('$user$' => 'UserName')
     * @return bool
     */
    public function sendEmail($send_to, $patern_name, $variables)
    {
        //loading CodeIgniter Email library 
        $this->load->library('email');
    
        //Getting settings 
        $patern_settings = $this->cmsemail_model->getPaternSettings($patern_name);
        $default_settings = $this->cmsemail_model->getSettings();
    
        //Prepare settings into correct array for initialize library
        if ($patern_settings)
        {
            foreach ($patern_settings as $key => $value)
            {
                if (!$value)
                {
                    if ($default_settings[$key])
                    {
                        $patern_settings[$key] = $default_settings[$key];
                    }
                }
            }
        }
        $default_settings['type'] = strtolower($patern_settings['type']);
    
        //Initializing library settings
        $this->_set_config($patern_settings);
    
        //Sending user email if active in options
        if ($patern_settings['user_message_active'])
        {
            $this->from_email = $patern_settings['from_email'];
            $this->from = $patern_settings['from'];
            $this->send_to = $send_to;
            $this->theme = $patern_settings['theme'];
            $this->message = $this->replaceVariables($patern_settings['user_message'], $variables);
            if (!$this->_sendEmail())
            {
                $this->errors[] = lang('User message doesnt send', 'cmsemail');
            }
            else
            {
                //Registering event if success
                \CMSFactory\Events::create()->registerEvent(
                        array(
                    'from' => $this->from,
                    'from_email' => $this->from_email,
                    'send_to' => $this->send_to,
                    'theme' => $this->theme,
                    'message' => $this->message
                        ), 'ParentEmail:userSend');
                \CMSFactory\Events::runFactory();
            }
        }
    
        //Sending administrator email if active in options
        if ($patern_settings['admin_message_active'])
        {
            $this->from_email = $patern_settings['from_email'];
            $this->from = $patern_settings['from'];
    
            if ($patern_settings['admin_email'])
            {
                $this->send_to = $patern_settings['admin_email'];
            }
            else
            {
                $this->send_to = $default_settings['admin_email'];
            }
    
            $this->theme = $patern_settings['theme'];
            $this->message = $this->replaceVariables($patern_settings['admin_message'], $variables);
    
            if (!$this->_sendEmail())
            {
                $this->errors[] = lang('User message doesnt send', 'cmsemail');
            }
            else
            {
                //Registering event if success
                \CMSFactory\Events::create()->registerEvent(
                        array(
                    'from' => $this->from,
                    'from_email' => $this->from_email,
                    'send_to' => $this->send_to,
                    'theme' => $this->theme,
                    'message' => $this->message
                        ), 'ParentEmail:adminSend');
                \CMSFactory\Events::runFactory();
            }
        }
    
        //Returning status 
        if ($this->errors)
        {
            return FALSE;
        }
        else
        {
            return TRUE;
        }
    }
    
    


    Так среднестатистический ведущий программист сделал централизацию всей системы оповещения пользователей. Кода стало меньше, он стал изящнее, управление оповещениями — проще.



    Какие проблемы решает данный функционал?

    Обновленная система оповещений, локализованная в едином интерфейсе, значительно упрощает работу, особенно неопытному пользователю, поскольку весь функционал собран в администраторской панели ImageCMS.
    На следующем скриншоте изображена панель приборов настройки шаблонов писем, которое получает пользователь и администратор.

    Пример настройки письма пользователю о сделанном заказе


    Пример настройки письма администратору магазина о сделанном заказе.


    Можно ли создавать свои шаблоны уведомлений?
    Более опытные юзеры ImageCMS Shop могут создавать собственные шаблоны оповещений, а также интегрировать их в систему. Гибкая система настроек позволяет варьировать компоненты отправки в разных комбинациях. Каждое оповещение имеет свои отдельные настройки адреса отправки, данных об отправителе, e-mail-адресе администратора и теме письма. Также имеются стандартные настройки, и если необязательные поля не заполнены, то данные будут взяты именно оттуда.
    Для еще большего удобства администратора предусмотрена проверка на сервере возможности отправки оповещения. Отправитель будет уверен в том, что получатель уведомлен о событии:
    • –10
    • 3,5k
    • 8
    ImageCMS
    40,00
    Компания
    Поделиться публикацией

    Похожие публикации

    Комментарии 8

    • НЛО прилетело и опубликовало эту надпись здесь
        0
        //Prepare settings into correct array for initialize library
        if ($patern_settings)
        {
        foreach ($patern_settings as $key => $value)
        {
        if (!$value)
        {
        if ($default_settings[$key])
        {
        $patern_settings[$key] = $default_settings[$key];
        }
        }
        }
        }

        Я правильно понимаю что если какая-то настройка $patern_settings (pattern?) при приведении к булевскому типу дает false, то она заменяется на дефолтную? Т.е. передать 0 или false или пустую строку в настройках нельзя в принципе.

        Зачем foreach, когда достаточно array_merge($default_settings, $patern_settings)?

        Да, разница в результатах у вашего foreach и у array_merge есть. В этом случае в результирующем массиве будут элементы с ключами, которых изначально могло не быть в $patern_settings, но были в $default_settings. Но, насколько я понимаю в колбасных обрезках, это именно то что требуется при слиянии дефолтных и передеанных настроек.
          0
          1. Когда вы отправляете мыло в пределах процесса web-сервера (проще говоря — непосредственно при обработке запроса) — бог убивает котенка.
          2. Слияние массивов через foreach — сами знаете, что это. Андрей не Фея, ему еще долго грызть гранитную палочку магии.
          3. В большинстве случаев else-блоки не нужны.
          Дальше не смотред.
          В общем, решение далеко не так красиво, как вы о нем пишете.
            0
            oтправляете мыло в пределах процесса web-сервера

            это в целом, или вы имеете ввиду именно процессы типа рассылок?
              0
              А я думаю, это лучший способ валидации, например, email во время регистрации. То есть часто такая отправка почты вполне приемлема, и даже полезна.
                +2
                Наверно, имеется ввиду, что отправку почты надо ставить в очередь и рассылать отдельным процессом, а не в рамках выполнения скрипта, обрабатывающего запрос пользователя, в вашем случае регистрации.
                  0
                  Наверное, а я говорю что во время регистрации таким образом проще всего проверить валидность email, и задержка во время такой разовой операции не критична.
                  Если имеется подписка на новые сообщения в треде — то да, в таком случае логичнее составлять очередь и отправлять отдельно.
              0
              И еще, ваша реализация не умеет отсылать HTML и plain text версии одного письма, а также аттачи, в том числе аттачи картинок для HTML-версии, несмотря на то, что на скринах WYSIWYG-редактор шаблонов имеет возможность вставки картинок.

              Не знаю умеет ли это библиотека CodeIgniter Email, но точно знаю, что есть библиотеки которые умеют.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое