Тема: Редактирование полей заказа из AJAX контроллера

Здравствуйте, я занимаюсь разработкой модуля доставки. Класс типа доставки почти готов, но неожиданно столкнулся с проблемой.
Дело в том, что модуль использует виджет карты, который выводится через метод getAddittionalHtml(). И далее, в нем реализовано изменение экстра-параметров заказа через AJAX. Обработчик AJAX запросов я разместил в отдельном фронт-контроллере.
Так вот в чем беда: когда я обращаюсь через AJAX к тем же функциям, что и через HTTP - система говорит мне, что я вызываю $this не из того контекста. В принципе ничего удивительного, только я никак не могу придумать как ее обмануть (написать правильно). В областях видимости совсем запутался.
Подскажите пожалуйста как это слелать.
Вот front-controller:

<?php
namespace Imldelivery\Controller\Front;

/**
* Фронт контроллер
*/
class AjaxCtrl extends \RS\Controller\Front
{
    public
        $order,
        $iml;

    function init()
    {
        //$this->order = \Shop\Model\Orm\Order::currentOrder();
        $this->iml = new \Imldelivery\Model\DeliveryType\Iml();
    }   

    function actionIndex()
    {
        if ($this->url->isAjax()) {
            $action = $this->url->request('action', TYPE_STRING, '');
            $params = $this->url->request('params', TYPE_ARRAY, '');
            $output = $this->iml->$action($params);
            //$this->result->addSection('params', $params);
            return $this->result->addSection($action, $output);
        }
    }
}
?>

Вот AJAX:

function updatePrice () {
    var url = $('#selectRegionCombo').attr('data-ajax-action');
    $.ajax({
        url: url,
        type: 'POST',
        dataType: 'json',
        data: {action: 'getDeliveryCostAjax'},
    })
    .done(function(data) {
        console.log(data.getDeliveryCostAjax);
    })
    .fail(function() {
        console.log("error");
    })
    .always(function() {
        console.log("complete");
    });  
}

Вот функция из модели:

public function getDeliveryCostAjax()
    {
        $order = \Shop\Model\Orm\Order::currentOrder();
        $address = $order->getAddress();
        return $this->getDeliveryCost($order, $address);
    }
/**
    * Возвращает стоимость доставки для заданного заказа. Только число.
    * 
    * @param \Shop\Model\Orm\Order $order
    * @param \Shop\Model\Orm\Address $address - Адрес доставки
    * @return double
    */
    function getDeliveryCost(\Shop\Model\Orm\Order $order, \Shop\Model\Orm\Address $address = null, $use_currency = true)
    {
        //return '';
        if(!$address) { 
            $address = $order->getAddress();
        }
        $delivery = $order->getDelivery(); 
        $cost = $this->getCost($order, $address);
        if (isset($cost['Price'])) {
            return $cost['Price'];
        } else {
            foreach ($cost as $error) {
                //$this->addError(t('Ошибка '.$error.'. '.$error));
            }
            return $cost;
        }
    }
/**
     * Запрос базовой стоимости заказа у IML
     * 
     * @param  array|null $filters
     * @return array
     */
    function getCost(\Shop\Model\Orm\Order $order, \Shop\Model\Orm\Address $address = null)
    {
        $extra = $order->getExtraInfo();

        $content = array(
            'Job' => $this->getOption('service_id'),            // услуга
            'RegionFrom' => $this->getOption('region_id_from'), // регион отправки
            'RegionTo' => $extra['iml_region_to']['value'],   // регион получения
            'Volume' => '1',            // кол-во мест  
            'Weigth' => $order->getWeight() ? $order->getWeight() : '1',            // вес(кг)
            'SpecialCode' => $extra['iml_request_code']['value']      // код пункта самовывоза(см. справочник пунктов самовывоза)
        );

        return $this->postApiRequest(self::URL_API_GETPRICE, self::API_LOGIN, self::API_PASS, $content);
    }

Повысить оценку Понизить оценку

2 Отредактировано Закусило Александр (08.12.2015 19:58:57)

Re: Редактирование полей заказа из AJAX контроллера

Всё гораздо проще чем Вы думаете. Никакого контроллера делать не нужно у нас всё для этого предусмотрено.
Вам надо отловить событие когда пользователь меняет пункт выдачи. После чего сделать запрос на фронт контроллер:

/shop/controller/front/checkout.inc.php он же в качестве маршрута shop-front-checkout (смотрите файл handlers.inc.php в модуле shop)
Соответственно url для обращения будет /checkout/useract/

Там есть метод actionUserAct. Он позволяет вызвать пользовательский метод из Вашего класса доставки или оплаты. В Вашем случае доставки.

Он принимает несколько параметров:
1. module - Имя модуля (По умолчанию Shop, у Вам соответственно Ваше название папки модуля)
2. typeObj - (0 или 1)  0 - доставка (DeliveryType), 1 - оплата (PaymentType)
3. typeId - id доставки или оплаты
4. class - Класс для обращения, т.е. класс который будет вызван.
5. userAct - Статический метод который нужно вызвать
6. params - Дополнительные параметры для передачи в метод
Описание типов переменных и, то что они принимают можно посмотреть в самом файле я думаю проблем не возникнет.

Соответственно мы можем сформировать запрос через AJAX, вот пример кода для доставки axiomus:

var $_this = $(this);
$.ajax({
                url  : $_this.data('url'),
                type : 'POST',    
                dataType : 'json',    
                data : {
                    module  : 'Axiomus',
                    typeObj : 0, //Доставка
                    typeId  : $_this.data('delivery-id'),
                    'class' : 'Axiomus',
                    userAct : 'getChangedDelivery',
                    params  : {
                        order : { //Объект заказа
                            way   : $_this.data('way'),
                            key   : $_this.data('key'),
                            value : office
                        }
                    }
                },
                success : function(response){
                   if (response.success){
                       $(data.options.axiomusCostId + $_this.data('delivery-id')).html(response.data);
                   } 
                }
            });

Мы в шаблоне добавляем там свой js который обслуживает обработку этих событий и следит за выбором пользователя.

{addjs file="%axiomus%/delivery/axiomus_widjet.js"}

Поэтому я дополнительно скину код javascript самого модуля доставки Axiomus. Он не решит Ваших всех проблем, но там можно подсмотреть реализацию.

(function( $ ){
   $.AxiomusWidjetCreator = function( method ) {
        //Найдём ближайший input c выбором
        var closestRadio = $('input[name="delivery"]:eq(0)');        
        var form         = closestRadio.closest('form'); //найдем форму
       
        var defaults = {            
           axiomusDiv      : '.axiomusDeliveryBlock', //Контейнер куда будет вставлятся информация Axiomus
           axiomusAdress   : '.axiomusAdress', //Куда вставляется адрес
           axiomusSchedule : '.axiomusSchedule', //Блок с расписанием работы
           axiomusCostId   : '#scost_' //Идентификатор блока с ценой за доставку
        },
        
        $this = form,
        data  = $this.data('axiomusInfo');
        
        if (!$this.length) return;
        if (!data) { //Инициализация
            data = { options: defaults };
            $this.data('axiomusInfo', data);
        }
        
        //public
        var methods = {
            /**
            * Инициализация плагина
            * 
            * @param initoptions - введённые пераметра для записи или перезаписи
            */
            init: function(initoptions) 
            {
                data.options = $.extend(data.options, initoptions);
                
                //Навесим все события
                presetAxiomusRadioEvents();
                
                //Если контент обновился (для заказа на одной странице)
                $('body').on('new-content',function(){
                    //Навесим события на переключатели
                    presetAxiomusRadioEvents();
                    
                    //Проверим выбор sheepla и если она выбрана, то вызовем соответсвующий метод
                    var selectedCheckbox = $('input[name="delivery"]:checked');
                    if (typeof(selectedCheckbox.data('axiomus-div-id'))!='undefined'){ 
                    //  setOrClearAxiomusInfo(null, selectedCheckbox); 
                    }
                });
            },
            
            /**
            * Открывает доступность Axiomus для выбора
            * 
            * @param checkBoxObj  - объект с функциями Axiomus
            */
            showAxiomus : function (checkBoxObj)
            {       
                var parent   = checkBoxObj.data('axiomus-div-id');
                $("select",$(parent)).prop('disabled',false).change();
            },
            
            /**
            * Убирает доступность Axiomus для выбора из всех вариантов
            * и скрывает поля
            * 
            */
            clearAxiomus : function ()
            {       
               $(data.options.axiomusDiv+" select").prop('disabled',true); 
            }
        }
        
        //private
        
        /**
        * Срабатывает при нажатии на выбранную доставку.
        * Если у этой доставки есть признак что если есть признак, что это Axiomus,
        * то ничего не делаем. Если нет признака, что подгружалось, 
        * 
        */
        var setOrClearAxiomusInfo = function (Event, item) 
        {
            if (Event === null){ //Если вызвали программно
                var $_this = item;
            }else{ //Если событие
                var $_this = $(this);
            }
            
            var dataAxiomusDiv = $_this.data('axiomus-div-id');
            if ( typeof(dataAxiomusDiv) != 'undefined' ) { //Если это радиокнопка Axiomus
               methods.showAxiomus($_this);  //Стартуем            
            }else{
               methods.clearAxiomus();  //Очищаем    
            }
        },
        
        /**
        * Устанавливает события для обработки
        */
        presetAxiomusRadioEvents = function()
        {
            //Смена адреса доставки в выпадающем списке
            $(data.options.axiomusDiv+" select",$this).off('change.axiomus').on('change.axiomus', changeAxiomusAdress);
            $(data.options.axiomusDiv+" input[type='radio']", $this).off('change.axiomusradio').on('change.axiomusradio', changeAxiomusRadio);
            
            //Пройдёмся по контейнерам СДЕК
            $(data.options.axiomusDiv,$this).each(function(i){
                 //Найдём и отметим подходящие радиокнопки, которые относятся к СДЕК  
                 var deliveryId = $(this).data('delivery-id');
                 var radio      = $('input[name="delivery"][value="'+deliveryId+'"]');
                 //Перенесём данные к выборанной радио кнопке, чтобы можно было манипулировать
                 radio.data('axiomus-div-id',"#"+$(this).attr('id'));
                 if (radio.prop('checked')){
                     $("select", $(radio.data('axiomus-div-id'))).prop('disabled',false).change();
                 }
            });

            //Получим все радио кнопки
            var radioboxes = $('input[type="radio"][name="delivery"]',$this); //найдем radio
            
            //Навесим переключение радиокнопок 
            radioboxes.each(function(){
                $(this).off('click.axiomus').on('click.axiomus', setOrClearAxiomusInfo);
            });
            $(data.options.axiomusDiv+" input[type='radio']:checked", $this).trigger('change.axiomusradio');
        },
        
        
        /**
        * Смена адреса доставки в выпадающем списке
        * 
        */
        changeAxiomusAdress = function()
        {
            var parent = $(this).closest(data.options.axiomusDiv);
            var info   = $("option:selected",$(this)).data('info');
            
            if (typeof(info.address)!='undefined'){
                $(data.options.axiomusAdress, $(parent)).html("Адрес: "+info.address); 
            }
            if (typeof(info.schedule)!='undefined'){
                $(data.options.axiomusSchedule, $(parent)).html(info.schedule);
            }
            
            var office = $(this).val();
            var $_this = $(this);
            $.ajax({
                url  : $_this.data('url'),
                type : 'POST',    
                dataType : 'json',    
                data : {
                    module  : 'Axiomus',
                    typeObj : 0,
                    typeId  : $_this.data('delivery-id'),
                    'class' : 'Axiomus',
                    userAct : 'getChangedDelivery',
                    params  : {
                        order : { //Объект заказа
                            way   : $_this.data('way'),
                            key   : $_this.data('key'),
                            value : office
                        }
                    }
                },
                success : function(response){
                   if (response.success){
                       $(data.options.axiomusCostId + $_this.data('delivery-id')).html(response.data);
                   } 
                }
            })
        },
        
        /**
        * Смена переключателя типа забора для DPD
        * 
        */
        changeAxiomusRadio = function ()
        {
            var parent = $(this).closest(data.options.axiomusDiv);
            var val    = $(this).val();
            
            if (val=="1"){
               $("select", parent).prop('disabled', false); 
            }else{
               $("select", parent).prop('disabled', true);  
            }
        } 
        
       
        if ( methods[method] ) {
            methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
            return methods.init.apply( this, arguments );
        }
   }     
})( jQuery );

$(document).ready(function(){
    $.AxiomusWidjetCreator();
});

Послав запрос система сделает обращение к публичному статическому метод (public static function) Вашей доставки. В который передадутся несколько параметров. Например для случая с Axiomus и методом getChangedDelivery:

\Axiomus\Model\Deliverytype\Axiomus::getChangedDelivery($order - объект заказа, $delivery_id - id доставки, $params - Ваши дополнительные параметры, которые Вы передадите)

Теперь, что касается заказа. Заказ во время его оформления находится в сессии постоянно, поэтому Вы можете в него записывать "на лету" сразу и данные сохранятся, просто присвоив нужные значения не вызывая при этом insert или update. Эти методы системе понадобятся только один раз при создании самого заказа, когда пользователь подтвердил его создание.
Мы для записи данных рекомендуем использовать метод addExtraKeyPair у объекта заказа.

$order->addExtraKeyPair('ключ по которому можно достать данные', 'Значение для записи можно массив');

А потом доставать данные с помощью:

$order->getExtraKeyPair('ключ по которому можно достать данные');

Т.к. в таком случае эти данные можно будет достать и после физической записи заказа.

У заказа также есть метод addExtraInfoLine он позволит не только записать данные в заказ, но и инфомация будет доступна для отображения в админке при редактировании товара.

Re: Редактирование полей заказа из AJAX контроллера

Отпишитесь если нужна помощь пожалуйста.

4

Re: Редактирование полей заказа из AJAX контроллера

Спасибо, за разъяснения. Буду пробовать. Правда переделывать много придется, так как в ожидании ответа я уже еще одну реализацию написал (я отлавливал AJAX в методе getAddittionalHtml):

function getAddittionalHtml(\Shop\Model\Orm\Delivery $delivery, \Shop\Model\Orm\Order $order = null)
    {
        //return var_dump($this);
        $request = \RS\Http\Request::commonInstance();
        if (!$order) {
            $order = \Shop\Model\Orm\Order::currentOrder();
        }
        $extra = $order->getExtraInfo();
        $imlData = $extra['iml_data']['data'];

        $view = new \RS\View\Engine();
        $view->assign(array(
            'region_id_to'       => $imlData['region_id_to'],       //Регион куда
            'region_id_from'     => $this->getOption('region_id_from'),     //Регион откуда
            'service_ids'        => json_encode($this->getActiveServices()),             //Услуги этогй доставки
            'delivery_cost_json' => json_encode($this->getImlCost($order)),         //Текущий объект цены доставки
            'delivery_cost'      => $this->getImlCost($order),         //Текущий объект цены доставки
            'order'              => $order,                                 //Текущий недоофрмленный заказ
            'delivery'           => $delivery,                              //Текущий объект доставки
            'user'               => \RS\Application\Auth::getCurrentUser(), //Текущий объект пользователя
        ) + \RS\Module\Item::getResourceFolders($this)); 

        if ($request->isAjax() && $this->getOption('show_map') == 1) {
            $action = $request->request('action', TYPE_STRING, '');
            $params = $request->request('params', TYPE_ARRAY, '');

            switch ($action) {

                case 'loadMap':
                    return $view->fetch('%imldelivery%/delivery/iml/map.tpl');;
                    break;

                default:
                    $output = $this->$action($params);
                    $view->assign(array(
                        'output' => json_encode($output),
                        'action' => $action
                    ));
                    return $view->fetch('%imldelivery%/delivery/iml/ajax_data.tpl');
                    break;

            }
        }

        if ($this->getOption('show_map') == 1) {
            return $view->fetch('%imldelivery%/delivery/iml/widget.tpl');
        }
        
    }

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

Повысить оценку Понизить оценку

5 Отредактировано Закусило Александр (10.12.2015 19:38:38)

Re: Редактирование полей заказа из AJAX контроллера

Хорошо. Ждём решения. Если нужна помощь и с административной частью, то мы поможем:) Обращайтесь.