Как уйти от TV и увеличить быстродействие MODX mFilter2?

Александр Здравствуйте! Вы в одном из комментариев отметили что для того, чтобы, скорость фильтрации mFilter2 возросла, необходимо чтобы значения tv сохранялись в отдельной таблице, озадачился этой темой но инфы по поводу этого просто ноль, не могли бы вы если это возможно, описать как создать отдельную таблицу и сохранять значения tv которые для фильтрации там а далее как указать фильтру эту таблицу?

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

Александр
Александр
Александр Здравствуйте! Столкнулся с проблемой решил не создавать отдельный вопрос, так как, есть предположение что проблема с дополнением msFavorites (Избранное), связана с расширением класса mfilter2.

В чанке deflaut, mFilter2, вызов избранного такой:
<a class="msfavorites"
           data-click
           data-data-list="default"
           data-data-type="resource"
           data-data-key="[[+id]]"
           data-msfavorites-animation="img/like.png"
           
        >
            <i class="msfavorites-icon-heart"></i>
        </a>
        <span class="msfavorites-total"
              data-data-list="default"
              data-data-type="resource"
              
        >0</span>
всё работает, ошибок нет, но проблема с множественной подгрузкой одного и того же файла, action.php, если открыть консоль браузера, раздел XHR, далее перезагрузить страницу с фильтром, будет видно, что грузиться один файл, action.php, это нормально, далее, если отметить один чекбокс в фильтре и перезагрузить страницу, будет видно что грузятся уже три одинаковых файла action.php, отмечаю в фильтре еще пару чекбоксов, перезагружаю страницу, грузятся уже 8 одинаковых файлов, чем больше в фильтре отмечено чекбоксов, тем больше одинаковых вызовов action.php, дополнения msFavorites.

Пытался убирать сторонние скрипты с шаблона, думал может в них проблема, грешил на дополнение, но тестанул и это походу не тот случай, так и не могу точно найти источник проблемы, остаётся только расширенный класс mFilter2. Александр, подскажите пожалуйста, что можно попробовать сделать в данном случае?

Скриншот консоли браузера раздела XHR:
Александр
Александр
Александр, баг с коннектором был в дополнении, сейчас всё работает, та версия с которой я тестил и пытался создать аналогичную проблему, была новая и проблемы с коннектором, там уже не было, поэтому был сделан ошибочный вывод, что причина не в msFavorites, а оказалось именно в нём.
Sergey
Sergey
Может есть способ проще, без создания таблицы… по типу как тут?
modx.pro/howto/13058
Александр Мальцев
Александр Мальцев
В случае с TV параметрами исключать нечего, поэтому наиболее корректный вариант – это оптимизировать данные в базе данных. Или вообще избавиться от них, и написать свои решения.
Sergey
Sergey
Скажите, а если уже имеется каталог с tv параметрами у mFilter2 этот способ поможет?
Александр
Александр
Да поможет, сейчас у вас фильтрует по дефолтной таблице а с этим вариантом будет по той которую создадите.
При редактировании ресурса, значения полей так же сможете изменять как и раньше, только в данном случае при заполнении тв у ресурса помимо дефолтной таблицы они будут дублироваться в новую таблицу. Уменьшиться количества запросов к базе и скорость фильтрации возрастёт. Перед тем как будите что-то менять, сделайте резервною копию базы данных и сайта. Способ отличный, спасибо Александру за подробное руководство.
Александр Мальцев
Александр Мальцев
Вот так можно сделать фильтр в mFilter2 для своей таблицы.

1. Создать файл \core\components\msearch2\custom\filters\custom.class.php:
<?php
class myCustomFilter extends mse2FiltersHandler {

  public function __construct(mSearch2 &$mse2, array $config = array()) {
    parent::__construct($mse2, $config);
    $this->modx->addPackage('extendResource' ,MODX_CORE_PATH.'components/extendresource/model/');
  }	

  public function getExtendresourceValues(array $fields, array $ids) {
    $filters = array();
    $q = $this->modx->newQuery('modResource', array('modResource.id:IN' => $ids));
    $q->leftJoin('extendResource', 'myTV',
      'myTV.resource = modResource.id'
    );
    $q->select('myTV.resource as id, myTV.tv1 as tv1, myTV.tv2 as tv2, myTV.tv3 as tv3');
    $tstart = microtime(true);
    if ($q->prepare() && $q->stmt->execute()) {
      $this->modx->queryTime += microtime(true) - $tstart;
      $this->modx->executedQueries++;
      while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        if (empty($row['id'])) {
          continue;
        } elseif ((is_null($row['tv2']) || trim($row['tv2']) == '') && (is_null($row['tv1']) || trim($row['tv1']) == '') && (is_null($row['tv3']) || trim($row['tv3']) == '')) {
          continue;
        }
        $tmp = strpos($row['tv2'], '||') !== false
          ? explode('||', $row['tv2'])
          : array($row['tv2']);
        foreach ($tmp as $v) {
          $v = str_replace('"', '"', trim($v));
          if ($v == '') {
            continue;
          }
          $name = 'tv2';
          if (isset($filters[$name][$v])) {
            $filters[$name][$v][$row['id']] = $row['id'];
          } else {
            $filters[$name][$v] = array($row['id'] => $row['id']);
          }
        }
        $tmp = strpos($row['tv1'], '||') !== false
          ? explode('||', $row['tv1'])
          : array($row['tv1']);
        foreach ($tmp as $v) {
          $v = str_replace('"', '"', trim($v));
          if ($v == '') {
            continue;
          }
          $name = 'tv1';
          if (isset($filters[$name][$v])) {
            $filters[$name][$v][$row['id']] = $row['id'];
          } else {
            $filters[$name][$v] = array($row['id'] => $row['id']);
          }
        }
        $tmp = strpos($row['tv3'], '||') !== false
          ? explode('||', $row['tv3'])
          : array($row['tv3']);
        foreach ($tmp as $v) {
          $v = str_replace('"', '"', trim($v));
          if ($v == '') {
            continue;
          }
          $name = 'tv3';
          if (isset($filters[$name][$v])) {
            $filters[$name][$v][$row['id']] = $row['id'];
          } else {
            $filters[$name][$v] = array($row['id'] => $row['id']);
          }
        }
      }
    } else {
      $this->modx->log(modX::LOG_LEVEL_ERROR, "[mSearch2] Error on get filter params.\nQuery: ".$q->toSQL()."\nResponse: ".print_r($q->stmt->errorInfo(),1));
    }
    return $filters;
  }

}
2. В системных настройках в разделе mSearch2 в параметре mse2_filters_handler_class указать свой класс-обработчик для фильтров:
myCustomFilter
3. Настроить сниппет mFilter2:
[[!mFilter2? 
  ...
  &loadModels=`extendresource`
  &filters=`extendresource|tv:default`
]]
Александр
Александр
Александр, огромнейшее Вам спасибо, за уникальную и ценнейшую информацию. Получилось вывести фильтры, фильтрация работает, но не могу понять кое какие моменты.

Вывожу фильтр так:

[[!mFilter2?
&tvFiltersOrDelimiter=`||`
&tpl_5=`tpl5th`
&parents=`0`
EmptyFilters=`1`
&tpls=`tpl_default`
&includeTVs=`data,tv1,tv2`
&noPreciseMSFilters=`1`
&forceSearch=`0`
&setMeta=`0` 
&suggestions=`1`
&showLog=`0`
&limit=`25`
&paginator=`pdoPage`
&ajaxMode=`button`
&toSeparatePlaceholders=`my.`
&loadModels=`extendresource`

&filters=`
extendresource|tv:default
`
&aliases=`
extendresource|tv1==tv1,
`
&tplFilter.outer.my.tv1=`tpl.outertv1`
&tplFilter.outer.tv|tv2=`tpl.mFilter2.filter.slider`
]]
1) Вот в этой строке: &includeTVs=`data,tv1,tv2` как правильно указать?

2) Не понятно как правильно указать чанк для группы фильтров: &tplFilter.outer.my.tv1=`tpl.outertv1`?

3) Сколько не пытался не получается вывести значения tv в результатах фильтра, создал сниппет, назвал getTVFields, поместил вызов в шаблон, где вызываю mfilter2:
[[!getTVFields? &id=`[[*id]]`]]
В чанке:
[[!+my.tv1]]
не выводит, подозреваю что не правильно указал тв в вызове фильтра:
&includeTVs=`data,tv1,tv2` ?
4) Как можно убедиться в том, что фильтрует именно по этой таблице а не дефолтной?

5) Группы фильтров вывожу так: [[+my.extendresource|tv2]], это правильный вариант?

6) Теперь если я правильно понимаю при добавлении нового тв, нужно править файл в этой строке:
$q->select('myTV.resource as id, myTV.tv1 as tv1, myTV.tv2 as tv2, myTV.tv3 as tv3');
и тут:
$tmp = strpos($row['tv1'], '||') !== false
          ? explode('||', $row['tv1'])
          : array($row['tv1']);
        foreach ($tmp as $v) {
          $v = str_replace('"', '"', trim($v));
          if ($v == '') {
            continue;
          }
          $name = 'tv1';
          if (isset($filters[$name][$v])) {
            $filters[$name][$v][$row['id']] = $row['id'];
          } else {
            $filters[$name][$v] = array($row['id'] => $row['id']);
          }
        }
6) Как я понимаю, если установлен сео-фильтр, чтобы он работал с этой таблицей, там нужно выбрать, (значение в другой таблице), как правильно там прописать, подскажите пожалуйста?


Александр
Александр
По одному из вопросов в предыдущем сообщении, касаемо своего чанка для каждой группы фильтров, проблем нет, у меня не работало потому-что, забыл прописать aliases, выводиться обычным способом:
&tplFilter.outer.tv1=`tpl.имя чанка`
Александр Мальцев
Александр Мальцев
Параметры «&includeTVs», «&tvFiltersOrDelimiter» и другие, связанные с TV не нужны. Иначе они будут включаться в запрос. Выборка данных осуществляется же с другой таблицы.
В этом случае сниппет нужно вызывать в чанке и в нём id записи уже будет плейсхолдер [[+id]]:
[[!getTVFields? &id=`[[+id]]`]]
Или вообще не использовать сниппет, а сделать так:
[[!mFilter2? 
  &loadModels=`extendresource`
  &leftJoin=`{
    "extendResource": {
      "class": "extendResource",
      "alias": "extFields", 
      "on": "extFields.resource = modResource.id"
    }
  }`
  &select=`{
    "extFields": "extFields.tv1 as tv1, extFields.tv2 as tv2, extFields.tv3 as tv3"
  }`
  ...
Поставьте debugParser и протестируйте время и количество запросов. Параметр «&filters» определяется почему будет фильтроваться.

При добавлении нового поля нужно добавить ещё в условие, где оно проверяется на пустоту или null.
Александр Мальцев
Александр Мальцев
В SeoFilter:
— компонент: extendresource
— класс: extendResource
— поле для сопоставления (id): resource
— поле, где хранится значение (name): tv1
Александр
Александр
Александр, по всем вопросам всё предельно понятно, спасибо. DebugParser, пользуюсь им и отслеживаю каждый килобайт и запрос, лишний надеюсь не проскочит:) но есть проблема только с выводом значений в результатах фильтра на сколько я понимаю, более а точнее менее затратный по ресурсам, это второй вариант: leftJoin, у меня не получается вывести, если я в чанке вызываю так: [[!+tv1]] то тянуться значения с дефолтной таблицы и не зависимо от того, есть ли в вызове mFilter2 ваш пример: leftJoin, проверил это ручным удалением с дефолтной таблицы значения. Если делаю так: [[!+extFields.tv1]] то не выводит не чего, очевидно что я просто не правильно делаю вызов?
Александр
Александр
SeoFilter при таком раскладе не хочет нормально работать, что с параметрами что без них не работает, а именно подсчёты не работают и хлебные крошки через ajax ну и в админке вкладка сео-страницы, название страниц не выводит, походу там тоже нужно расширять класс, чтобы работало нормально? Сейчас всё завязано на этом: modTemplateVar
Александр Мальцев
Александр Мальцев
SeoFilter не использовал, могу подсказать только навскидку.
Александр Мальцев
Александр Мальцев
Наиболее просто — это вывести все плейсхолдеры на экран и посмотреть, как они называются. Для этого параметру устанавливающему чанк для каждого результата нужно в качестве значения установить пустую строку:
&tpl = ``
Александр
Александр
Как я понимаю, если использовать свой класс в SeoFilter, там нужно расширять его, примерно так-же как и в mFilter2, тогда подсчёты будут работать, это так мысли в слух.
Александр
Александр
Спасибо! Мистика честно говоря, вызов обычный: [[!+tv1]] выводит и при чём выводит с новой таблицы, всё как нужно, это при том, что я удалил с дефолтной таблицы в ручную все значения, оставил только их в новой таблице, но не понятно почему тогда, когда значения находились в обоих таблицах, при таком вызове: [[!+tv1]] выводились значения с дефолтной таблицы, вообще такое теоретические возможно, при указанном &leftJoin, если значения в обоих таблицах то выводятся с дефолтной, если только в новой, то с неё? И обязательно вызов должен быть не кешированым?
Александр
Александр
Александр, подскажите пожалуйста, как изменить разделитель || при выводе значений на, с помощью модификатора? как лучше и правильней это делать?
Александр
Александр
По поводу разделителя, хотел бы уточнить свой вопрос, понимаю что можно выводить так:
[[!+tv1:replace=`||==, `]]
, но интересует ваше мнение и есть ли ищё какие варианты вывода? Можно ли в &leftJoin это прописать?
Александр Мальцев
Александр Мальцев
Обработку лучше выполнять после получения данных.
Можно через Fenom:
{var $arrtv1 = $tv1 | split : '||'}
{if $arrtv1 | iterable}
    {foreach $arrtv1 as $value}
        <div>{$value}</div>
    {/foreach}
{/if}
Так же можно написать пользовательские модификаторы вывода. Они реализуются через сниппеты, тут уже можно написать любую логику.
Александр Мальцев
Александр Мальцев
Может они переопределялись значениями дефолтной таблицы.
Александр
Александр
Александр, спасибо.
Александр
Александр
Здравствуйте, изначально не заметил но сортирует не правильно:

Вызов такой:
<a href="#" data-sort="extendresource|tv1" data-dir="[[+mse2_sort:is=`extendresource|tv1:desc`:then=`desc`]]" data-default="desc" class="sort">Тест <span></span></a>
с чем связано не подскажите?
Александр
Александр
Возможно кому пригодиться, проблема с сортировкой была у полей, которых значения в виде числа: пример: 5000,350,580 и т.д, у таких полей в таблице, тип поля нужно выбрать: smallint.
Александр Мальцев
Александр Мальцев
Как понимаю, проблема с сортировкой решилась. Нужно было чтобы она выполнялась как с числами, а она выполнялась как со строками.
Александр
Александр
Да, с сортировкой проблема решилась, хотел бы уточнить кое какие моменты, для чисел нужно использовать INT, для текста VARCHAR или TEXT — тогда всё должно сортироваться верно.
Александр Мальцев
Александр Мальцев
В основном эти.
Для целых чисел int, для строк с переменной длиной от 1 до 255 символов varchar, text — для строк с максимальной длиной, равной 65535 символов.
Ещё бывает используется tinyint для хранения маленьких чисел (со знаком от –128 до 127, без знака от 0 до 255), datetime — для даты и время, и mediumtext — для больших текстов с максимальной длиной до 16777215 символов.
Александр
Александр
Александр, здравствуйте! Не так давно, вышло обновление mSearh2 1.14-0-pl, где добавлена возможность кэширование предварительных результатов и сразу возникли проблемы с фильтрацией по своей таблицы а именно с предварительными подсчётами и правильным построением результатов, происходит это так у всех фильтров в фильтре в предварительных результатах одно и тоже число, при выборе любого фильтра остальные становятся не доступны, логика полностью нарушена, до этого были проблемы с подсчётами и тогда помогло такое решение, добавить пару строчек кода в файл custom.class.php:

//Правильные подсчёты в mFilter2
public function filterDefault(array $requested, array $values, array $ids) {
     return array_unique(parent::filterDefault($requested,$values,$ids));
}
после всё работало, но после обновления mSearh2, такой вариант не прокатывает, подскажите пожалуйста что можно попробовать сделать?
Александр Мальцев
Александр Мальцев
Здравствуйте! Это нужно смотреть какие изменения были сделаны в этой версии. Но так как проект закрытый и его нет на Github, то узнать, что в коде было изменено так просто не получится. Если найдёте какие изменения были конкретно сделаны в новой версии, то в этом случае уже можно будет что-то предложить.
Александр
Александр
Александр, по поводу отсутствия id в таблице, оказывается забыл при создании таблицы поставить галку параметра auto_increment.
Сейчас выглядит так:


Остались только проблемы с разбиением, как я понимаю от этого зависит разделитель значений тв в таблице? что там указывать?

Так же не получается вывести в mfilter2, как нужно указать там?

Вывожу так:
[[!mFilter2?
&tvFiltersOrDelimiter=`||`
&parents=`0`
EmptyFilters=`1`
&includeTVs=`tv1`
&noPreciseMSFilters=`1`
&forceSearch=`0`
&setMeta=`0` 
&suggestions=`1`
&showLog=`0`
&limit=`25`
&paginator=`pdoPage`
&ajaxMode=`button`
&toSeparatePlaceholders=`my.`

&filters=`
tv|tv1
`
&aliases=`
		tv|tv1==tv1
`
&tplFilter.outer.tv1=`tpl.checkboxmy`
]]
Александр Мальцев
Александр Мальцев
В плагине нужно поправить код:
...
//$tv1 = $resource->getTVValue('tv1');
//$tv2 = $resource->getTVValue('tv2');            
//$tv3 = $resource->getTVValue('tv3'); 

$tv1 = $modx->getObject('modTemplateVarResource', array(
  'tmplvarid' => 1, // id TV
  'contentid' => $id
));
if (is_object($tv1)) {
  $tv1 = $tv1->get('value');
} else {
  $tv1= '';
}
       
$tv2 = $modx->getObject('modTemplateVarResource', array(
  'tmplvarid' => 2, // id TV
  'contentid' => $id
));
if (is_object($tv2)) {
  $tv2 = $tv2->get('value');
} else {
  $tv2 = '';
}            
           
$tv3 = $modx->getObject('modTemplateVarResource', array(
  'tmplvarid' => 3, // id TV
  'contentid' => $id
)); 
if (is_object($tv3)) {
  $tv3 = $tv3->get('value');
} else {
  $tv3 = '';
}   
Метод getTVValue выполняет обработку данных, поэтому вместо него нужно просто выбирать значения.

Таблица готова, теперь для выбора всех полей нужен всего 1 запрос.

По mFilter2 нет готового рецепта, тут нужно писать решение для этой таблицы, т.е. свой фильтр для mFilter2.
Александр
Александр
Здравствуйте! При таком раскладе в таблицу пишуться урл тв поля img чё то не правильно делаю?

<?php
$modx->addPackage('extendResource', $modx->getOption('core_path').'components/extendresource/model/');

switch ($modx->event->name) {
 
        // Documents
        case 'OnDocFormSave':
        case 'OnDocPublished':
        case 'OnDocUnPublished':
        case 'OnResourceUndelete':
          

$isPublished = $resource->get('published');
    $id = $resource->get('id');        
$tv1 = $modx->getObject('modTemplateVarResource', array(
  'tmplvarid' => 1, // id TV
  'contentid' => $id
));
if (is_object($tv1)) {
  $tv1 = $tv1->get('value');
} else {
  $tv1= '';
}
       
$tv2 = $modx->getObject('modTemplateVarResource', array(
  'tmplvarid' => 2, // id TV
  'contentid' => $id
));
if (is_object($tv2)) {
  $tv2 = $tv2->get('value');
} else {
  $tv2 = '';
}            
           
$tv3 = $modx->getObject('modTemplateVarResource', array(
  'tmplvarid' => 3, // id TV
  'contentid' => $id
)); 
if (is_object($tv3)) {
  $tv3 = $tv3->get('value');
} else {
  $tv3 = '';
}   
            
            
    $extendResource = $modx->getObject('extendResource',array(
               'resource' => $id,
            ));
            if (is_object($extendResource)) {
                if ($isPublished) {
                $extendResource->set('resource', $id);
                $extendResource->set('tv1', $tv1);
                $extendResource->set('tv2', $tv2);
                $extendResource->set('tv3', $tv3);
                $extendResource->save(); } else {
                  $extendResource->remove();
                }
            } else {
                if ($isPublished) {
                $extendResource = $modx->newObject('extendResource');
                $extendResource->set('resource', $id);
                $extendResource->set('tv1', $tv1);
                $extendResource->set('tv2', $tv2);
                $extendResource->set('tv3', $tv3);
                $extendResource->save();
                }
            }
        break;        
        
        case 'OnDocFormDelete':
          $extendResource = $modx->getObject('extendResource',array(
               'resource' => $resource->get('id'),
          ));
          if (is_object($extendResource)) {
              $extendResource->remove();
          }          
        break;     
}
Александр Мальцев
Александр Мальцев
Сейчас TV указываются с помощью id. Значит нужно указать id нужных TV полей.
'tmplvarid' => 1, // id TV
Александр
Александр
Александр, где эти id? Если 3,4,5 у вас ниже на скрине тоже самое что и у меня, то с ними у меня не работает, так же если те id в таблице где у вас на втором скрине 5,6,7,8 они же не привязаны к конкретному полю или я чё то не догоняю, на всякий случай пробовал те и эти не сохраняет в таблице значения. В данном случае появляется пустая строка в таблице с новым id.


Александр Мальцев
Александр Мальцев
id TV это то что указано в круглях скобках в дереве элементов:
Вкладка с элементами в MODX
В этом примере tv1 имеет id, равный 7, tv2 имеет id, равный 8, tv3 имеет id, равный 9.
Александр
Александр
:) Точно нужны же id полей, чтобы плагин знал какие брать с дефолтной таблицы, и дублировал их в новой, запутался не много, поэтому не сообразил, спасибо:) Теперь всё работает.

Александр а по поводу фильтрации с mfilter2 по этой таблице, как это примерно реализовать, расширять класс? Может есть готовое решение где нужно просто изменить по минимуму, к примеру взять с файла filter.class.php? как я понимаю сейчас даже не получиться вывести эти тв в результатах mfilter2
Александр
Александр
Александр, если у базы данных префикс к примеру такой 2jbFDWbsFUQx, что в плагине нужно изменить? Просто возникла проблема если плагин установлен то не сохраняет ресурс, просто зависает при сохранении. На локалке всё работает на хосте с другим префиксом зависает.
Александр
Александр
Такие ошибки сыпятся:
[2019-10-05 20:06:10] (ERROR @ /home/s20017/www/core/xpdo/xpdo.class.php : 644) Could not load class: extendResource from mysql.extendresource.
[2019-10-05 20:06:10] (ERROR @ /home/s20017/www/core/xpdo/xpdo.class.php : 762) extendResource::load() is not a valid static method.
[2019-10-05 20:06:10] (ERROR @ /home/s20017/www/core/xpdo/xpdo.class.php : 644) Could not load class: extendResource from mysql.extendresource.
Александр
Александр
Хотел бы дополнить и поправить вопрос, оказывается причина не в префиксе, не могу разобраться в чём, но как только устанавливаешь системное событие: OnDocFormSave, вылетают ошибки а при сохранении ресурса просто зависает, Александр если есть время внесите свою лепту по поводу этого, очень нужно?
Александр
Александр
Вообщем проблема тут:
addPackage('extendResource'
крови она мне выпила, но странная вещь, на локалке работает на на хосте не в какую, поэтому изначально даже не предполагал что в этом весь гемор.
Александр Мальцев
Александр Мальцев
На хосте создали таблицу в базе данных? Дополнение core/extendresource скопировали?
Александр
Александр
Всё делал по новой, то есть с нуля, не работало если в плагине указано addPackage('extendResource' при чём пять раз переделывал, заработало addPackage('extendresource', по логике тут же должно быть название пакета, (папки extendresource) а не класса или я не прав?
Александр
Александр
Хотя наверно я не прав, но факт есть, работает а по другому не работает, с чем связано не понятно.
Александр Мальцев
Александр Мальцев
Там указывается название пакета (папки), так что должно быть маленькими буквами, т.е. extendresource. В коде неправильно написал. В Windows нет разницы прописная буква или строчная, поэтому и сработало. А на боевом сайте (в Linux) — нет, т.к. там есть разница прописная буква или строчная.
Александр Мальцев
Александр Мальцев
Префикс мы ни где не указывали. Он не нужен. Т.к. запрос к базе данных составляет сам механизм MODX и он, конечно, создаёт его с учётом префикса.
Префикс может понадобиться, если писать SQL запросы напрямую. В этом случае нужно будет получить префикс таблиц и подставить его в SQL:
$tablePrefix = $modx->getOption('table_prefix');
Александр
Александр
А зачем подгружать пакет плагин да без этого не работает, а в custom.class.php, если убрать блок construct работает, зачем он там нужен?
Александр
Александр
Спасибо, предельно понятно. Изначально грешил на него, когда были проблемы с плагином.
Александр Мальцев
Александр Мальцев
Значит уже загружен, тогда подгрузка не нужна и фрагмент construct можно убрать.
Александр
Александр
Заметил не большой баг, при сохранении ресурса в котором лежат все ресурсы, плагин добавил пустую строку в базе данных с id этого ресурса, на функционал не влияет но хотелось бы понять почему?
Александр
Александр
Всё на много серьёзней оказывается, при сохранении любого ресурса в новой базе появляется пустая строка, как исправить не подскажите?
Александр Мальцев
Александр Мальцев
Это из-за того, что в плагине нет проверки.
TV привязываются к шаблону, поэтому можно написать проверку, в которой проверить id шаблона ресурса. Если id шаблона нужный, то выполнять сохранение, в противном случае прервать выполнение кода.
switch ($modx->event->name) {
    case 'OnDocFormSave':
    case 'OnDocPublished':
    case 'OnDocUnPublished':
    case 'OnResourceUndelete':
        // например, выполнять после условия, только для тех ресурсов, у которых id шаблона, равно 15
        if ($resource->get('template') !== 15) {
            break;
        }
        ...
Александр
Александр
То что нужно, спасибо.
Александр Мальцев
Александр Мальцев
Привет!
Первую часть вопроса можно реализовать воспользовавшись рецептом, описанным в этой статье.

Т.е. открываете phpMyAdmin или какой-то другой инструмент для работы с базой данных. С помощью phpMyAdmin создаёте таблицу, например, modx_site_content_extend (modx_ — это префикс, который установлен для таблиц на сайте).
Структура таблицы modx_site_content_extend
Тут, всё понятно. Поле «id» – это первичный ключ, resource – это id ресурса, tv1, tv2, tv3, и т.д. – значения tv полей.

После этого создаём в «/core/components» папку нашего приложения, например, extendresource. А в ней папку «model», а в ней папку «schema».
В папке «schema» создадим файл «extendresource.mysql.schema.xml».
Содержимое файла:
<?xml version="1.0" encoding="UTF-8"?>
<model package="extendresource" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
    <object class="extendResource" table="site_content_extend" extends="xPDOSimpleObject">
        <field key="resource" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" index="pk" />
        <field key="tv1" dbtype="mediumtext" phptype="string" null="false" />
        <field key="tv2" dbtype="mediumtext" phptype="string" null="false" />
        <field key="tv3" dbtype="mediumtext" phptype="string" null="false" />
        <index alias="resource" name="resource" primary="false" unique="true" type="BTREE" >
            <column key="resource" length="" collation="A" null="false" />
        </index>
        <aggregate alias="Resource" class="modResource" local="resource" foreign="id" cardinality="one" owner="foreign" />
    </object>
</model>
Далее скачиваем скрипт parse_schema.php и помещае его в корень проекта.
Открываем данный файл, вводим в него название «extendresource».
Выполняем данный файл. Всё компонент готов.

Далее создаём плагин со следующим содержимым:
<?php
$modx->addPackage('extendResource', $modx->getOption('core_path').'components/extendresource/model/');

switch ($modx->event->name) {
 
        // Documents
        case 'OnDocFormSave':
        case 'OnDocPublished':
        case 'OnDocUnPublished':
        case 'OnResourceUndelete':
          
            $isPublished = $resource->get('published');
            $id = $resource->get('id');
            $tv1 = $resource->getTVValue('tv1');
            $tv2 = $resource->getTVValue('tv2');            
            $tv3 = $resource->getTVValue('tv3'); 
            
            $extendResource = $modx->getObject('extendResource',array(
               'resource' => $id,
            ));
            if (is_object($extendResource)) {
                if ($isPublished) {
                $extendResource->set('resource', $id);
                $extendResource->set('tv1', $tv1);
                $extendResource->set('tv2', $tv2);
                $extendResource->set('tv3', $tv3);
                $extendResource->save(); } else {
                  $extendResource->remove();
                }
            } else {
                if ($isPublished) {
                $extendResource = $modx->newObject('extendResource');
                $extendResource->set('resource', $id);
                $extendResource->set('tv1', $tv1);
                $extendResource->set('tv2', $tv2);
                $extendResource->set('tv3', $tv3);
                $extendResource->save();
                }
            }
        break;
        
        case 'OnDocFormDelete':
          $extendResource = $modx->getObject('extendResource',array(
               'resource' => $resource->get('id'),
          ));
          if (is_object($extendResource)) {
              $extendResource->remove();
          }          
        break;     
        
}
На вкладке «Системные события» отмечаем галочками пункты: OnDocFormDelete, OnDocFormSave, OnDocPublished, OnDocUnPublished и OnResourceUndelete.
Системные события плагина MODX

Данный плагин будет обновлять таблицу modx_site_content_extend при сохранении ресурса, публикации, снятие его с публикации и удалении.

Данные таблицы modx_site_content_extend
Александр
Александр
Александр душевное Вам спасибо, за развёрнутый ответ. Получилось сделать, чтобы сохранялись значений поля tv1 в этой таблице, но я что-то походу намудрил в создании таблицы, сделал скрины что у меня вышло:

Как видно id нет и значения не разделяются


И ошибки есть:


В руководстве есть опечатка «modal» model.

Подскажите пожалуйста в чём я допустил ошибку?

Александр
Александр
Александр всё работает ищё раз огромное спасибо, фильтрация тоже, только не понятны некоторые вещи, скажите, значения тв в таблице добавляются слитно, это нормально?

Предупреждение (Разбиение отсутствует) как с этим решить вопрос?

Как я понимаю сейчас при создание тв, если мне нужно чтобы его значения сохранялись в этой таблице, необходимо вручную добавить строку в таблице и указать название тв в плагине, правильно я понимаю?

В компонентах не чего не нужно править для работы с этой таблицей?
Александр
Александр
Поспешил не много, как я понял значения полей tv1 tv2 tv3, дублируются в таблицу modx_site_tmplvar_contentvalues, изначально ошибочно предполагая что сохраняться они будут только в новой таблице, поэтому решил что всё у меня работает, так как фильтрация работала. Александр, если не трудно подскажите как использовать эти поля, значения в фильтре? И просто выводить плейсхолдером?

И нормально ли это что в таблице значения у тв без разделителя просто слитно в таком формате: СпичкиСтружка?

Александр Мальцев
Александр Мальцев
Да, они дублируются, иначе нужно было бы переписывать уже существующую логику на страницах. А так она будет использоваться там, где нужно сократить время. Например, если используется 7 TV, то вместо 7 запросов можно ограничиться одним.

Т.к. таблица кастомная, то нужно писать свою логику для mFilter2.

Выводить значения из своей таблицы можно реализовать через сниппет или плагин.
Например, можно создать сниппет getExtendFields:
<?php
$modx->addPackage('extendResource', $modx->getOption('core_path').'components/extendresource/model/');

$extendResource = $modx->getObject('extendResource',array(
  'resource' => $id,
));

$tv1 = "";
$tv2 = "";
$tv3 = "";

if (is_object($extendResource)) {
  $tv1 = $extendResource->get('tv1');
  $tv2 = $extendResource->get('tv2');
  $tv3 = $extendResource->get('tv3');
}
          
$modx->setPlaceholders(array(
   'tv1' => $tv1,
   'tv2' => $tv2,
   'tv3' => $tv3,   
),'my.');

return; 
После этого поместить его вызов в шаблон:
[[!getTVFields? &id=`[[*id]]`]]
Вывести поля в этом случае можно так:
[[!+my.tv1]]
[[!+my.tv2]]
[[!+my.tv3]]
Александр Мальцев
Александр Мальцев
Да, для дублирования TV в своей таблице нужно не только это выполнить, а также обновить схему компонента и сгенерировать ему новую модель.