Наверх

Часть 3 (Практика). Массовое редактирование объектов

Задание было таким:
Задание. Представьте, что вы сделали клиенту сайт, он его заполнил, в раздел «Статьи» уже выложил около 50 статей, после чего звонит вам и просит, чтобы в разделе статьи URL были такими:
/articles/23/
то есть, чтобы псевдонимом у статей был id ресурса. Напишите код, который надо выполнить в консоли, чтобы у всех существующих статей исправить псевдонимы.

Для более продвинутых пользователей — напишите плагин, который будет устанавливать нужное значение для всех создаваемых документов в разделе «Статьи»

Итак, первая часть задания.

Для того, чтобы у нескольких ресурсов изменить значение какого-то поля, нужно их сначала получить:
$resources = $modx->getCollection('modResource',array('parent' => 22));
и после этого внести изменения:
$resources = $modx->getCollection('modResource',array('parent' => 22));
foreach ($resources as $res) {
    $res->set('alias', $res->get('id'));
    $res->set('isfolder', 1);
    $res->save();
}
Заодно и сделали все статьи контейнерами, чтобы URL заканчивался не на .html, а на /

Теперь вторая часть задачи.

Здесь мы уже определили, что при создании плагина надо указать, что он срабатывает на событие OnDocFormSave:
/* Сначала проверяем на всякий случай,
   нужное ли нам событие произошло и что ресурс находится в "статьях" */
if ($modx->event->name != "OnDocFormSave"
    || $resource->get('parent') != 22) {return;}
// Устанавливаем новые значения
$resource->set('alias', $resource->get('id'));
$resource->set('isfolder', 1);
// и сохраняем объект
$resource->save();
return;


28 комментариев

  1. Дмитрий 18 сентября 2015, 14:04 # 0
    А можно программно вызвать событие OnDocFormSave для ресурса? Если да, то можете показать как?
    1. Илья Уткин 18 сентября 2015, 14:21 # 0
      $modx->invokeEvent('OnDocFormSave', array(
                  'mode' => 'new', // или upd
                  'id' => $id, // id ресурса
                  'resource' => $resource, // сам ресурс
                  'reloadOnly' => false
      ));
      1. Дмитрий 19 сентября 2015, 05:06 # 0
        А что должна содержать переменная $resource?
        1. Илья Уткин 19 сентября 2015, 08:53 # 0
          Объект ресурса. Например, так
          $resource = $modx->getObject('modResource', $id);
          1. Игорь Сидлярук 01 декабря 2017, 08:02 # 0
            Илья, плиииззз (((((
        2. Игорь Сидлярук 30 ноября 2017, 18:21 # 0
          Илья, помоги (
          Необходимо пересохранять ресурсы
          Вложенность ресурсов 5 уровней и больше. Указывая один корневой раздел и условие по id шаблона (независимо от уровня вложенности) обновить/пересохранить. Не подскажете?
          ====
          То есть раздел, у которого id=2 и внутри него пересохранить все ресурсы, у которых template=4
          ====
          Должно, вроде бы, работать, но Consloe не может завершить, и висит на «Loading...»
          <?php
          ini_set('max_execution_time', 0);
          ignore_user_abort(true);
          $c = 'modResource';
          $q = $modx->newQuery($c);
          $resources = $modx->getChildIds(2, 10, array('context'=>'web'));
          $q->where(array('id:IN' =>$resources));
          $q->where(array('template' => 4));
          $q->sortby('id');
          $q->select(array(
            "{$c}.*",
          ));
          $s = $q->prepare();
          $s->execute();
          while($row = $s->fetch(2)) {
            $response = $modx->runProcessor('resource/update', $row);
            if($response->isError()){
              print_r($response->getResponse());
              return;
            }
            $modx->error->reset();
          }
          Заранее большое спасибо.
          1. Илья Уткин 05 декабря 2017, 11:15 # 0
            Да, вроде, выглядит так, что должно работать… В чём проблема — не вижу. Попробуй написать вопрос на modx.pro/ — вдруг, там у кого-то будет идея.
            1. Игорь Сидлярук 05 декабря 2017, 11:29 # 0
              Да уже решил. За основу твой же сниппет Offset взял:
              <?php
              // Отвечаем только на Ajax
              if ($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
              
              // Можно передавать в скрипт разный action и выполнять разные действия.
              $action = $_POST['action'];
              if (empty($action)) {return;}
              
              // Получаем от клиента номер итерации
              $offset = $_POST['offset'] ? (int) $_POST['offset'] : 0;
              $limit = 10;
              
              // Составляем список ресурсов для обработки в текущей итерации
              $q = $modx->newQuery('modResource', array('parent' => 2));
              $q = $modx->newQuery('modResource', array('template' => 4));
              $q->sortby('id');
              
              $s = $q->prepare();
              
              // Узнаем, сколько всего ресурсов
              $count = $modx->getCount('modResource', $q);
              
              // Обрабатываем только часть, пропуская уже обработанные
              $q->limit($limit,$offset);
              
              $q->prepare();
              $q->stmt->execute();
              $res = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
              
              foreach ($res as $v) {
                  $properties = array(
                      'id' => $v['modResource_id']
                    , 'context_key' => 'web'
                    );
              
                  // Теперь можно, напирмер, поменять псевдоним ресурса
                  $properties['alias'] = $v['modResource_pagetitle'];
                  $response = $modx->runProcessor('resource/update', $properties);
              }
              
              // Проверяем, все ли строки обработаны
              $offset = $offset + $limit;
              if ($offset >= $count) {
                  $sucsess = 1;
              } else {
                  $sucsess = round($offset / $count, 2);
              }
              
              // И возвращаем клиенту данные (номер итерации и сообщение об окончании работы)
              $output = Array('offset' => $offset, 'sucsess' => $sucsess);
              echo json_encode($output);
              die();
      2. Дмитрий 20 сентября 2015, 16:15 # 0
        Слушай, не мог бы помочь?
        У меня есть ресурсы с товаром(у них шаблон с ID=4), на странице есть два поля, price_dollar(цена в долларах) и price(цена в рублях). А на главной странице есть curs(курс рубля). Что надо написать в плагине, что бы при смене курса поле цены в рублях пересчитывалось у всех ресурсов с ID=4?
        Пробовал сделать, но почему-то не работает… вот код:
        <?php
        if ($modx->event->name != "OnDocFormSave"
            || $resource->get('template') != 2) {return;}
        $where = array('template' => 4);
        $res = $modx->getCollection('modResource',$where);
        foreach ($res as $resource) {
        $id = $resource->get('id');
        $wh1 = array(
                'contentid' => $id
              , 'tmplvarid' => 25
            );
        $tv1 = $modx->getObject('modTemplateVarResource', $wh1);
        $doll = $tv1->get('value');
        $wh2 = array(
                'contentid' => 1
              , 'tmplvarid' => 26
            );
        $tv2 = $modx->getObject('modTemplateVarResource', $wh2);
        $curs = $tv2->get('value');
        $newdoll = $doll * $curs;
        $resource->setTVValue('price',$newdoll)
        }
        return;
        1. Илья Уткин 21 сентября 2015, 11:14 # 0
          Вроде, все верно… Попробуй в конце дописать $resource->save (перед последней фигурной скобкой).

          А вообще, давай, попробуем по-проще написать.
          <?php
          if ($modx->event->name != "OnDocFormSave"
              || $resource->get('template') != 2) {return;}
          // Сначала узнаем курс
          $mainpage = $modx->getObject('modResource', 1);
          $curs = $mainpage->getTVValue('curs');
          // Потом выберем все товары
          $where = array('template' => 4);
          $res = $modx->getCollection('modResource',$where);
          // Ну и каждому товару укажем новую цену
          foreach ($res as $product) {
            $price = $product->getTVValue('price_dollar');
            $product->setTVValue('price', $price * $curs);
          }
          
          return;
        2. Виталий 04 марта 2016, 09:28 # 0
          Добрый день.
          Спасибо за статьи, начал только изучать modx. Очень помогли. Подскажите, мне нужно обработать 2 шаблона ($where = array('template' => 4);$where = array('template' => 8);)
          Как загнать в один массив для обработки?
          1. Илья Уткин 04 марта 2016, 09:38 # 0
            1. Виталий 04 марта 2016, 09:52 # 0
              Спасибо. То что надо.
          2. Евгений 05 июля 2016, 23:22 # 0
            Илья, здравствуйте!
            Прошу Вашей помощи.
            На сайте компании сделан пересчёт цены товара в зависимости от курса валюты. Пересчёт происходит при сохранении карточки товара. Часто приходится импортировать товар, соответственно он появляется без цены, нужно заходить в каждый товар и нажимать сохранить. Мне посоветовали использовать следующий скрипт:
            <?php
            
            foreach($modx->getIterator('modResource',array('template' => 4)) as $res){
                $modx->invokeEvent('OnDocFormSave', array(
                    'mode' => 'upd',
                    'id' => $res->id,
                    'resource' => $res,
                    'reloadOnly' => false
                ));
            }
            


            Пересчёт действительно идёт, но ни с того вдруг затыкается на каком-то товаре и дальше пересчёт не идёт. Если зайти в товар с id следующим после последнего пересчитаного и сохранить карточку, то цена появляется. Но при дальнейшем запуске скрипта пересчёт всё равно уже не идёт:(
            Подскажите, пожалуйста, что можно предпринять в данном случае?
            Спасибо большое!
            1. Илья Уткин 06 июля 2016, 10:41 # 0
              Видимо, сервер просто не успевает. Попробуйте такой код:
              <?php
              // Сколько ресурсов обрабатывать за раз
              $step = 1;
              // Если процесс уже остановлен, сбрасываем OFFSET
              if (!isset($_SESSION['Console']['completed'])) {
                  $_SESSION['console_offset'] = 0;
              }
              $offset = isset($_SESSION['console_offset']) && $_SESSION['console_offset'] ? $_SESSION['console_offset'] : 0;
              // Формируем запрос
              $q = $modx->newQuery('modResource',array('template' => 4));
              $total = $modx->getCount('modResource', $q);
              // Пропускаем все уже обработанные объекты
              $q->limit($step, $offset);
              $resources = $modx->getCollection('modResource', $q);
              // Обработка
              foreach ($resources as $resource) {
                  print "<p>Processing resource <b>".$resource->get('pagetitle')."</b></p>";
                  $modx->invokeEvent('OnDocFormSave', array(
                      'mode' => 'upd',
                      'id' => $resource->id,
                      'resource' => $resource,
                      'reloadOnly' => false
                  ));
              }
              // Меняем offset
              $_SESSION['console_offset'] = $offset + $step;
              if ($_SESSION['console_offset'] >= $total) {
                $sucsess = 100;
                $_SESSION['Console']['completed'] = true;
              } else {
                $sucsess = round($_SESSION['console_offset'] / $total, 2) * 100;
                $_SESSION['Console']['completed'] = false;
              }
              for ($i=0; $i<=100; $i++) {
                  if ($i <= $sucsess) {
                      print '=';
                  } else {
                      print '_';
                  }
              }
              print "\n";
              print $sucsess.'% ('.$_SESSION['console_offset'].')'."\n\n";
              1. Евгений 06 июля 2016, 10:58 # 0
                Спасибо Вам огромное! Скрипт великолепен!
            2. Pavel 30 января 2017, 00:35 # 0
              Здравствуйте, что значит строка?
              if (.....|| $resource->get('parent') != 20) {return;} 
              что делает resource? и get'parent' != 20 — это как я понимаю каждый раз false??? И что retuurn возвращает? Заранее, спасибо)
              1. Илья Уткин 06 февраля 2017, 09:19 # 0
                Ну там же комментарий как раз:
                /* Сначала проверяем на всякий случай,
                   нужное ли нам событие произошло и что ресурс находится в "статьях" */
                return не просто возвращает, а ещё и прекращает дальнейшее выполнение кода. Соответственно, если условие не верно, дальше код выполнен не будет
              2. Kimoncar 30 апреля 2017, 05:29 # 0
                А вот так меняем шаблон всех ресурсов из категории 19, на шаблон с номером 4
                $resources = $modx->getCollection('modResource',array('parent' => 19));
                foreach ($resources as $res) {
                    $res->set('template', 3);
                    $res->save();
                }
                1. man 05 мая 2017, 14:17 # +1
                  на шаблон с номером 4
                  на шаблон с номером 3
                  1. Kimoncar 05 мая 2017, 14:34 # 0
                    Точно!) спасибо
                2. Кирилл 09 марта 2018, 01:26 # 0
                  Подскажите как получить поля-опции minishop2, которые msProductOptions у всех товаров и прописать в introtext, т.е. тоже что ниже только вместо id мне надо получить поле опции, мною созданное, с ключом 'view'
                  $resources = $modx->getCollection('modResource',array('parent' => 22));
                  foreach ($resources as $res) {
                      $res->set('introtext', $res->get('id'));
                      $res->save();
                  }
                  
                  1. Илья Уткин 19 марта 2018, 09:34 # 0
                    Так сходу не смогу сказать. Вам нужно смотреть в XML-схему miniShop2, найти там правильное название объекта, который хранит значение опции и составить правильный запрос для getObject. Тогда, получив этот объект, можно будет и получить значение опции.
                  2. Кирилл 22 марта 2018, 11:20 # 0
                    Может кому пригодится. спасибо за уроки Илья!!! Большое!
                    <?php
                    $resources = $modx->getCollection('modResource',array('parent' => 4)); 
                    foreach ($resources as $res) {
                        $id = $res->get('id');
                        $product = $modx->getObject('msProduct', $id);  //Получаем объект класса msProduct
                        $options = $product->getMany('Options'); //Так как композитная связь один ко многим, то применяем getMany()
                        //Прокодим циклом по опциям
                        foreach ($options as $option) {
                            //Ищем поле с нашим ключем
                            if($option->get('key') == 'drive'){
                                //Проверяем на пустоту
                                if($option->get('value') != ''){
                                $drive = $option->get('value'); //Получаем значение опции с ключом drive например
                                $pagetitle = $res->get('pagetitle'); //Получаем поле с заголовком
                                $res->set('introtext', 'Текст '.$drive.' текст '.$pagetitle.' текст.');
                                $res->save();
                                }else{
                                    echo 'Не заполнено drive';
                                    continue;
                                }
                            }
                        }
                    }
                    
                    1. Сергей 07 апреля 2018, 14:53 # 0
                      Илья что то не получилось.
                      1) Я создал контейнер статьи с айди7 и поместил туда несколько дочерних ресурсов.
                      2) В «элементы — плагин — создать плагин» добавил плагин и назвал его test
                      3) Написал туда код
                      /* Сначала проверяем на всякий случай,
                         нужное ли нам событие произошло и что ресурс находится в "статьях" */
                      if ($modx->event->name != "OnDocFormSave"
                          || $resource->get('parent') != 7) {return;}
                      // Устанавливаем новые значения
                      $resource->set('alias', $resource->get('id'));
                      $resource->set('isfolder', 1);
                      // и сохраняем объект
                      $resource->save();
                      return;
                      4) Начал создавать ресурсы алиас не меняется.
                      Пришел к выводу что то не работает.
                      Что я сделал не так?
                      1. Илья Иванов 23 мая 2018, 22:29(Комментарий был изменён) # +1
                        Как вариант, может быть не выбрано системное событие, при котором отрабатывается плагин — prntscr.com/jlr3aw.
                      2. Rus 05 июня 2018, 13:29(Комментарий был изменён) # 0
                        Привет. Вот скрипт для редактирования одного документа. Подскажите, пожалуйста, как сделать массовое редактирование документов (изменение типа публикации, изменения контента) с поиском по заголовкам.

                        <?php
                        // Выполняем поиск по заголовку
                        $resource = $modx->getObject('modResource', array('pagetitle' => 'Заголовок 1', 'pagetitle' => 'Заголовок 2'', 'pagetitle' => 'Заголовок 3'));
                        if (is_object($resource)) {
                        // преобразуем объект в массив
                        $resource = $resource->toArray();
                        // изменим публикацию (опубликована/не опубликована) страницы
                        $resource[«published»] = '1';
                        // выполнение процессора
                        $response = $modx->runProcessor('resource/update', $resource);

                        // если ошибка
                        if($response->isError()){
                        echo «Произошла ошибка». $response->getMessage();
                        }
                        else{
                        echo «Документ изменен»;
                        }
                        } else {
                        echo «Документ не найден»;
                        }
                        1. Илья Уткин 06 июня 2018, 13:13 # 0
                          Вот как раз статья с нужной информацией: Метод getCollection

                        Авторизация

                        через сервис Loginza:


                        Шаблоны MODX

                        1 2 Дальше »

                        Объектная
                        модель
                        MODX