Друзья, сегодня мы с вами сделаем вывод товаров в категорию с помощью снипета mFilter2, который идет в компоненте mSearch2. Данный компонент платный, но я советую вам его приобрести. Если у вас по каким-то причинам это сделать не получается, то вы можете сделать аналогичный вывод товаров, используя pdoPage.
Конечно, чтобы нам вывести товары в категориях, их сначала необходимо создать. Что я и сделал. Сначала я создал несколько брендов:
Создал несколько опций товаров и подкатегории, чтобы нам было что выводить:
И, конечно, нужно создать товары. Я создал 27 товаров из разных ниш:
Что ж приступим! Для начала предлагаю вам создать чанк для вывода товара, который я называл productTpl, для этого создаем файл в директории core/elements/chunks со следующим содержимым:
<!-- item --> <div class="col-xl-4 col-lg-6 col-md-6 item-wrap"> <div class="item eqh"> <div class="image"> <div class="labels"> {if $favorite?} <div> <span class="labels-hits">Хит продаж</span> </div> {/if} {if $new?} <div> <span class="labels-new">Новинка</span> </div> {/if} {if $old_price > 0?} <div> <span class="labels-action">Акция</span> </div> {/if} </div> <div class="bigger"> <a href="{$id | url}" class="text-center" style="display:block"> {if $image?} <img src="{$image}" alt="{$pagetitle | htmlent}" title="{$pagetitle | htmlent}" class="img-fluid"> {else} <img src="{$_modx->getPlaceholder('+noimage')}" alt="{$pagetitle | htmlent}" title="{$pagetitle | htmlent}" class="img-fluid"> {/if} </a> {if $_pls['vendor.logo']?} <img src="{$_pls['vendor.logo']}" class="vendor" alt="{$_pls['vendor.name']}"> {/if} </div> </div> <div class="text"> <a href="{$id | url}" class="title">{$pagetitle}</a> <div class="features"> </div> <div class="characters"> {$_modx->runSnippet('msProductOptions', [ 'onlyOptions' => $parent | resource: 'options', 'product' => $id, 'tpl' => '@FILE chunks/main_opts.tpl' ])} </div> <div class="prices-block"> <div class="prices"> {if $old_price > 0?} <div class="old_price"> <span>{$old_price}</span> <i class="fa fa-ruble"></i> </div> {/if} {if $price > 0?} <div class="price"> <span>{$price}</span> <i class="fa fa-ruble"></i> </div> {/if} </div> <form method="post" class="ms2_form"> <input type="hidden" name="id" value="{$id}"> <input type="hidden" name="count" value="1"> <input type="hidden" name="options" value="[]"> <button type="submit" name="ms2_action" value="cart/add" class="btn btn-blue btn-rounded">В корзину</button> </form> </div> </div> </div> </div> <!-- / item -->
Как вы уже успели заметить я не зря заполнял опции товаров, так как по нашему макету на каждой плашке карточки товара должны выводиться основные характеристики товара. Как мы их будем задавать? Я решил, что проще всего это будет сделать следующим образом:
- создать TV-параметр options и привязать его к шаблону “Каталог товаров”
- и для дочерних товаров выводить только те опции, которые указаны в TV
За вывод данного блока у нас отвечает конструкция:
{$_modx->runSnippet('msProductOptions', [ 'onlyOptions' => $parent | resource: 'options', 'product' => $id, 'tpl' => '@FILE chunks/main_opts.tpl' ])}
Подробно по каждому из параметров я расскажу в видео. И собственно, чанк main_opts.tpl:
{foreach $options as $option} <div class="char-item"> <span class="label">{$option.caption}:</span> <span class="value"> {if $option.value is array} {$option.value | join : ', '} {if $option.measure_unit?} {$option.measure_unit} {/if} {else} {$option.value} {if $option.measure_unit?} {$option.measure_unit} {/if} {/if} </span> </div> {/foreach}
Кстати, именно на этом чанке видно основное преимущество Fenom над стандартным шаблонизатором MODx: чанков становится на порядок меньше. Мы с вами в прошлом курсе уже разбирали как делать фильтр на mSearch2, думаю, что вы легко справитесь с предстоящей задачей.
Создадим с вами первый чанк mfilter_outer.tpl (я напоминаю, что все чанки у нас хранятся в файлах):
<div class="row msearch2" id="mse2_mfilter"> <div class="col-lg-3"> <div class="sidebar"> <!-- widget --> <div class="widget filters"> <span class="title">Фильтр</span> <div class="widget-content"> <form action="{$id | url}" method="post" id="mse2_filters"> {$filters} </form> </div> </div> <!-- / widget --> </div> </div> <div class="col-lg-9"> <div class="product-list"> <div class="row" id="mse2_results"> {$results} </div> <div class="paging mse2_pagination"> <nav> {'page.nav' | placeholder} </nav> </div> </div> </div> </div>
Обратите особое внимание на классы и идентификаторы, которые прописаны в чанке. Теперь нам можно попробовать сделать вызов mFilter2 в шаблоне:
{$_modx->runSnippet('!mFilter2', [ 'element' => 'msProducts', 'class' => 'msProduct', 'showEmptyFilters' => 1, 'limit' => 9, 'tplOuter' => '@FILE chunks/mfilter_outer.tpl', 'tplPageWrapper' => '@INLINE <ul class="pagination">{$prev}{$pages}{$next}</ul>', 'tplPageActive' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">{$pageNo}</a></li>', 'tplPage' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">{$pageNo}</a></li>', 'tplPagePrev' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">«</a></li>', 'tplPageNext' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">»</a></li>', 'tplPagePrevEmpty' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">«</a></li>', 'tplPageNextEmpty' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">»</a></li>', 'filters' => 'msoption|sndvvatt, msoption|sndvvolume, ms|price:number, msoption|pokroi, msoption|rukzkolvootd, msoption|rukzvolume, msoption|smartobpam, msoption|smartoppam', 'aliases' => 'msoption|sndvvatt==sndvvatt, msoption|sndvvolume==sndvvolume, ms|price==price, msoption|pokroi==pokroi, msoption|rukzkolvootd==rukzkolvootd, msoption|rukzvolume==rukzvolume, msoption|smartobpam==smartobpam, msoption|smartoppam==smartoppam', 'ajaxMode' => 'button', 'filterOptions' => '{ "more_tpl": "<div class=\"text-center\"><button class=\"btn btn-blue btn-rounded btn_more\">Загрузить еще</button></div>" }', 'tpls' => '@FILE chunks/product_tpl.tpl', ])}
Нам с вами осталось привести в порядок фильтры. Для начала мы с вами переведем все опции, которые у нас не переведены. Для этого переходим в “Управление словарями”, выбираем топик “mSearch2” и язык “RU”. И добавляем наши опции:
После перевода нам нужно добавить еще 4 параметра в вызов mFilter2. Эти параметры, как вы уже догадались, чанки, которые нам также необходимо создать в папке core/elements/chunks. Вызов mFilter2 у нас приобретет следующий вид:
{$_modx->runSnippet('!mFilter2', [ 'element' => 'msProducts', 'class' => 'msProduct', 'showEmptyFilters' => 1, 'limit' => 9, 'tplOuter' => '@FILE chunks/mfilter_outer.tpl', 'tplPageWrapper' => '@INLINE <ul class="pagination">{$prev}{$pages}{$next}</ul>', 'tplPageActive' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">{$pageNo}</a></li>', 'tplPage' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">{$pageNo}</a></li>', 'tplPagePrev' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">«</a></li>', 'tplPageNext' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">»</a></li>', 'tplPagePrevEmpty' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">«</a></li>', 'tplPageNextEmpty' => '@INLINE <li class="page-item"><a class="page-link" href="{$href}">»</a></li>', 'filters' => 'msoption|sndvvatt, msoption|sndvvolume, ms|price:number, msoption|pokroi, msoption|rukzkolvootd, msoption|rukzvolume, msoption|smartobpam, msoption|smartoppam', 'aliases' => 'msoption|sndvvatt==sndvvatt, msoption|sndvvolume==sndvvolume, ms|price==price, msoption|pokroi==pokroi, msoption|rukzkolvootd==rukzkolvootd, msoption|rukzvolume==rukzvolume, msoption|smartobpam==smartobpam, msoption|smartoppam==smartoppam', 'ajaxMode' => 'button', 'filterOptions' => '{ "more_tpl": "<div class=\"text-center\"><button class=\"btn btn-blue btn-rounded btn_more\">Загрузить еще</button></div>" }', 'tpls' => '@FILE chunks/product_tpl.tpl', 'tplFilter.outer.default' => '@FILE chunks/filter_outer.tpl', 'tplFilter.row.default' => '@FILE chunks/filter_row.tpl', 'tplFilter.outer.price' => '@FILE chunks/filter_outer_price.tpl', 'tplFilter.row.price' => '@FILE chunks/filter_row_price.tpl' ])}
Чанк filter_outer.tpl – это стандартная обертка для наших фильтров:
<div class="widget-filter" id="mse2_{$table}{$delimeter}{$filter}"> <h4 class="filters-title">{$_modx->lexicon('mse2_filter_'~$table~'_'~$filter)}</h4> <div class="widget-content"> {$rows} </div> </div>
Чанк filter_row.tpl – это вывод опции в виде чекбокса:
<div class="custom-control custom-checkbox"> <input type="checkbox" name="{$filter_key}" id="mse2_{$table}{$delimeter}{$filter}_{$idx}" value="{$value}" {$checked} {$disabled} class="custom-control-input"> <label for="mse2_{$table}{$delimeter}{$filter}_{$idx}" class="{$disabled} custom-control-label"> {$title} </label> </div>
Чанк filter_outer_price.tpl – обертка для фильтра цены (в виде слайдера):
<div class="widget-filter" id="mse2_{$table}{$delimeter}{$filter}"> <h4 class="filters-title">{$_modx->lexicon('mse2_filter_'~$table~'_'~$filter)}</h4> <div class="widget-content"> <fieldset> <div class="mse2_number_inputs row"> {$rows} </div> <div class="mse2_number_slider"></div> </fieldset> </div> </div>
Чанк filter_row_price.tpl – это элемент при выводе слайдера цены (минимальное и максимальное значение):
<div class="col-6"> <label for="mse2_{$table}{$delimeter}{$filter}_{$idx}">{$title} <input type="text" name="{$filter_key}" id="mse2_{$table}{$delimeter}{$filter}_{$idx}" value="{$value}"> </label> </div>
Как видите все достаточно просто. Мы с вами реализовали вывод товаров в категории. Если вы попытаетесь кликнуть по кнопке “В корзину”, товар туда добавится. На этом мы с вами закончим нашу 4 часть.