Задокументирую очередной вариант фильтра постов. Фильтр располагается в шаблоне главной страницы.
Верстка в шаблоне
<?php echo do_shortcode( '[site-filter]' ); ?>
<div class="filter-loader"><img src="/wp-content/uploads/2019/10/preloader-basket.gif"></div>
<div class="subcategory-posts">
<?php query_posts( 'posts_per_page=12');
if (have_posts()) :
echo '<div class="tagged-posts">';
while (have_posts()) : the_post();
get_template_part( 'solus' );
endwhile;
echo '</div>';
else: echo 'По вашему запросу нет результатов';
endif; //wp_reset_query(); ?>
<?php if ( $wp_query->max_num_pages > 1 ) {
echo '<div id="pagination">';
echo '<label class="checked"><input id="pagine" type="radio" name="pagine" value="1" checked>1</label>';
$i = 2;
while( $i <= $wp_query->max_num_pages ) :
echo '<label><input id="pagine" type="radio" name="pagine" value="'.$i.'"';
echo '>'.$i.'</label>';
$i++;
endwhile;
echo '</div>';
} ?>
</div>
Подключение ajax и включение функции фильтра (из плагина):
add_action('wp_print_scripts', 'ajax_filter_posts_scripts', 100);
function ajax_filter_posts_scripts() {
wp_enqueue_style( 'ajax_filter_posts', plugins_url('/site-ajax-filter-posts.css', __FILE__) );
wp_register_script( 'afp_script', plugins_url('/site-ajax-filter-posts.js', __FILE__), array('jquery'), null, true );
wp_enqueue_script( 'afp_script' );
wp_localize_script(
'afp_script',
'afp_vars',
array(
'afp_nonce' => wp_create_nonce( 'afp_nonce' ), // Create nonce which we later will use to verify AJAX request
'afp_ajax_url' => admin_url( 'admin-ajax.php' ),
)
);
}
function sitefilter() { include(__DIR__."/filter.php"); }
add_shortcode( 'site-filter', 'sitefilter' );
Верстка формы фильтра
<div id="filter"><form class="filter" action="" method="">
<?php $args = array(
'posts_per_page' => -1,
);
$catposts = get_posts( $args ); ?>
<div id="filters">
<!--<div><span class="name-group">Наименование</span>
<input type="text" value="" name="word" id="word">
</div>-->
<input type="hidden" value="8" name="number" id="number">
<!-- Фильтр по категориям -->
<div id="category_filter"><div><span class="name-group">Категория</span>
<span class="group-filter">
<?php $all_cat_ids = array();
foreach( $catposts as $catpost ){ //setup_postdata($post);
$all_cat_objects = get_the_category($catpost->ID);
if(!empty($all_cat_objects)){
foreach($all_cat_objects as $cat) {
$all_cat_ids[] = $cat->term_id;
}
}
}
wp_reset_postdata();
if ( count($all_cat_ids) > 0 ): ?>
<?php $cat_ids_unique = array_unique($all_cat_ids);
foreach($cat_ids_unique as $cat_id): ?>
<?php if ($cat_id != 1): // Отсеиваем категорию Без рубрики ?>
<?php $post_cat = get_term( $cat_id, 'category' ); ?>
<label>
<input class="categoria" type="checkbox" name="postercats" value="<?php echo $post_cat->term_id; ?>">
<span><?php echo $post_cat->name; ?></span>
</label>
<?php endif; ?>
<?php endforeach; ?>
<?php endif; ?>
</span></div></div>
<!-- Фильтр по произвольному полю -->
<div id="area_filter"><div><span class="name-group">Площадь (м²)</span>
<span class="group-filter">
<input type="text" value="" name="area_from" placeholder="от..." id="area_from">
<input type="text" value="" name="area_to" placeholder="до..." id="area_to">
</span></div></div>
<!-- Фильтр по произвольному полю -->
<div id="price_filter"><div><span class="name-group">Цена (руб.)</span>
<span class="group-filter">
<input type="text" value="" name="price_from" placeholder="от..." id="price_from">
<input type="text" value="" name="price_to" placeholder="до..." id="price_to">
</span></div></div>
<!-- Фильтр по меткам -->
<div id="options_filter"><div><span class="name-group">Опции</span>
<span class="group-filter">
<?php $all_tag_ids = array();
foreach( $catposts as $catpost ){ //setup_postdata($post);
$all_tag_objects = get_the_tags($catpost->ID);
if(!empty($all_tag_objects)){
foreach($all_tag_objects as $tag) {
$all_tag_ids[] = $tag->term_id;
}
}
}
wp_reset_postdata();
if ( count($all_tag_ids) > 0 ): ?>
<?php $tag_ids_unique = array_unique($all_tag_ids);
foreach($tag_ids_unique as $tag_id): ?>
<?php $post_tag = get_term( $tag_id, 'post_tag' ); ?>
<label>
<input class="metka" type="checkbox" name="tags" value="<?php echo $post_tag->term_id; ?>">
<span><?php echo $post_tag->name; ?></span>
</label>
<?php endforeach; ?>
<?php endif; ?>
</span></div></div>
</div>
<button type="submit" class="taguniq"><i class="fa fa-filter" aria-hidden="true"></i>Фильтровать</button>
</form></div>
Скрипт отправки значений формы
Для пэйджинации разбит на 2 разных скрипта (с разными action (filter_posts и filter_posts_order)): первый для изменения запроса и обнуления номера страницы, второй для перемещения по номерам страницы:
jQuery(document).ready(function($) {
// Нажатие кнопки Фильтрация при изменении формы
$("body").on("change", ".filter", function () {
$('.taguniq').trigger('click');
});
$('.taguniq').click( function(event) { // По нажатию на кнопку
// Prevent defualt action - opening tag page
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
//var sortPagine = $("input[name='pagine']:checked").val();
//var word = $("#word").val();
var area_from = $("#area_from").val();
var area_to = $("#area_to").val();
var price_from = $("#price_from").val();
var price_to = $("#price_to").val();
var arrList = $('.metka:checkbox:checked').map(function(){
return $(this).attr('value');
}).get();
var tags = arrList.join(', '); // преобразовываем массив в строку с разделителем ' '
var arrList2 = $('.categoria:checkbox:checked').map(function(){
return $(this).attr('value');
}).get();
var cats = arrList2.join(', '); // преобразовываем массив в строку с разделителем ' '
$(document).scrollTop( $("#filter").offset().top ); // Перемещаемся под фильтр (к объектам)
$('.tagged-posts').fadeOut();
data = {
action: 'filter_posts',
afp_nonce: afp_vars.afp_nonce,
tags: tags,
cats: cats,
//word: word,
area_from: area_from,
area_to: area_to,
price_from: price_from,
price_to: price_to,
};
$.ajax({
type: 'post',
dataType: 'html',
url: afp_vars.afp_ajax_url,
data: data,
beforeSend: function(){
$('.filter-loader').show();
},
success: function( data, textStatus, XMLHttpRequest ) {
$('.subcategory-posts').html( data );
$('.subcategory-posts').fadeIn();
console.log( textStatus );
console.log( XMLHttpRequest );
if( data ) {
$('.filter-loader').hide();
}
setTimeout(function () {
$('.hentry-gallery, #product-gallery').prepend('<div class="label"></div>');
$('.tag-hypothec .label').prepend('<div class="hypothec" title="Ипотека"><img src="/wp-content/uploads/2019/10/Ipoteka_96.png"></div>');
$('.tag-military .label').prepend('<div class="military" title="Военная ипотека"><img src="/wp-content/uploads/2019/10/star.png"></div>');
$('.tag-agp .label').prepend('<div class="agp" title="АЖП"><img src="/wp-content/uploads/2019/10/family.png"></div>');
$('.tag-mother .label').prepend('<div class="mother" title="Материнский капитал"><img src="/wp-content/uploads/2019/10/pacifier.png"></div>');
$('.tag-installment .label').prepend('<div class="installment" title="Рассрочка"><img src="/wp-content/uploads/2019/10/installment.png"></div>');
}, 400);
},
error: function( MLHttpRequest, textStatus, errorThrown ) {
console.log( MLHttpRequest );
console.log( textStatus );
console.log( errorThrown );
$('.subcategory-posts').html( 'No posts found' );
$('.subcategory-posts').fadeIn();
}
})
});
// Обновление при изменении списка с количеством страниц
$("body").on("change", "#pagine", function () {
// Prevent defualt action - opening tag page
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
var sortPagine = $("input[name='pagine']:checked").val();
//var word = $("#word").val();
var area_from = $("#area_from").val();
var area_to = $("#area_to").val();
var price_from = $("#price_from").val();
var price_to = $("#price_to").val();
var arrList = $('.metka:checkbox:checked').map(function(){
return $(this).attr('value');
}).get();
var tags = arrList.join(', '); // преобразовываем массив в строку с разделителем ' '
var arrList2 = $('.categoria:checkbox:checked').map(function(){
return $(this).attr('value');
}).get();
var cats = arrList2.join(', '); // преобразовываем массив в строку с разделителем ' '
$(document).scrollTop( $("#filter").offset().top ); // Перемещаемся под фильтр (к объектам)
$('.tagged-posts').fadeOut();
data = {
action: 'filter_posts_order',
afp_nonce: afp_vars.afp_nonce,
tags: tags,
cats: cats,
pagine:sortPagine,
//word: word,
area_from: area_from,
area_to: area_to,
price_from: price_from,
price_to: price_to,
};
$.ajax({
type: 'post',
dataType: 'html',
url: afp_vars.afp_ajax_url,
data: data,
beforeSend: function(){
$('.filter-loader').show();
},
success: function( data, textStatus, XMLHttpRequest ) {
$('.subcategory-posts').html( data );
$('.subcategory-posts').fadeIn();
console.log( textStatus );
console.log( XMLHttpRequest );
if( data ) {
$('.filter-loader').hide();
}
setTimeout(function () {
$('.hentry-gallery, #product-gallery').prepend('<div class="label"></div>');
$('.tag-hypothec .label').prepend('<div class="hypothec" title="Ипотека"><img src="/wp-content/uploads/2019/10/Ipoteka_96.png"></div>');
$('.tag-military .label').prepend('<div class="military" title="Военная ипотека"><img src="/wp-content/uploads/2019/10/star.png"></div>');
$('.tag-agp .label').prepend('<div class="agp" title="АЖП"><img src="/wp-content/uploads/2019/10/family.png"></div>');
$('.tag-mother .label').prepend('<div class="mother" title="Материнский капитал"><img src="/wp-content/uploads/2019/10/pacifier.png"></div>');
$('.tag-installment .label').prepend('<div class="installment" title="Рассрочка"><img src="/wp-content/uploads/2019/10/installment.png"></div>');
}, 400);
},
error: function( MLHttpRequest, textStatus, errorThrown ) {
console.log( MLHttpRequest );
console.log( textStatus );
console.log( errorThrown );
$('.subcategory-posts').html( 'No posts found' );
$('.subcategory-posts').fadeIn();
}
})
});
});
После успешного завершения ajax-запроса срабатывает пользовательская функция вывода лэйблов.
Сам фильтр (изменение запроса)
Фильтр также разделен на две большие функции в зависимости от того меняем ли мы запрос или переходим по страницам (функции фильтра связаны с action отправляемых форм ajax)
function ajax_filter_get_posts() {
// first query
if ($_POST['taxonomy'] != '') { $country = $_POST['taxonomy']; }
else { $country = 0; }
if ($_POST['taxonomy2'] != '') { $power = $_POST['taxonomy2']; }
else { $power = 0; }
if ($_POST['area_from'] != '') { $area_from = $_POST['area_from']; }
else { $area_from = 0; }
if ($_POST['area_to'] != '') { $area_to = $_POST['area_to']; }
else { $area_to = 1000; }
if ($_POST['price_from'] != '') { $price_from = $_POST['price_from']; }
else { $price_from = 0; }
if ($_POST['price_to'] != '') { $price_to = $_POST['price_to']; }
else { $price_to = 1000000000; }
$first_ids = get_posts( array(
'fields' => 'ids',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'price',
'value' => array( $price_from, $price_to ),
'type' => 'numeric',
'compare' => 'BETWEEN'
),
array(
'key' => 'area',
'value' => array( $area_from, $area_to ),
'type' => 'numeric',
'compare' => 'BETWEEN'
)
)
));
// second query
$tags = explode(" ", $_POST['tags']);
$cats = explode(" ", $_POST['cats']);
if ($_POST['labels'] != '') { $labels = explode(" ", $_POST['labels']); }
else { $labels = 0; }
$second_ids = get_posts( array(
'fields' => 'ids',
'posts_per_page' => -1,
'tax_query' => array(
'relation' => 'OR', //AND - записи которые одновременно входят в указанные таксономии; OR - записи принадлежащие любой из указанных таксономий
array(
'taxonomy' => 'post_tag',
'field' => 'id',
'terms' => $tags,
'operator' => 'IN' //AND - в записи должны быть все выбранные термины, IN - в записи должен быть хотя бы один из выбранных терминов
),
)
));
if (count($first_ids) == 0) { // если в первом массиве нет значений, берем только второй массив
$post_ids = array(0);
} elseif (count($second_ids) == 0) { // если во втором массиве нет значений, берем только первый массив
$post_ids = $first_ids;
} else { // если во втором массиве есть значения
$intersect_ids = array_intersect($first_ids, $second_ids); // сравниваем массивы и находим совпадения
if (count($intersect_ids) == 0) { // если нет совпадений, то в post__in передаем 0
$post_ids = array(0);
} else { // если есть совпадения, то в post__in передаем ID которые нужно получить
$post_ids = $intersect_ids;
}
}
// Обнуляем пагинацию
$_POST['pagine'] = 0;
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'cat' => $cats,
'posts_per_page' => $_POST['number'],
'paged' => $_POST['pagine'],
'post__in' => $post_ids,
's' => $_POST['word']
);
$theme_post_query = new WP_Query( $args );
if ($theme_post_query->have_posts()) {
echo '<div class="tagged-posts">';
$i = 0;
while( $theme_post_query->have_posts() ) :
$theme_post_query->the_post();
include(TEMPLATEPATH."/solus.php");
$i++;
endwhile;
echo '</div>';
} else { echo '<p class="empty">По вашему запросу нет результатов</p>'; }
if ( $theme_post_query->max_num_pages > 1 ) {
echo '<div id="pagination">';
echo '<label class="checked"><input id="pagine" type="radio" name="pagine" value="1" checked>1</label>';
$i = 2;
while( $i <= $theme_post_query->max_num_pages ) :
echo '<label><input id="pagine" type="radio" name="pagine" value="'.$i.'"';
echo '>'.$i.'</label>';
$i++;
endwhile;
echo '</div>';
}
exit;
}
add_action('wp_ajax_filter_posts', 'ajax_filter_get_posts');
add_action('wp_ajax_nopriv_filter_posts', 'ajax_filter_get_posts');
Вторая функция:
function ajax_filter_get_posts_order() {
// first query
if ($_POST['taxonomy'] != '') { $country = $_POST['taxonomy']; }
else { $country = 0; }
if ($_POST['taxonomy2'] != '') { $power = $_POST['taxonomy2']; }
else { $power = 0; }
if ($_POST['area_from'] != '') { $area_from = $_POST['area_from']; }
else { $area_from = 0; }
if ($_POST['area_to'] != '') { $area_to = $_POST['area_to']; }
else { $area_to = 1000; }
if ($_POST['price_from'] != '') { $price_from = $_POST['price_from']; }
else { $price_from = 0; }
if ($_POST['price_to'] != '') { $price_to = $_POST['price_to']; }
else { $price_to = 1000000000; }
$first_ids = get_posts( array(
'fields' => 'ids',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'price',
'value' => array( $price_from, $price_to ),
'type' => 'numeric',
'compare' => 'BETWEEN'
),
array(
'key' => 'area',
'value' => array( $area_from, $area_to ),
'type' => 'numeric',
'compare' => 'BETWEEN'
)
)
));
// second query
$tags = explode(" ", $_POST['tags']);
$cats = explode(" ", $_POST['cats']);
if ($_POST['labels'] != '') { $labels = explode(" ", $_POST['labels']); }
else { $labels = 0; }
$second_ids = get_posts( array(
'fields' => 'ids',
'posts_per_page' => -1,
'tax_query' => array(
'relation' => 'OR', //AND - записи которые одновременно входят в указанные таксономии; OR - записи принадлежащие любой из указанных таксономий
array(
'taxonomy' => 'post_tag',
'field' => 'id',
'terms' => $tags,
'operator' => 'IN' //AND - в записи должны быть все выбранные термины, IN - в записи должен быть хотя бы один из выбранных терминов
),
)
));
if (count($first_ids) == 0) { // если в первом массиве нет значений, берем только второй массив
$post_ids = array(0);
} elseif (count($second_ids) == 0) { // если во втором массиве нет значений, берем только первый массив
$post_ids = $first_ids;
} else { // если во втором массиве есть значения
$intersect_ids = array_intersect($first_ids, $second_ids); // сравниваем массивы и находим совпадения
if (count($intersect_ids) == 0) { // если нет совпадений, то в post__in передаем 0
$post_ids = array(0);
} else { // если есть совпадения, то в post__in передаем ID которые нужно получить
$post_ids = $intersect_ids;
}
}
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'cat' => $cats,
'posts_per_page' => $_POST['number'],
'paged' => $_POST['pagine'],
'post__in' => $post_ids,
's' => $_POST['word']
);
$theme_post_query = new WP_Query( $args );
if ($theme_post_query->have_posts()) {
echo '<div class="tagged-posts">';
$i = 0;
while( $theme_post_query->have_posts() ) :
$theme_post_query->the_post();
include(TEMPLATEPATH."/solus.php");
$i++;
endwhile;
echo '</div>';
} else { echo '<p class="empty">По вашему запросу нет результатов</p>'; }
if ( $theme_post_query->max_num_pages > 1 ) {
echo '<div id="pagination">';
$i = 1;
$cp = $_POST['pagine'];
while( $i <= $theme_post_query->max_num_pages ) :
echo '<label';
if ( $i == $cp ) { echo ' class="checked"'; }
echo '><input id="pagine" type="radio" name="pagine" value="'.$i.'"';
if ( $i == $cp ) { echo 'checked'; }
echo '>'.$i.'</label>';
$i++;
endwhile;
echo '</div>';
}
exit;
}
add_action('wp_ajax_filter_posts_order', 'ajax_filter_get_posts_order');
add_action('wp_ajax_nopriv_filter_posts_order', 'ajax_filter_get_posts_order');
Некоторые стили оформления фильтра:
#filter {margin-bottom: 30px; font-size: 1.3rem;}
#filters {display: flex; justify-content: space-between; align-items: center; margin-left: -5px; margin-right: -5px;}
#filters > div {padding: 0 5px; min-width: 25%;}
#filters > div > div {border: 1px solid #eee; padding: 0 10px; border-radius: 7px;}
.name-group {display: block; width: 50%; margin: -10px auto 5px auto; background: #fff;}
.group-filter {display: flex; justify-content: center; min-width: 15%; text-align: left;}
.group-filter input {border: none; border-bottom: 1px solid #eee;}
#filters > #area_filter, #filters > #price_filter {min-width: 20%;}
#area_filter .group-filter, #price_filter .group-filter {justify-content: space-between; margin-bottom: 10px;}
#area_filter input, #price_filter input {text-align: center; width: 45%; padding: 0; font-size: 1.2rem; margin-bottom: 7px;}
#category_filter .group-filter, #options_filter .group-filter {flex-wrap: wrap;}
#filter label {margin: 0 7px; display: flex; align-items: center; margin-bottom: 7px;}
#filter label input {margin-right: 7px;}
#filter label small {color: #eee;}
#filter select {width: 100%;}
#filter button {display: none;}
#filter input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {color: #aaa;}
#filter input::-moz-placeholder, textarea::-moz-placeholder {color: #aaa;}
#filter input:-moz-placeholder, textarea:-moz-placeholder {color: #aaa;}
#filter input:-ms-input-placeholder, textarea:-ms-input-placeholder {color: #aaa;}
.filter-loader {display: none;}
#pagination {display: flex; justify-content: center;}
#pagination label input {display: none;}
#pagination label {
background: #ffc31e;
font-size: 1.2rem;
cursor: pointer;
line-height: 35px;
width: 35px;
display: block;
text-align: center;
margin: 0 5px;
}
#pagination label:hover {background: #ff9800;}
#pagination label.checked {background: #ff9800; cursor: default;}
@media screen and (max-device-width: 1024px) {
#pagination {flex-wrap: wrap;}
#pagination label {margin-bottom: 10px;}
}
@media screen and (max-device-width: 800px) {
#filter {margin-bottom: 20px;}
#filters {flex-wrap: wrap; justify-content: center;}
#filters > #category_filter {min-width: 100%; margin-bottom: 15px;}
#filters > #area_filter, #filters > #price_filter {margin-bottom: 15px;}
#postsmore {width: 50%; margin: 30px 25% 0 25%;}
}
@media screen and (max-device-width: 480px) {
#filters > #area_filter, #filters > #price_filter {width: 50%; margin-bottom: 15px;}
#filters > div {min-width: 50%;}
.name-group {width: 70%;}
#postsmore {width: 100%; margin: 30px 0 0 0;}
}
[site-socialshare]