Вывести только тикеты с рейтингом больше нуля

1 300
Итак, если вам нужно вывести тикеты с рейтингом выше ноля, то эту задачу можно решить двумя способами. Первый для высоконагруженных проектов, второй для менее нагруженных.

Первый вариант (относительно сложный в реализации, но выигрывает по скорости при большой выборке ресурсов):

  1. Заходите в phpmyadmin.
  2. Выбираете таблицу modx_site_content
  3. — Переходите в закладку «структура»
  4. — Внизу открывшегося окна нажимаете ОК (Добавить 1 поле в конец таблицы)
    Имя: ticket_rating
    Тип: int
    Длина/значение: 10
    По умолчанию: Как определено (0)
    Null: галочка
  5. Сохраняем
Таким способом мы расширили дефолтную таблицу MODX'a со списком ресурсов.

Далее:

Создаем плагин (допустим, customTicketRating) со следующим содержанием:
<?php
    switch ($modx->event->name) {
    	
    	case 'OnMODXInit':
    		$modx->loadClass('modResource');
    		$modx->map['modResource']['fields']['ticket_rating'] = 0;
    		$modx->map['modResource']['fieldMeta']['ticket_rating'] = array(
    			'dbtype' => 'int',
    			'precision' => 10,
    			'attributes' => '',
    			'phptype' => 'int',
    			'null' => true,
    			'default' => 0,
    		);
    		break;
    		
    		case 'OnTicketVote':
    			if ($object->class == 'Ticket') {
    				if ($ticket = $modx->getObject('Ticket', $object->id)) {
    					$properties = $ticket->getProperties('tickets');
    					$rating = !empty($properties['rating'])
    						? $properties['rating']
    						: 0;
    					$ticket->set('ticket_rating', $rating + $object->value);
    					$ticket->save();
    				}
    			}
    			break;
    }
«Вешаем» его на события OnTicketVote и OnMODXInit. Теперь наше новое поле воспринимается модксом и изменяется после каждого голосования за любой ресурс.

Теперь осталось только вызвать сниппет с нужным условием:
[[!pdoPage?
	&element=`getTickets`
	&where=`{"ticket_rating:>": 1}`
]]
Важно: рейтинг начнет вычисляться только с последующих голосований за тикеты.

За решение благодарим modx.pro/users/kaminari/

Второй вариант более простой в реализации и подойдет, если вам нужно выбрать тикеты из относительно небольшого кол-ва ресурсов:

1. Создаем сниппет:

<?php
$class = 'modResource';
$pdo = $modx->getService('pdoFetch');
$pdo->addTime('pdoFetch загружен');

$options = array(
	'cacheTime' => '10',
);

if (!$output = $pdo->getCache($options)) {
	$pdo->addTime('Кэш не найден, генерируем данные');
	
    $pdo->setConfig(array(
    	'class' => $class,
    	'select' => 'id, properties',
    	'limit' => 0,
    	'parents' => 0,
    	'return' => 'data'
    ));
    
    $data = $pdo->run();
    
    $output = array();
    foreach ($data as $row) {
        if (isset($row['properties']['tickets']['rating']) && $row['properties']['tickets']['rating'] > 1) {
            $output[] = $row['id'];
        }
    }
    $output = implode(',', $output);
	$pdo->setCache($output, $options);
	$pdo->addTime('Данные сохранены в кэш');
} else {
	$pdo->addTime('Данные загружены из кэша');
}
$pdo->addTime('Данные получены');
// print $pdo->getTime();
return $output;
Здесь указано, сколько в секундах хранить кеш:

$options = array(
	'cacheTime' => '10',
);
А здесь — больше какого значения должен быть рейтинг у ресурсов:

if (isset($row['properties']['tickets']['rating']) && $row['properties']['tickets']['rating'] > 1) {
            $output[] = $row['id'];
        }
2. Передаем результат его работы в getTickets или другой используемый сниппет, который принимает параметр resources. Например:

[[getTickets?
	&parents=`0`
	&resources=`[[имя сниппета с кодом выше]]`
]]
За вариант благодарим modx.pro/users/1801/
Wassi Wassinen
26 февраля 2016, 03:31
modx.pro
10
3 765
+2

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

Григорий Коленько
26 февраля 2016, 18:47
0
Класс. Идея Василию. Добавить раздел «вопросы за 300». С автоматической отправкой победителю выигрыша ) и 10% на развития портала.
    Wassi Wassinen
    26 февраля 2016, 21:24
    0
    Я готов играть в эту игру. :) Лишь бы результат был.
    Максим Кузнецов
    26 февраля 2016, 18:53
    0
    Насколько я понял, раньше в тикетах было присоединение таких данных к выборке по-умолчанию, но создавало проблемы с производительностью — поэтому от нее отказались.

    Поэтому, вот этот вариант, пожалуй, самый правильный для хранения таких данных. А дальше дело только за
    &where=`{"ticket_rating:>": 1}`
      Wassi Wassinen
      26 февраля 2016, 21:25
      0
      Максим, я прошу прощения, не силен в этих символах. Я за копипаст и проверку на производительность debugParser'ом. Если у вас есть возможность, напишите «от» и «до» в комментариях и я готов заплатить. Благодарю!
        Максим Кузнецов
        27 февраля 2016, 00:18
        2
        +2
        — Заходите в phpmyadmin.
        — Выбираете таблицу modx_site_content
        — Переходите в закладку «структура»
        — Внизу открывшегося окна нажимаете ОК (Добавить 1 поле в конец таблицы)
        Имя: ticket_rating
        Тип: int
        Длина/значение: 10
        По умолчанию: Как определено (0)
        Null: галочка
        (сохраняем)

        Таким способом мы расширили дефолтную таблицу MODX'a со списком ресурсов. Далее:
        — Создаем плагин (допустим, customTicketRating) со следующим содержанием:
        <?php
            switch ($modx->event->name) {
            	
            	case 'OnMODXInit':
            		$modx->loadClass('modResource');
            		$modx->map['modResource']['fields']['ticket_rating'] = 0;
            		$modx->map['modResource']['fieldMeta']['ticket_rating'] = array(
            			'dbtype' => 'int',
            			'precision' => 10,
            			'attributes' => '',
            			'phptype' => 'int',
            			'null' => true,
            			'default' => 0,
            		);
            		break;
            		
            		case 'OnTicketVote':
            			if ($object->class == 'Ticket') {
            				if ($ticket = $modx->getObject('Ticket', $object->id)) {
            					$properties = $ticket->getProperties('tickets');
            					$rating = !empty($properties['rating'])
            						? $properties['rating']
            						: 0;
            					$ticket->set('ticket_rating', $rating + $object->value);
            					$ticket->save();
            				}
            			}
            			break;
            }
        и включаем его на события OnTicketVote и OnMODXInit. Теперь наше новое поле воспринимается модксом и изменяется после каждого голосования за любой ресурс.

        Теперь осталось только вызвать сниппет с нужным условием:
        [[!pdoPage?
        	&element=`getTickets`
        	&where=`{"ticket_rating:>": 1}`
        ]]

        Важно: рейтинг начнет вычисляться только с последующих голосований за тикеты.
          Wassi Wassinen
          27 февраля 2016, 14:58
          0
          А для чего создавать отдельное поле для хранения этого значения у каждого ресурса. Разве тикеты не хранят его по дефолту?
            Владимир Кисилица
            27 февраля 2016, 15:18
            0
            Хранят, в поле properties вместе с остальными кастомными данными
              Wassi Wassinen
              27 февраля 2016, 15:22
              0
              То есть, по этому полю сортировка будет быстрее?
                Владимир Кисилица
                27 февраля 2016, 15:33
                0
                Нет, сортировка по данным в поле properties не реализована, и реализовывать ее, нет смысла, потому что данные в базе хранятся в json формате.
                  Wassi Wassinen
                  27 февраля 2016, 16:32
                  0
                  Владимир, я не про поле properties. В комментариях ниже уже увидел, что сортировка по вашему полю будет быстрее.
                    Владимир Кисилица
                    27 февраля 2016, 16:56
                    0
                    На самом деле, если объединить мое решение и решение Максима Кузнецова, скорость будет еще выше. Насколько наши решения отличаются по скорости сказать не могу, его решение, будет даже быстрее моего, если использовать для вывода сниппеты Василия. Я привел статистику выборки в которой участвуют 112 ресурсов.

                    Мое решение просто проще в реализации. Решение Максима для человека, который знает, что он делает и зачем.
            Максим Кузнецов
            27 февраля 2016, 15:26
            0
            Хранят. Но только в общем поле properties в формате json:
            {
            	"tickets": {
            		"disable_jevix":false,
            		"process_tags":false,
            		"rating":1,
            		"rating_plus":1,
            		"rating_minus":0
            	},
            	"ms2gallery": {
            		"media_source": "4"
            	}
            }
            Сортировка такого поля в естественном виде как минимум требует дополнительную выборку (как в примере ниже), а как максимум — весьма ощутимо проигрывает в скорости.
            (по этой же причине, например, очень нежелательно производить сортировку пользователей по extended-полям)

            К слову, на modx.pro хранение рейтинга реализована примерно таким же способом.
            Wassi Wassinen
            28 февраля 2016, 05:33
            0
            Максим, спасибо за вариант. Напишите через личку куда отправлять деньги.
              Wassi Wassinen
              29 февраля 2016, 12:53
              0
              Деньги отправил. Спасибо!
          Владимир Кисилица
          27 февраля 2016, 13:54
          0
          Код сниппета, который вернет id ресурсов, рейтинг которых больше 0.
          $class = 'modResource';
          
          $pdo = $modx->getService('pdoFetch');
          $pdo->setConfig(array(
          	'class' => $class,
          	'select' => 'id, properties',
          	'limit' => 0,
          	'parents' => 0,
          	'return' => 'data'
          ));
          $data = $pdo->run();
          
          $output = array();
          foreach ($data as $row) {
              if (isset($row['properties']['tickets']['rating']) && $row['properties']['tickets']['rating'] > 0) {
                  $output[] = $row['id'];
              }
          }
          $output = implode(',', $output);
          return $output;

          Передаешь результат его работы в getTickets или другой используемый сниппет, который принимает параметр resources. Например:
          [[getTickets?
          	&parents=`0`
          	&resources=`[[имя сниппета с кодом выше]]`
          ]]
          Можно еще попробовать закешировать результаты сниппета, допустим, на 5-10 минут, чтобы уменьшить нагрузку, если сайт популярный. Для кеширования можно использовать методы getCache и setCache класса pdoTools.
            Wassi Wassinen
            27 февраля 2016, 14:57
            0
            Опробую в течение дня. Спасибо.
              Сергей Шлоков
              27 февраля 2016, 15:30
              0
              Если выбирать по шустрости, то этот вариант проигрывает. У Максима выбираются нужные тикеты за один запрос. А у Владимира выбираются все тикеты, а потом вторым шагом формируется массив с id нужных тикетов. А потом нужно еще раз зачитать с этими айдишниками. Если к варианту Максима добавить индекс по этому полю, то на больших объемах разница между этими вариантами будет очень заметна.
              П.С. Ещё обрати внимание на ошибку. Класс должен быть Ticket, а не modResource.
              $class = 'Ticket';
              И еще нужно учитывать удаленные и опубликованные.
                Владимир Кисилица
                27 февраля 2016, 15:44
                0
                modResource, тоже хранят рейтинг, смотря как это все реализовано на сайте. Мой вариант действительно, проигрывает по скорости, но он не требует написания плагинов, которые будут кастомизировать карты классов при инициализации MODX

                Еще один вариант с использованием кеша:

                $class = 'modResource';
                $pdo = $modx->getService('pdoFetch');
                $pdo->addTime('pdoFetch загружен');
                
                $options = array(
                	'cacheTime' => '10',
                );
                
                if (!$output = $pdo->getCache($options)) {
                	$pdo->addTime('Кэш не найден, генерируем данные');
                	
                    $pdo->setConfig(array(
                    	'class' => $class,
                    	'select' => 'id, properties',
                    	'limit' => 0,
                    	'parents' => 0,
                    	'return' => 'data'
                    ));
                    
                    $data = $pdo->run();
                    
                    $output = array();
                    foreach ($data as $row) {
                        if (isset($row['properties']['tickets']['rating']) && $row['properties']['tickets']['rating'] > 1) {
                            $output[] = $row['id'];
                        }
                    }
                    $output = implode(',', $output);
                	$pdo->setCache($output, $options);
                	$pdo->addTime('Данные сохранены в кэш');
                } else {
                	$pdo->addTime('Данные загружены из кэша');
                }
                $pdo->addTime('Данные получены');
                // print $pdo->getTime();
                return $output;

                Без кеша:
                0.0000479: xPDO query object created
                0.0037699: Added selection of <b>modResource</b>: <small>SQL_CALC_FOUND_ROWS `id`, `properties`</small>
                0.0000160: Processed additional conditions
                0.0001459: Sorted by <b>modResource.id</b>, <b>ASC</b>
                0.0001490: SQL prepared <small>"SELECT SQL_CALC_FOUND_ROWS `modResource`.`id`, `modResource`.`properties` FROM `modx_site_content` AS `modResource` ORDER BY modResource.id ASC "</small>
                0.0010781: SQL executed
                0.0000989: Total rows: <b>112</b>
                0.0001380: Rows fetched
                0.0003340: Returning raw data
                0.0011299: Saved data to cache "default//055045fd31203f5f6ebcd7e3c93408e60bdf372a"
                0.0000112: Данные сохранены в кэш
                0.0000038: Данные получены
                0.0074821: <b>Total time</b>
                4 456 448: <b>Memory usage</b>

                С кешем:
                0.0001650: pdoFetch загружен
                0.0002382: Retrieved data from cache "default//055045fd31203f5f6ebcd7e3c93408e60bdf372a"
                0.0000088: Данные загружены из кэша
                0.0000021: Данные получены
                0.0004170: <b>Total time</b>
                3 145 728: <b>Memory usage</b>

                В итоге, не нужно ничего кастомизировать.

                P.S. Насчет «И еще нужно учитывать удаленные и опубликованные» не соглашусь. Нам нужно вернуть id ресурсов которые имеют положительный рейтинг больше единицы. А уж решать выводить неопубликованные или удаленные, должен сниппет, которому передаются эти id, у него для этого есть логика
                  Wassi Wassinen
                  28 февраля 2016, 05:32
                  0
                  Спасибо! Мне понравились оба варианта. Ваш и modx.pro/users/kaminari/. Поэтому, готов заплатить обоим. Напишите в личку куда прислать деньги.
            Сергей Шлоков
            28 февраля 2016, 07:46
            0
            Брюки превращаются...
            Прикольно. Сначала задаешь вопросы, а потом переводишь их в пошаговые инструкции. ;)
              Wassi Wassinen
              28 февраля 2016, 17:34
              0
              Я плачу за ответы. :) И новичкам проще, когда ответ есть в шапке тикета. По себе знаю.
              Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
              23