[СДЕЛАЙ САМ] Поиск с mSearch2 и SendIt без перезагрузки страницы и динамическими параметрами

Приветствую, думаю ни для кого не секрет, что mSearch2 не умеет показывать результаты поиска без перезагрузки страницы, а pdoPage не умеет «на лету» менять параметры. На своём сайте я захотел сделать морфологический поиск услуг без перезагрузки, с постраничной навигацией и возможностью выбирать категорию поиска. Ниже опишу чтобы для этого было предпринято.

1. Разметка
Для начала нам нужна форма для ввода запроса и выбора категории, блок для вставки результатов и блок для вставки пагинации. Выглядит это примерно так:
<form id="searchForm" data-si-form="searchForm" data-si-preset="search">
    <label>
        <input type="text" name="query" placeholder="Ключевые слова">
    </label>
    <div>
        <select name="parent">
            <option value="36">Выберите категорию</option>
            <!-- вызов спиппета pdoResources -->
        </select>
    </div>
    <button type="submit">Найти</button>
</form>
<div id="results">
    <!-- вызов спиппета pdoPage -->
</div>
<div id="pagination">
    <!-- пагинация -->
</div>
У меня за отправку формы отвечает сниппет SendIt, вы можете использовать тот, который вам больше нравится.

2. Сниппет-обработчик
Для обработки формы на сервере нужен сниппет:
<?php
$_GET['query'] = $_POST['query'];

/*
тут должен быть массив параметров вызова pdoPage
я их получаю из файла с пресетами,
но это актуально только при использовании MigxPageConfigurator
 */
$params = include(MODX_BASE_PATH . $presetPath);
$catalogParams = $params[$presetName];
foreach ($catalogParams as $k => $v) {
    $catalogParams[$k] = str_replace('#/', '@FILE chunks/', $v);
}

/*
 устанавливаем параметры вызова из запроса
 */
$catalogParams['parents'] = (int)$_POST['parent'];
if ($_POST['query'] && strlen($_POST['query']) >= $catalogParams['minQuery']) {
    $catalogParams['resources'] = $modx->runSnippet('mSearch2', ['parents' => (int)$_POST['parent'], 'limit' => 0, 'returnIds' => 1]);
}

/*
 проверяем поменялся ли родитель с прошлого запроса
 если поменялся значит это новая выборка и нужно перейти на первую страницу
 */
if ($_SESSION['liveSearch']['parent'] !== (int)$_POST['parent'] || $_SESSION['liveSearch']['query'] !== $_POST['query']) {
    $_GET['page'] = 1;
}

/*
 устанавливаем отступ
 */
if ((int)$_POST['current_page'] && (int)$_POST['current_page'] <= (int)$_POST['total_pages']) {
    $params['offset'] = ((int)$_POST['current_page'] - 1) * $catalogParams['limit'];
}

/*
 получаем плейсхолдеры сгенерированные pdoPage
 */
$html = $modx->runSnippet('pdoPage', $catalogParams);
$page = (int)$modx->getPlaceholder($catalogParams['pageVarKey'] ?: 'page');
$pagination = $modx->getPlaceholder($catalogParams['pageNavVar'] ?: 'page.nav');

/*
 записываем параметры запроса в сессию
 */
$_SESSION['liveSearch']['resources'] = $catalogParams['resources'];
$_SESSION['liveSearch']['parent'] = $_POST['parent'];
$_SESSION['liveSearch']['query'] = $_POST['query'];

return $SendIt->success('', ['html' => $html, 'parent' => (int)$_POST['parent'], 'page' => $page, 'query' => $_POST['query'], 'pagination' => $pagination]);
3. Обработка ответа сервера
После того как наш сниппет запустил mSearch2 и pdoPage нам нужно вывести результаты, для этого понадобится примерно такой JS
document.addEventListener('si:send:finish', async (e) => {
    const results = document.querySelector('#results');
    const pagination = document.querySelector('#pagination');
    const url = window.location.href;
    const params = new URLSearchParams(window.location.search);

    e.detail.result.data.parent ? params.set('parent', e.detail.result.data.parent) :  params.delete('parent');
    e.detail.result.data.query ? params.set('query', e.detail.result.data.query) :  params.delete('query');

    if(e.detail.result.data.page ><code></code> 1){
        params.set('page', e.detail.result.data.page);
    }else{
        params.delete('page');
        pdoPage.keys[pdoPage.configs.page['pageVarKey']] = 1;
    }

    if(params.toString()){
        window.history.replaceState({}, '', url.split('?')[0] + '?' + params.toString());
    }else{
        window.history.replaceState({}, '', url.split('?')[0]);
    }

    if(results){
        results.innerHTML = e.detail.result.data.html;
    }
    if(pagination){
        pagination.innerHTML = e.detail.result.data.pagination;
    }    
});

Этот код добавит запрос и родителя в get-параметры, что позволит делится ссылкой (@Дима Касаткин спасибо за идею), если в вызове pdoPage вы эти параметры укажете примерно вот так
{'!pdoPage' | snippet: [
'element' => 'msProducts',
'resources' => ('!mSearch2' | snippet: ['parents' => $.get.parent, 'limit' => 0, 'returnIds' => 1])?: '999999'
...
]}
Я привел примерный код, поскольку в моём вариант после загрузки происходит автоматическая отправка формы, при наличии сохранённых параметров.

4. Обработка переключения страницы
При переключении страницы нужно дополнительно отправлять на сервер выбранные параметры (запрос и родителя). Дл этого нужно изменить оригинальный JS pdoPage (assets/components/pdotools/js/pdopage.js). В методе pdoPage.loadPage в конце блока объявления переменных (после стр. 128) нужно добавить наши данные
var query = wrapper.find('[name="query"]');
var parent = wrapper.find('[name="parent"]');
А после блока формирования параметров нужно добавить наши данные к ним
if(query.length){
    params['query'] = query.val();
}
if(parent.length){
    params['parent'] = parent.val();
}
На строке 173 замените config['connectorUrl'] на путь к вашему коннектору (зачем он нужен читайте ниже).
На этом изменения в оригинальном JavaScript закончены. Отмечу, что править исходный файл не нужно, сделайте копию и измените системную настройку с путями к скриптам фронтенда.

5. Собственный коннектор
Поскольку на предыдущем шаге мы добавили в запрос новые параметры, нужно научить коннектор их обрабатывать. ДЛя этого я сделал копию родного коннектора и внёс изменения после 24 строки
/*
 если уже есть найденные ресурсы
значит нужно перейти на другую страницу
в другом случае надо перейти на первую страницу
 */
if ($_SESSION['liveSearch']['resources']) {
    $scriptProperties['resources'] = $_SESSION['liveSearch']['resources'];
} else {
    if ($_SESSION['liveSearch']['parent'] !== $_GET['parent'] || $_SESSION['liveSearch']['query'] !== $_GET['query']) {
        $_GET['page'] = 1;
    }
    $scriptProperties['parents'] = $_SESSION['liveSearch']['parent'];
}
Вот и всё. Посмотреть как это работает можно тут.

В данный момент ведётся активная работа над MiniShop3 силами сообщества и руками @Николай Савин, поэтому призываю всех неравнодушных поддержать эту работу финансово.
Артур Шевченко
06 сентября 2023, 10:47
modx.pro
3
645
+13
Поблагодарить автора Отправить деньги

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

Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
0