Мова

Зв'язатися

Плагін для WordPress · з 2014

MDTF

Фасетний пошук записів WordPress і власних типів записів одночасно за полями post meta та таксономіями.

Перший реліз
Активні встановлення
1 000+
Рейтинг
4.5 / 5 (28)Читати відгуки
  • PHP
  • WordPress
  • WP_Query
  • meta_query
  • tax_query
  • Transients API
  • Сесії PHP
  • MySQL
  • PDO
  • jQuery
  • ion.rangeSlider
  • WooCommerce
  • WPML

Про мене

MDTF фільтрує будь-який тип запису одразу за мета-полями й таксономіями. Його створено для каталогів, що не є магазинами: нерухомість, автосалони, довідники. Власник сайту визначає поля фільтра у вбудованому конструкторі (checkbox, випадний список, повзунок діапазону, випадний список діапазону, календар, текстове поле, label), заповнює їх для кожного запису й розміщує форми пошуку як віджети або шорткоди.

Складність у тому, що WordPress зберігає мета як нетиповані рядки в wp_postmeta. Кожну умову діапазону, дати й тексту треба перетворити на meta_query, що порівнює коректно, кожна видима опція потребує живого лічильника за поточного пошуку, а AND/OR між групами ніколи не повинні послаблювати умови, які приховують записи.

Плагін підтримується з 2014 року, крізь PHP 7.x і 8.x, зміни сховища WooCommerce 3.0, блоковий редактор віджетів і довгу низку повідомлень про безпеку, оброблених через Patchstack і Wordfence. Преміум- і безкоштовна гілки мають одну кодову базу.

MDTF · фільтр мета-даних

що розв'язано

ПРАЦЮЄз 2014

Стан пошуку та посилання

shipped · 2014

Що це даєПосилання на результати пошуку лишаються короткими, їх можна додати в закладки чи поширити. Повторення того самого пошуку використовує одне посилання замість накопичення збережених копій.

Проблема
Пошук за багатьма мета-полями й таксономіями — це велика вкладена структура. У GET-параметрах вона дає дуже довгі посилання й розкриває форму запиту, а кожен запит сторінки (результати, лічильники, пагінація, сортування, ajax-перерахунок) має читати один і той самий стан.
Рішення

Стан форми кодується як JSON, зберігається на сервері в transient або сесії PHP, а посилання несе лише короткий ключ. Один метод доступу декодує й перевіряє його для всіх запитів у межах запиту.

Стек
  • PHP
  • Transients API
  • Сесії PHP
  • admin-ajax
Задачі · 5
Стан пошуку надто великий, щоб передавати його в URLЗберігати base64 JSON на сервері, у посилання вкладати лише короткий ключ page_mdf

encode_search_get_params() отримує серіалізовану форму через ajax, розбирає й санітизує її, видаляє значення '~' (будь-яке) і зберігає решту як base64 JSON через set_page_mdf_session(). Залежно від налаштування keep_search_data_in дані потрапляють у transient на одну добу або в $_SESSION. Відповідь — mdf_cat=...&page_mdf=<key>, з якої фронтенд будує посилання на результати.

Той самий пошук щоразу створював нову збережену копіюКлюч — md5 від нормалізованого JSON: асоціативні ключі відсортовано, скаляри обрізано, списки зберігають порядок

get_search_data_key() хешує вивід normalize_search_data(), яка рекурсивно обходить масив, сортує асоціативні ключі й зберігає порядок списків. Порядок полів із браузера більше не має значення, тож один пошук відповідає одному transient і одному посиланню. Посилання зі старішими ключами працюють, поки живе їхній transient.

Публічна точка зберігає кожен пошук на добуВідхиляти нерядковий чи завеликий ввід (фільтрується, 65536 байтів) до того, як щось буде збережено

mdf_encode_search_get_params зареєстровано для неавторизованих відвідувачів, і кожен окремий пошук записує transient, що живе добу. Обробник перевіряє, що vars є рядком у межах mdf_search_data_max_length, інакше відповідає mdf_cat=-1 без збереження. Наданий ключ приймається лише якщо збігається з /^[0-9a-f]{32}$/, інакше ключ виводиться з даних.

Збережений стан не є довіреним, коли його читають назадДекодувати в масив або в ніщо; примусово приводити межі діапазону from/to до чисел

get_page_mdf_data() вважає все, що не декодується в масив, відсутністю пошуку. Вона зливає опції віджета за замовчуванням і для кожного поля medafi_ приводить 'from' і 'to' до чисел, тож значення діапазонів і календарів потрапляють у meta_query як числа, що б не надіслав відвідувач. Декодований результат зберігається в статичній змінній до кінця запиту.

Фіксованим пошукам потрібні постійні посилання, що ніколи не спливаютьЧисловий ключ зіставляється з post meta page_mdf_string замість transient

Постійні посилання — це окремий прихований тип запису. Його save_post() зберігає закодований пошук у page_mdf_string. get_page_mdf_session() перевіряє ключ: числовий означає ID запису й читає збережене мета, усе інше йде в сховище transient або сесії. Результати, лічильники й шаблони не мусять знати, яке посилання було використано.

Побудова мета-запиту

shipped · 2014

Що це даєВідвідувачі можуть шукати за ціною, роком, пробігом, датами чи словами, зокрема за відкритими діапазонами на кшталт «і більше», а фільтри можуть повторно використовувати дані, вже введені в записах.

Проблема
Мета-значення — це рядки в wp_postmeta. Ціни, роки, пробіг, діапазони дат і вільний текст треба перетворити на умови meta_query, що порівнюють як числа чи дати, підтримують відкриті діапазони й усе ж дозволяють MySQL їх виконати.
Рішення

Кожен тип елемента фільтра має власне перетворення на meta_query або на фрагмент posts_where, з порівняннями DECIMAL для повзунків і випадних списків діапазонів, відкритими верхніми межами та моделями діапазону з двома ключами, що зберігаються для кожного запису.

Стек
  • PHP
  • WP_Meta_Query
  • posts_where
  • MySQL
Задачі · 5
Повзунок на максимумі має означати «і більше», а не «до максимуму»Замінювати верхню межу на 2147483647, коли ручка стоїть на налаштованому максимумі

У get_meta_query_array() випадок повзунка порівнює вибране 'to' із власним максимумом повзунка; якщо вони рівні, верхня межа стає MetaDataFilterCore::$max_int, тож записи зі значеннями вище за налаштовану шкалу все одно збігаються. Випадні списки діапазонів використовують ту саму ідею із суфіксом 'i' (нескінченність), як-от '1001-i'. Одиночні повзунки й випадні списки діапазонів використовують BETWEEN з типом DECIMAL.

Позиція має діапазон значень, а відвідувач шукає одне числоПовзунок із подвійним значенням: key <= v AND key_to >= v за двома мета-полями

Для повзунків зі slider_range_value = 1 запис зберігає два значення: ключ поля й key_to (наприклад, діапазон ціни чи розміру). Відвідувач вибирає одне значення, і запит вимагає, щоб воно потрапляло в діапазон самого запису, обидві сторони типізовано як DECIMAL.

Діапазони дат, де будь-який кінець календаря може бути порожнімЧотири гілки за ключами _from/_to: відкритий початок, відкритий кінець, один день, BETWEEN

Елемент календаря зберігає <key>_from і <key>_to. Якщо задано лише 'to', виходить _to <= to; лише 'from' дає _from >= from; рівні значення відповідають позиціям, чий діапазон охоплює цей день; повноцінний діапазон стає BETWEEN за _from. Коментар у коді датує останню гілку 08-10-2014.

Текстове поле шукає в заголовку чи вмісті слово за словомposts_where додає LIKE для кожного слова через $wpdb->prepare та esc_like, з'єднані фільтром

Текстовий елемент може націлюватися на власне мета-значення або на post_title, post_content, чи на обидва через OR або AND. Для колонок запису умова передається фільтрам MDTF_HELPER на posts_where, які ділять фразу на слова (перемикається через mdf_txtsearch_some_words_behavior) і з'єднують їх через mdf_txtsearch_some_words_condition, за замовчуванням AND. Повторні пробіли спершу згортаються.

Поля фільтра мають повторно використовувати мета, що вже існує на сайтіВіддзеркалені елементи зіставляють ключ фільтра з наявним мета-ключем під час запиту

Поле, позначене is_reflected, несе reflected_key. Конструктор запиту, розгалужувач OR і публічний API get_field() використовують віддзеркалений ключ, тож фільтр може працювати з мета, записаним темою чи іншим плагіном, без копіювання значень у ключі medafi_.

Логіка AND/OR між групами фільтра

у продукті

Що це даєВідвідувачі можуть вибирати, чи мають результати відповідати будь-якій або всім позначеним опціям. Приховані, відсутні в наявності чи сторонні позиції все одно лишаються поза результатами.

Проблема
Відвідувачі можуть хотіти OR між позначеними опціями, але простий OR на верхньому рівні meta_query також застосовує OR до службових умов: категорії фільтра, прихованих записів, видимості WooCommerce та наявності. Тоді приховані й сторонні записи потрапляють у результати. Вибраний режим також має дійти до кожного запиту пошуку.
Рішення

Умови поділяються на умови відвідувача, до яких можна застосувати OR, і обов'язкові. Фінальний запит — це AND на верхньому рівні з однією вкладеною групою OR, для мета й для таксономій. Режим подорожує всередині збереженого стану пошуку.

Стек
  • PHP
  • WP_Meta_Query
  • WP_Tax_Query
Задачі · 6
Простий OR також застосував би OR до умов, що приховують записиwrap_meta_query(): AND зверху, вкладена група OR відвідувача, службові умови обов'язкові

wrap_meta_query() будує relation AND і вкладає умови, до яких можна застосувати OR, як одну групу relation OR, лише коли їх дві або більше. mdf_hide_post NOT EXISTS, meta_data_filter_cat, _visibility і _stock_status потрапляють на верхній рівень і застосовуються завжди. Ту саму обгортку використовують запит результатів і лічильники.

Під OR мають сенс лише умови checkbox і labelsplit_meta_query_array() типізує кожну умову за елементом фільтра; повзунки лишаються обов'язковими

Розгалужувач зіставляє кожен ключ пошуку (власний чи віддзеркалений) з типом його елемента за секціями фільтра в пошуку. Умови елементів checkbox і label стають придатними для OR; повзунки, випадні списки, текстові поля, календарі та випадні списки діапазонів лишаються обов'язковими. У режимі AND масив повертається без змін, тож наявні пошуки AND дають той самий SQL, що й раніше.

OR для таксономій не повинен відкидати видимість і додаткові таксономіїsplit_tax_query_array() відокремлює терміни відвідувача від доданих фільтром за ідентичністю

Після того як сторонні фільтри (mdf_filter_taxonomies, додаткові таксономії, product_visibility) змінили tax_query, розгалужувач порівнює кожну умову зі списком, побудованим із форми, через строгий array_search. Збіги — це умови відвідувача, решта — обов'язкові. Повторні умови відвідувача, які фільтр зібрав за запит, відкидаються. Потім wrap_tax_query() вкладає під OR лише частину відвідувача.

Режим AND/OR має дійти до перерахунку, пагінації та сортуванняЗберігати режим в опціях віджета page_mdf, запит використовувати лише як запасний варіант

encode_search_get_params() записує meta_data_filter_bool і mdf_tax_bool у mdf_widget_options збереженого пошуку, беручи вибір відвідувача, коли віджет дозволяє обидва. get_meta_data_filter_bool() і get_mdf_tax_bool() спершу читають збережене значення й нормалізують усе, що не є 'OR', до 'AND'. Результати, лічильники, ajax-перерахунок, пагінація й сортування читають один і той самий стан.

Увімкнення OR для таксономій не повинно змінювати результати віджетів, що вже працюють на живих сайтахЗастосовувати OR для таксономій лише до віджетів, збережених із прапорцем версії; старіші лишаються на AND

Віджети, збережені старішими версіями, завжди шукали за таксономіями через AND, що б не казала їхня опція OR. Застосування опції до них змінило б результати на живих сайтах без жодної дії власника. get_widget_tax_bool() враховує опцію лише тоді, коли задано taxonomies_options_behaviour_ver, що відбувається, коли віджет зберігає нова версія.

Позначений батько й позначений нащадок: батько збігався б з усім під нимВідкидати позначений термін, коли також позначено одного з його нащадків

tax_query за замовчуванням включає нащадків, тож позначений батько збігався б з кожним записом під ним і робив би вибір нащадка безглуздим. get_tax_query_array() видаляє позначений термін, якщо get_term_children() від нього перетинається з позначеною множиною, а потім переіндексовує список.

Динамічні лічильники та кешування

shipped · 2014

Що це даєКожна опція показує, скільки позицій вона поверне, тож відвідувачі уникають порожніх результатів. Лічильники кешуються, тому сайт лишається швидким.

Проблема
Кожен checkbox, опція випадного списку й термін показує, скільки записів він дасть за поточного пошуку. Це один запит на кожен видимий елемент, з об'єднаннями на wp_postmeta, під час кожного перегляду сторінки й кожного ajax-перерахунку.
Рішення

Запити підрахунку беруть лише ID і пропускають found-rows та прогрів кешу. Лічильники можна кешувати в таблиці ключ/значення за ключем-хешем запиту, а дерева термінів — у transients із заданим терміном життя.

Стек
  • PHP
  • WP_Query
  • MySQL
  • Transients API
Задачі · 5
Лічильник має показувати пошук плюс цю одну опціюДля кожного елемента: додати його умову, замінити сусіднє значення того самого select, виконати знову

get_item_posts_count() починає з поточного пошуку. Checkbox додає key = 1, якщо його там ще немає. Опція випадного списку спершу видаляє умову того самого ключа, бо сусідні опції взаємовиключні, а потім додає себе (як BETWEEN для опцій діапазону). Для термінів get_tax_count() вставляє термін у копію стану таксономії з вимкненим кешем таксономій на запит і відновлює його після цього.

Запити підрахунку платили за дані, яких ніколи не використовувалиfields=ids, nopaging, без прогріву кешу мета/термінів, set_found_posts() перевизначено

Запити лічильників просять лише ID з вимкненими update_post_meta_cache, update_post_term_cache і cache_results. WP_QueryMDFCounterIn розширює WP_Query і перевизначає set_found_posts() так, щоб вона нічого не робила, тож WordPress не виконує зайвого кроку found-rows; відповідь — це post_count.

Ті самі лічильники перераховуються під час кожного перегляду сторінкиКешувати лічильники в {prefix}mdf_query_cache за ключем md5 від аргументів запиту

WP_QueryMDFCounter хешує повний масив аргументів і шукає його в двоколонковій таблиці з індексом на mkey; при промаху виконує запит і вставляє лічильник. Сторінка налаштувань створює таблицю й вимикає опцію, показуючи оператор CREATE, якщо користувачеві БД бракує привілею. Адміністратор очищає кеш зі сторінки налаштувань, що виконує TRUNCATE за manage_options і nonce.

Ієрархічні списки термінів коштують один запит на рівеньОдин виклик get_categories() на таксономію, дерево будується в PHP, кешується як transient

get_terms() один раз завантажує всі терміни таксономії, а assemble_terms_childs() будує списки нащадків з цього масиву. З увімкненим cache_terms_data результат зберігається під mdf_terms_cache_<md5 від аргументів> як transient, термін життя якого береться з налаштування періоду кешу термінів (від щогодини до 7 днів; без закінчення, коли період не задано).

Фільтр не повинен завантажуватися на сторінках, де немає пошукуНеобов'язковий білий список URL: пропускати ініціалізацію на фронтенді, якщо шляху немає в списку

is_should_init() виконується до того, як плагін реєструє свої типи записів і хуки. Якщо задано init_on_pages_only, вона нормалізує поточний URL (рядок запиту й /page/N видаляються) і завершується раніше, якщо URL немає в списку. Запити адмінки завжди ініціалізуються.

Модель даних та інтеграція з WordPress

shipped · 2014

Що це даєРезультати з'являються в наявному дизайні сайту, а персонал може перейменовувати чи реорганізовувати поля фільтра без втрати даних, уже збережених у записах.

Проблема
Поля фільтра визначає власник сайту, їхні значення живуть у post meta довільних типів записів, а результати мають з'являтися у власних шаблонах теми без зміни теми.
Рішення

Визначення полів живуть у прихованому типі запису. Значення записів зберігаються двічі: як один масив для редагування й як окремі мета-ключі для запитів. Головний запит замінюється з pre_get_posts із захистом від повторного входу.

Стек
  • PHP
  • Хуки WordPress
  • WP_Query
  • WooCommerce
  • WPML
Задачі · 6
Мета-значення мають редагуватися як набір і запитуватися по одномуЗберігати набір як один масив, а кожне поле medafi_ — як окремий мета-ключ

save_post() записує page_meta_data_filter як один масив, який зчитує екран редагування, а потім записує кожне поле medafi_ через update_post_meta(), що може використати meta_query. Секції фільтра та їхні елементи зберігаються як мета html_items прихованого типу запису meta_data_filter.

Зміна категорії фільтра запису не повинна лишати застарілі значенняПри зміні категорії видаляти все мета medafi_ запису перед збереженням нового

Поля старої категорії фільтра інакше лишилися б у wp_postmeta і збігалися б з пошуками цієї категорії. save_post() порівнює надіслану категорію зі збереженою і видаляє всі рядки medafi_% запису, коли вона змінилася.

Перейменування ключа поля не повинно залишати осиротілі дані в записахПеревірити унікальність у всіх секціях фільтра, потім перейменувати в postmeta одним UPDATE

change_meta_key() вимагає префікс medafi_, відхиляє ключ, що вже використовується в цій або будь-якій іншій секції фільтра, і називає секцію-конфліктника. Потім вона перейменовує ключ усередині html_items, переписує масиви page_meta_data_filter у зачеплених записах і виконує UPDATE postmeta SET meta_key. Вона потребує nonce, edit_post і edit_others_posts, бо зачіпає всі записи.

Результати мають відображатися у власному шаблоні пошуку темиpre_get_posts виконує запит результатів і замінює глобальний $wp_query, із захистом від циклу

parse_query() позначає головний запит як пошук запитаного типу запису. pre_get_posts() відображає [meta_data_filter_results] без шаблону; це відображення оголошує global $wp_query і присвоює новий WP_Query, побудований із пошуку. Прапорець запиту запобігає повторному входу, бо новий запит знову запускає pre_get_posts. Фільтр mdtf_prohibited_post_types тримає внутрішні запити ACF і Contact Form 7 поза цим.

WooCommerce 3.0 переніс приховані товари з мета в таксономіюПеремикач за версією: product_visibility NOT IN для 3.0+, до цього — мета _visibility

Результати, лічильники елементів і лічильники термінів розгалужуються за WOOCOMMERCE_VERSION: починаючи з 3.0 вони додають tax_query, що виключає exclude-from-catalog, а раніші версії отримують мета-умову _visibility IN (catalog, visible). Необов'язкова умова _stock_status ховає товари, яких немає в наявності. З активним WPML кожен запит також отримує поточну мову.

Опції старіших випадних списків не мають ключа, а записи зберігають їхні назви як значенняfill_select_keys() використовує назву опції як ключ під час читання, збігаючись зі збереженими значеннями записів

Опції випадних списків, збережені старішими версіями конструктора, мають порожній ключ, тоді як мета-блок запису зберігає назву опції як значення. Замість міграції fill_select_keys() виконується всередині get_html_items() і при збереженні, заповнюючи порожні ключі обрізаною назвою й не чіпаючи явні ключі, зокрема '0'. Наявні значення записів збігаються без повторного збереження записів.

Посилення безпеки

у продукті

Що це даєПублічний пошук не можна використати, щоб записати небажані дані чи дістатися до дій адмінки, що захищає сайт і його вміст.

Проблема
Плагін відкриває публічні ajax-точки, відображає шаблони за назвою з опцій і записує post meta з форм.
Рішення

Серверні підписи для даних, які браузер повертає назад, білі списки для шляхів шаблонів і мета-ключів, а також перевірки прав і nonce для дій адмінки.

Стек
  • PHP
  • Nonce-и WordPress
  • wp_hash
Задачі · 4
Віджет усередині блока має перемалювати ajax-перерахунок з опцій, що несе сторінкаПідписувати клас віджета й опції через wp_hash; приймати лише два відомі класи віджетів

Legacy Widget усередині блока не має бічної панелі, де перерахунок міг би знайти його за ID, тож його форма несе клас віджета й опції. get_widget_redraw_data() підписує їх через wp_hash(); get_widget_from_redraw_data() перевіряє через hash_equals() і перед викликом the_widget() приймає лише MetaDataFilter_Search і MetaDataFilter_TaxSearch.

Шлях шаблону результатів надходить з опцій шорткоду та віджетаОбрізати кожен сегмент шляху до [A-Za-z0-9_-]; підпапки дозволені, '..' неможливе

sanitize_template_path() нормалізує слеші, фільтрує кожен сегмент до безпечного набору символів і відкидає порожні, тож значення не може вийти за межі теки шаблонів, а підпапки на кшталт any/my_posts працюють.

Форма редагування запису не повинна записувати мета-ключі, окрім полів фільтраПриймати лише ключі medafi_ і вимагати edit_post перед збереженням значень

save_post() видаляє з надісланого масиву кожен ключ без префікса medafi_ перед збереженням і перевіряє префікс ще раз перед кожним викликом update_post_meta(), тож форма не може задати _edit_lock, _price чи подібні ключі. Збереження значень вимагає current_user_can('edit_post').

Ajax-дії адмінки не повинен мати змогу викликати будь-який відвідувачПраво плюс nonce для очищення кешу, перейменування ключа й редагування віджета

Очищення кешу вимагає manage_options і mdtf_ajax_nonce. Перейменування ключа вимагає власного nonce, edit_post і edit_others_posts. Дії з полями віджета вимагають edit_theme_options. Неавторизовані відвідувачі потрапляють лише до дій пошуку на фронтенді (нащадки термінів, кодування пошуку, перерахунок, панель пошуку, вміст кнопки пошуку). Інші обробники, зареєстровані для них, самі перевіряють доступ: дії підписки messenger вимагають входу й nonce, помічник статистики вимагає manage_options.

Цифри

wordpress.org
  • Активні встановлення 1 000+ лічильник wordpress.org.
  • Відгуки 28 Середня оцінка: 4.5 з 5.
  • Років підтримки 12 З 22 квітня 2014 р., останнє оновлення 13 жовтня 2026 р..