Срабатывание плагина, если tv изменился

Приветствую.

Подскажите, есть ли возможность проверить, изменилось ли значение tv-параметра ресурса и (если изменилось) выполнить плагин по onDocFormSave (например)?

Ситуация: для раздела есть поле скидка. При его изменении — изменяется цена товаров данного подраздела.
Нужно запустить плагин (выполнить изменение цен) только тогда, когда значение скидки изменилось (чтобы не нагружать сервак просто так).
Владислав
14 января 2015, 14:32
modx.pro
4
4 216
0

Комментарии: 20

Наумов Алексей
15 января 2015, 08:58
0
Сразу в голову пришло запихнуть куда-нибудь массив вида $sales[$resource_id] = $sale в виде json и проверять новое значение TV с ним.

Или для этих же целей можно использовать:

$prop = $modx->fromJSON($resource->get('properties'));
$old_sale = empty($prop['old_sale']) ? 0 : $prop['old_sale'];

... тут сравниваем скидку с $old_sale
...

$prop['old_sale'] = $resource->getTVValue('sale');

$resource->set('properties', $modx->toJSON($prop')))
$resource->save();


как то так…
    Сергей Шлоков
    15 января 2015, 10:28
    0
    А где менеджер меняет значение, в админке или на сайте?
    but1head
    15 января 2015, 10:42
    0
    switch ($modx->event->name) {
    	case 'OnDocFormPrerender':
    		$_SESSION['before'] = $resource->getTVValue('adres'); // адрес при загрузке
    	break;
    	case 'OnDocFormSave':
    		$after = $resource->getTVValue('adres'); // адрес при сохранении
    
    		if($after == $_SESSION['before']){
    			$modx->event->output('равны');
    		}else{
    			$modx->event->output('не равны');
    		}
    	break;
    }
      Сергей Шлоков
      15 января 2015, 11:38
      0
      Можно и так. Только
      $modx->event->output('равны');
      в этом событии не сработает.

      Вот чуть измененный вариант
      switch ($modx->event->name) {
      	case 'OnDocFormRender':
      		$_SESSION['discount'] = $resource->getTVValue('discount'); 
      	break;
      	case 'OnDocFormSave':
      		$new_discount = $resource->getTVValue('discount'); 
      
      		if($new_discount != $_SESSION['discount']){
      			$modx->runSnippet('RecalcPrice',array('discount'=>$new_discount,'parent'=>$resource->id ));
      		}
      		unset($_SESSION['discount']);
      	break;
      }
      В сниппете RecalcPrice меняешь цены согласно новой скидке для ресурсов из раздела, указанном в параметре parent.
        Владислав
        15 января 2015, 12:06
        0
        Спасибо)
        А еще подскажи пжлста, как в сниппете RecalcPrice получить пришедшие переменные?
        Раньше не доводилось вызывать runSnippet
          Сергей Шлоков
          15 января 2015, 12:10
          0
          Как к обычным переменным $discount, $parent.
          И есть еще массив всех переменных сниппета $scriptProperties. Можно через него $scriptProperties['discount'] и т.п.
      Василий Наумкин
      15 января 2015, 11:11
      0
      Если ТВ меняется в админке (или вообще, через процессор), то нужно ловить событие OnBeforeDocFormSave и сравнивать присланное значение ТВ с имеющимся в документе.

      Если они отличаются — выполнять свой код по обновлению цен. Можно прям в этом событии, а можно поставить флаг в сессию, выполнить код в OnDocFormSave (то есть, уже после сохранения новой цены в ТВ) и удалить флаг до следующего раза.

      В любом случае, нужно слушать события изменения документа, потому что все сохранения ТВ — там.
        Владислав
        15 января 2015, 11:14
        0
        Ну это все понятно (в теории).
        Но вот как получить ТВ по событию OnBeforeDocFormSave (кроме предложенного выше решения)?
          Василий Наумкин
          15 января 2015, 11:19
          0
          Все присылаемые значения можно получить из $_POST, все имеющиеся в ресурсе через $resource->get().

          ТВ в $_POST, если я не ошибаюсь, получаются как tv_id, то есть:
          $tv = $_POST['tv_5'];
        Cyrax_02
        15 января 2015, 11:39
        1
        0
        Но вот как получить ТВ по событию OnBeforeDocFormSave (кроме предложенного выше решения)?

        В OnBeforeDocFormSave новые (сохраняемые) значения TV можно получить следующими способами:
        1) Из массива $_POST (как указал Василий). Но этот вариант работает только при вызове коннекторов (сохранение ресурса в админке). Если процессор вызывается из собственного кода, то никаких параметров в запросе не будет.

        2) Из массива data, который также передаётся плагину (не задокументирован). В этом массиве лежат все новые (сохраняемые) параметры — и стандартные, и ТВ. Ключ стандартного параметра = имени параметра, ключ ТВ = tvID.

        В OnBeforeDocFormSave старые (исходные) значения TV можно получить методом getTVValue (извлекает данные непосредственно из БД).

        Самый корректный вариант, имхо, — это OnBeforeDocFormSave + массив data + getTVValue.
          Владислав
          15 января 2015, 12:04
          0
          Спасибо за оживленную дискуссию! :-)
            Владислав
            16 января 2015, 16:10
            0
            Итак, я попробовал все предложенные варианты. У каждого свои ограничения:
            0. При работе с сессиями в любом виде, на странице (в админке), в которой производится изменение скидки, выводится "../www/core/components/shopkeeper/elements/tv/output/".
            1. От Сергея modx.pro/help/4600/#comment-33447
            OnDocFormRender не выполняется при сохранении значения, только при перезагрузке страницы (вполне логично).
            2. От Василия modx.pro/help/4600/#comment-33439
            $_POST тоже приходит только при перезагрузке страницы + приходится пользоваться переменной сессии.
            3. От Cyrax_02 modx.pro/help/4600/#comment-33448 Не смог добраться до массива data.
            В итоге остановился на варианте №1. Указанные ограничения победить пока не удалось.

            код:
            <?php
            /*
            *  Action: OnDocFormRender, OnDocFormSave 
            */
            global $modx;
            
            if ($mode == modSystemEvent::MODE_UPD ) {
            	$parent = $resource->get('parent');
            	$isDiscountChanged = 0;
            	
            	if ($parent == "9") {
            		$startTime = microtime(true);
            		$brand = $resource->get('menutitle');
            
            		switch ($modx->event->name) {
            			case 'OnDocFormRender':
            				$_SESSION['discount_before'] = (float)$resource->getTVValue(13); // скидка при загрузке
            
            				$modx->log(modX::LOG_LEVEL_ERROR, '$discount_before: ' . $_SESSION['discount_before']);
            			break;
            			case 'OnDocFormSave':
            				$discount = (float)$resource->getTVValue(13); // скидка при сохранении
            				$priceCoeff = (float)$resource->getTVValue(14); // коэфф. при сохранении
            
            				if($discount != $_SESSION['discount_before']){
            					$isDiscountChanged = 1;
            				}
            				
            				if ($isDiscountChanged) {
            					
            					{...} 	// получение выборки из базы
            
            					if ( $query -> prepare() && $query -> stmt -> execute() ) {
            						while ( $queryRow = $query -> stmt -> fetch( PDO::FETCH_ASSOC ) ) {
            							$thisProject = $modx -> getObject( 'modResource', $queryRow['id'] );
            							$price = $thisProject -> getTVValue( 1 );
            
            							$discountPrice = round((1-$discount/100)*$price);
            							$thisProject->setTVValue(2, $discountPrice); 	//внесение цены со скидкой	
            					}
            				}
            
            				unset($_SESSION['discount_before']);
            
            
            				$finishTime = microtime(true);
            				$timeScript = $finishTime - $startTime;	
            				$modx->log(modX::LOG_LEVEL_ERROR, 'setDiscount checked ' . $i . ' elements');
            				$modx->log(modX::LOG_LEVEL_ERROR, 'Time wasted: ' . round($timeScript,4) . 'sec');
            
            			break;
            		}
            	}
            }
              Владислав
              16 января 2015, 17:00
              0
              Дополнение:
              По OnDocFormRender значение изменяется, но не обнуляется.
              Думаю, с $_POST тоже самое.
                Cyrax_02
                17 января 2015, 02:06
                0
                3. От Cyrax_02 modx.pro/help/4600/#comment-33448 Не смог добраться до массива data.
                В итоге остановился на варианте №1. Указанные ограничения победить пока не удалось.

                Навскидку могу предположить, что:
                а) ты его «искал» в тот момент, когда у тебя плагин не был подписан на событие OnDocFormSave и (или) OnDocFormRender. Массив $data передаётся только обработчику OnBeforeDocFormSave
                б) получить к нему доступ ты пытался в разделе case 'OnDocFormRender' или case 'OnDocFormSave' оператора switch. С массивом $data нужно работать в case 'OnBeforeDocFormSave'

                Ещё раз по порядку:
                1) Подписываешь плагин на событие OnBeforeDocFormSave
                2) В плагине в разделе case 'OnBeforeDocFormSave' пишешь:
                $modx->log(modX::LOG_LEVEL_ERROR, print_r($data, true));
                3) Загружаешь страницу (именно ту, которая вызывает плагин)
                4) Смотришь логи modx: там увидишь все НОВЫЕ стандартные и ТВ-параметры, которые содержит массив $data. Также увидишь их ключи (как к ним обращаться).

                События OnBefore...FormSave генерируются до физического сохранения новых значений и только в обработчиках этого события можно одновременно получить доступ и к старым значениям полей, и к новым. Именно для этого в OnBefore...FormSave и передаётся массив новых значений $data, которые будут физически сохранены после этого события.

                P.S. У варианта OnBeforeDocFormSave + массив data + getTVValue пока не вижу никаких ограничений. Полная свобода.
                  Cyrax_02
                  17 января 2015, 02:32
                  0
                  1. От Сергея modx.pro/help/4600/#comment-33447
                  OnDocFormRender не выполняется при сохранении значения, только при перезагрузке страницы (вполне логично).

                  Именно поэтому, если в период между загрузкой страницы ресурса и сохранением ресурса сессия истечёт, то при сохранении ресурса (будет запрошена авторизация) в массиве $_SESSION значение discount_before установлено не будет.

                  Более того, если другой пользователь (с другой сессией) изменит значение скидки в период между загрузкой страницы ресурса и сохранением ресурса, то в OnDocFormRender будут получены старые значения (имевшие место в момент открытия страницы ресурса). Впрочем, в вашем случае, к проблемам это не приведёт, но во многих других задачах, где требуется обязательно получать актуальные на текущий момент значения, этот факт делает данный способ получения старых (исходных) значений неприменимым.

                  2. От Василия modx.pro/help/4600/#comment-33439
                  $_POST тоже приходит только при перезагрузке страницы + приходится пользоваться переменной сессии.

                  В массиве $_POST данные передаются именно в момент сохранения ресурса (скрипт сохранения вызывает коннектор путём отправки самостоятельного http-запроса к серверу).

                  Для получения старых (исходных) значений необходимо:
                  а) в OnBeforeDocFormSave — метод getTVValue()
                  б) в OnDocFormSave — только через ранее «запомненные» значения (например, через переменные сессии)

                  В любом случае, как я уже говорил, новых значений в POST-параметрах запроса не будет, если ресурс сохраняется из собственного кода. Кроме того, от версии к версии формат передаваемых в $_POST данных может меняться, поскольку эти данные предназначены исключительно для внутренних нужд modx (для коннекторов).
                    Владислав
                    17 января 2015, 13:44
                    0
                    Cyrax_02 , спасибо за развернутый ответ.

                    Попробовал ваш метод — действительно, все просто.
                    Только вот проблема теперь с shopkeeper'ом (тестировал на нескольких сайтах).
                    Непонятно, почему, но
                    switch ($modx->event->name) {            
                                case 'OnBeforeDocFormSave':
                                    $discount = (float)$resource->getTVValue(13);                
                                    $modx->log(modX::LOG_LEVEL_ERROR, '$discount: ' . $discount);                
                                    break;
                            }
                    выдает ошибку "../www/core/components/shopkeeper/elements/tv/output/".
                    Отписал разработчику shopkeeper'a. После устранения проблемы буду использовать ваш метод.
                      Cyrax_02
                      17 января 2015, 18:40
                      0
                      Текст ошибки?
                Дмитрий Храмов
                15 июня 2017, 09:46
                0
                Подскажите, делаю плагин на событие OnDocFormSave, для выбора товара с таким же алиасом из контекста web.
                Для автоматического заполнения опции price_oct_day

                <?php
                if ($modx->event->name != "OnDocFormSave" || $resource->get('class_key') != 'msProduct' ) return;
                
                
                    $web_uri = $resource->get('alias');
                if (empty($resource->get('price_oct_day')))
                {
                
                    $res = $modx->getObject('msProduct', array('context_key' => 'web', 'alias' => $web_uri));
                    $tv_auto = $res->get('price_oct_day');
                    $resource->setTVvalue('price_oct_day', $tv_auto);
                }
                $resource->save();
                Но при сохранении — тв поле price_oct_day не меняется =(
                Подскажите где косяк?
                  Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                  20