[РЕШЕНО] Вопрос для академиков

Сразу оговорюсь, никакого сарказма, на мой взгляд задача действительно должна быть известна тем кто получил академическое образование по специальности программирование. Собственно задача.
Дан массив
[246 => 1, 267 =>2, 296 => 3, 308 => 4, 309 => 5]
Его ключи это id ресурсов, а значения отвечают за их сортировку, пусть будет menuidex. Нужно, чтобы при изменении значения например с ключом 308 с 4 на 2 получился массив
[246 => 1, 267 => 3, 296 => 4, 308 => 2, 309 => 5].
Я написал такое рашение
<?php
switch ($modx->event->name) {
    case 'OnDocFormSave':
        $resource->set('publishedon', time());
        $resource->save();
        if($resource->get('class_key') == 'Ticket'){
            $curPosition = $resource->getTVValue('position');
            $positions = [];
            if($resource->get('parent') == 73){
                $resources = $modx->getIterator('modResource', array('class_key' => 'Ticket', 'id:!=' => $id, 'parent' => 73));
            }
            else{
                $resources = $modx->getIterator('modResource', array('class_key' => 'Ticket', 'id:!=' => $id, 'parent:!=' => 73));
            }
            foreach($resources as $res){
                $pos = $res->getTVValue('position');
                if($pos){
                    $positions[$res->get('id')] = $pos;
                }
            }
            
            if(in_array($curPosition,$positions)){
                asort($positions);
                foreach($positions as $id => $pos){
                    if($pos >= $curPosition){
                        $modx->log(1, print_r($curPosition,1));
                        $modx->log(1, print_r($pos,1));
                        $res = $modx->getObject('modResource', $id);
                        $res->setTVValue('position', $pos + 1);
                        $res->save();
                        $curPosition = $pos + 1;
                    }
                }
            }
            //$modx->log(1, print_r($positions,1));
        }
    break;
}
Но кажется мне, что в нём что-то не так, но вот что именно не могу понять.
Артур
28 января 2021, 09:42
modx.pro
300
+1
Поблагодарить автора Отправить деньги

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

Наумов Алексей
28 января 2021, 10:44
+1
Нам нужно старое значение menuindex и новое.

Если новое меньше старого, то делаем sql запрос, в котором у ресурсов (только с тем же parent конечно) с menuindex от нового до старого делаем menuindex=menuindex+1.

Если старое меньше нового — наоборот.

Вот пример кода из какого-то процессора (там не ресурсы, а свои объекты).

if ($source->get('priority') < $target->get('priority')) {
            $this->modx->exec("UPDATE ".$tableName."
				SET priority = priority - 1 WHERE
					priority <= {$target->get('priority')}
					AND priority > {$source->get('priority')}
					AND priority > 0
			");

        } else {
            $this->modx->exec("UPDATE ".$tableName."
				SET priority = priority + 1 WHERE
					priority >= {$target->get('priority')}
					AND priority < {$source->get('priority')}
			");
        }
    Артур
    28 января 2021, 10:51
    0
    А где взять старое значение? Изменение делает пользователь, сохраняет и только потом срабатывает плагин.
      Alexey
      29 января 2021, 09:44
      0
      Если плагин повесить на OnBeforeDocFormSave, то можно обойтись без промежуточного TV

      <?php
      $eventName = $modx->event->name;
      switch($eventName) {
          case 'OnBeforeDocFormSave':
              $menuindex = $modx->getObject('modResource', $id)->get('menuindex');
              $new_menuindex = strip_tags($_POST['menuindex']);
              
              $modx->log(1, print_r($menuindex, 1));
              $modx->log(1, print_r($new_menuindex, 1));
              
              break;
      }

      В этом случае нужно помнить, что не срабатывает сохранение TV-поля из плагина. Но оно в данном случае нам и не нужно.
        Артур
        29 января 2021, 10:39
        0
        Почему вы считаете, что TV не сохраняется из плагина? У меня не далее как вчера сохранялась. И второй момент, почему вы считаете, что TV не нужно, ведь значение menuidex сохраняемого ресурса меняет не плагин, а пользователь, т.е. в плагин приходит уже новое значение.
          Alexey
          29 января 2021, 10:44
          0
          >>>У меня не далее как вчера сохранялась.
          Серьезно? На событие OnBeforeDocFormSave? skrinshoter.ru/s/290121/E2iWCCxq

          >>>т.е. в плагин приходит уже новое значение.
          В плагин на событие OnBeforeDocFormSave приходит новые значение, а вы заметили, откуда я извлекаю старое? Пытались мой пример протестить?
            Артур
            29 января 2021, 10:58
            0
            Сорри, слово before не заметил)))
              Артур
              29 января 2021, 11:00
              0
              И вообще TV на самая большая проблема в этой задаче, самая большая проблема это я, потому что я не понимаю что не так, в голове и на бумаге логика безупречна, а код работает не правильно.
                Alexey
                29 января 2021, 15:25
                0
                Если честно, я не сильно вчитывался в код, приведенный в условии. Но я бы в принципе по-другому пути пошел.

                1. Вначале вытащил бы в отдельный массив данные нужных ресурсов (ключ-id, значение-menuindex)
                2. Сохранил бы в отдельные переменные новый menuindex и его id (в плагине он и так доступен)
                3. И спокойно бы раскурочил массив из п.1 так как нужно — не в пример нагляднее, чем перебирать ресурсы. Есть методы сортировки, array_flip и так далее..
                4. Полученный в результате «пыток» в п.3 массив должен содержать: ключ — id ресурса, значение — новое значение menuindex, которое соответствует условиям
                5. Сохраняем menuindex из массива в ресурсы, id которых в ключе

                Тут всё наглядно, при работе с массивом можно каждый шаг дебажить и тестить. А так с наскока — перебирая ресурсы — мне, к примеру, тяжеловато сообразить
                  Артур
                  29 января 2021, 16:01
                  0
                  Ну суть та же, только я хотел обойтись без промежуточного массива. По факту
                  $resources = $modx->getIterator('msProduct', array('parent' => $resource->get('parent')));
                  Массив для пункта 1 можно вытащить только перебрав $resources. И да, с массивом работать удобнее, но п.3 это проблема. Давайте псевдокод напишу
                  $oldValue // старое значение позиции текущего ресурса
                  $newValue // новое значение
                  $resources // массив со всеми значениями [id => menuindex]
                  //По идее надо перебрать $resources предварительно сравнив $oldValue и $newValue
                  if($newValue < $oldValue){
                  foreach($resources as $r){
                  //надо прибавить  1 ко всем menuindex которые соответствуют  $newValue <= menuindex < $oldValue
                  }
                  else{
                  //надо отнять  1 ото всех menuindex которые соответствуют  $newValue > menuindex >= $oldValue
                  }
                  }
                  Но получается что если $newValue < $oldValue, то последнее значение не меняется.
      Артур
      28 января 2021, 17:36
      0
      Я переписал код, но он всё равно как-то не так работает
      <?php
      switch ($modx->event->name) {
          case 'OnDocFormSave':
              $oldValue = $resource->getTVValue('old_value');
              $newValue = $resource->get('menuindex');
              $resource->setTVValue('old_value', $newValue);
              $resource->save();
              $resources = $modx->getIterator('msProduct', array('parent' => $resource->get('parent')));
              
              if($newValue < $oldValue){
                  foreach($resources as $res){
                      $menuindex = $res->get('menuindex');
                     
                      if($menuindex >= $newValue && $menuindex < $oldValue && $res->get('id') != $id){
                          $res->set('menuindex', $menuindex + 1);
                          $res->set('old_value', $menuindex + 1);
                      }
                      
                      $res->save();                
                  }
              }
              else{
                  foreach($resources as $res){
                      $menuindex = $res->get('menuindex');
                     
                      if($menuindex > $newValue && $menuindex <= $oldValue && $res->get('id') != $id){
                          $res->set('menuindex', $menuindex - 1);
                          $res->set('old_value', $menuindex - 1);
                      }
                      
                      $res->save();                
                  }    
              }
          break;
      }
        Артем
        29 января 2021, 18:21
        0
        Зачем высчитывать все эти расхождения, если можно просто заново проиндексировать массив на основе нового значения?
        Я предполагаю, что в этом массиве у тебя собраны сразу все необходимые id:
        <?php
        
        $array = [246 => 1, 267 => 2, 296 => 3, 308 => 4, 309 => 5];
        $affectedResource = 308;
        $newIndex = 2;
        
        $array[$affectedResource] = $newIndex - 0.5;
        asort($array);
        $index = 1; // индексирование будет начато с единицы
        foreach ($array as $id => &$oldIndex) {
            $oldIndex = $index;
            $index++;
        }
        
        return $array;

        Если тебе не принципиально, чтобы первый индекс был единицой, то можно еще проще:
        <?php
        
        ...
        asort($array);
        return array_flip(array_keys($array));
          Артур
          29 января 2021, 18:39
          0
          Спасибо всем участвующим. Будем считать вопрос решённым.
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          12