HUSKY
Fasetowy filtr produktów dla WooCommerce: taksonomie, atrybuty, cena, stan magazynowy, pola meta i tekst, jako widżet lub shortcode, z przeładowaniem strony lub przez AJAX.
- Pierwsze wydanie
- Aktywne instalacje
- 80 000+
- Ocena
- Czytaj opinie
O mnie
HUSKY filtruje produkty WooCommerce według kategorii, atrybutów, tagów, ceny, statusu magazynowego, oceny, autora, SKU, własnych pól meta i tekstu. Jest na wordpress.org od 2014 roku. Wersja darmowa i premium powstają z jednej bazy kodu; edycję wybiera numer wersji głównej, a części dostępne tylko w premium są w wersji darmowej albo zastąpione atrapami, albo ograniczone.
Każdy stan filtra kończy jako zapytanie WordPressa do wp_posts, wp_postmeta i tabel terminów, a sklepy obsługują dziesiątki tysięcy produktów i wariantów. Pokazanie, ile produktów zwróci każda opcja, oznacza jedno dodatkowe zapytanie na każdy widoczny termin przy każdym wyświetleniu strony. Produkty warunkowe (z wariantami), widoczność w katalogu, podatki i wtyczki walutowe zmieniają to, co uznaje się za dopasowanie.
Wtyczka działa też wewnątrz obsługi żądań, której nie kontroluje: parsowania reguł rewrite w WordPressie, głównego zapytania o produkty i szablonów WooCommerce, kreatorów stron, wtyczek wielojęzycznych i warstw cache. Większość opisanego niżej kodu istnieje dlatego, że któraś z tych warstw sprawiała, iż bezpośrednie podejście było błędne albo zbyt wolne.
HUSKY · filtr produktów
co rozwiązano
Adresy SEO dla stanów filtra
w produkcieCo to dajePrzefiltrowane strony dostają czyste, stabilne adresy, które wyszukiwarki mogą indeksować. Właściciel decyduje, które strony filtrów pojawią się w Google i z jakim tytułem.
- Problem
- Stany filtra to parametry zapytania (query string), które wyszukiwarki albo ignorują, albo indeksują bez ograniczeń. Adresy ścieżkowe, takie jak /swoof/color-red/size-m/, są nieznane regułom rewrite WordPressa i kolidują z przekierowaniami kanonicznymi.
- Rozwiązanie
Przechwycić parsowanie żądania, odciąć segment filtra, pozwolić WordPressowi dopasować resztę i odtworzyć stan filtra z segmentu według stałego kodowania i reguł indeksowania zależnych od głębokości.
- Technologie
/shop/swoof/color-red/size-xl/ nie pasuje do żadnej reguły rewrite i zwraca 404Odciąć segment filtra, skierować resztę przez reguły rewrite rdzenia i odtworzyć z niego filtry
Link filtra to adres archiwum plus slug filtra (domyślnie swoof) i jeden segment na każdy filtr: /product-category/shoes/swoof/color-red-and-blue/size-xl/. Wtyczka nie rejestruje żadnych reguł rewrite. url_process() uruchamia się na do_parse_request na froncie i od razu kończy działanie, chyba że get_search_request() znajdzie w ścieżce /<slug>/. Pobiera segment filtra do /page/, ? lub #, usuwa go i dopasowuje resztę (/product-category/shoes/ lub /shop/page/2) do $wp_rewrite->wp_rewrite_rules() kodem skopiowanym z WP::parse_request(): rozbudowane reguły stron, publiczne i prywatne zmienne zapytania, wyświetlane typy treści, taksonomie bez możliwości zapytań, wp_resolve_numeric_slug_conflicts() i filtr request. Następnie wywołuje query_posts(), handle_404() i register_globals() (akcja parse_request przed WordPressem 6.0) i zwraca false, żeby rdzeń nie parsował adresu ponownie.
parse_url_query() dzieli segment po '/' i dopasowuje każdą część do mapy nazw z get_all_items(): slugi atrybutów bez pa_, slugi taksonomii przetłumaczone przez WPML, price oraz stałe słowa instock, onsale, featured, backorder_not_in. -and- i -or- zamieniają się w przecinki, -to- w separator zakresu, + w spację; price-10-to-50 daje min_price i max_price. add_request() scala wynik przez filtr woof_get_request_data, więc get_request_data() zwraca tę samą tablicę co dla zapytania ?swoof=1&pa_color=red. W głównym zapytaniu parse_query() ustawia następnie post_type product, oznacza zapytanie jako archiwum typu treści i dodaje zapytania meta, a tax_query() na woocommerce_product_query_tax_query zastępuje wpisy WooCommerce dla tych samych taksonomii własnym zapytaniem taksonomii wtyczki.
Ponieważ reszta jest kierowana istniejącymi regułami, baza sklepu, baza kategorii produktów i paginacja archiwów działają bez nowych reguł: /page/N/ jest odcinane, zanim zostanie zlokalizowany segment filtra, i zostaje w kierowanej ścieżce. parse_query() wyłącza is_tax na stronach filtrów, więc bieżąca kategoria jest odzyskiwana z ostatniego segmentu oczyszczonego adresu (get_term_from_cleared_url()). Gdy strona sklepu jest jednocześnie stroną główną, opisana niżej poprawka redirect_canonical chroni przed przekierowaniem przez WooCommerce adresu filtra na /.
Dla adresu pasującego do reguły SEO meta_head() wypisuje canonical: adres filtra bez query string, jeśli stan jest indeksowalny (z wyłączonymi canonicalami Yoast i SEOPress), w przeciwnym razie adres obcięty na slugu filtra, czyli zwykłe archiwum. W Yoast SEO sitemap_index() na wpseo_sitemap_index dołącza adresy wpisane przez administratora w polu tekstowym ustawień, po jednym w wierszu; wersja premium zabezpiecza je przez esc_url_raw() i esc_xml().
Kolejność użycia filtrów nie powinna zmieniać adresu URLZapisywać filtry w posortowanej kolejności kluczy; odwzorować gramatykę -and- / -to- / + w PHP i JS
woof_get_submit_link() w url_parser.js sortuje aktywne filtry według klucza i zapisuje każdy jako <name>-<values>, gdzie ',' to -and-, separator zakresu to -to-, a spacje to +. Cena staje się price-<min>-to-<max>; dostępne, w promocji, wyróżnione i backorder zamieniają się w stałe słowa. parse_url_query() odwraca to odwzorowanie.
Dzięki sortowaniu wybranie najpierw koloru, a potem rozmiaru daje tę samą ścieżkę co w odwrotnej kolejności. Wartości w obrębie jednego filtra są zapisywane w kolejności wyboru.
Atrybut pa_niveau i pole meta niveau żądają tej samej nazwy w URLWykrywać kolizje nazw: taksonomia zachowuje krótką nazwę, druga dostaje unikalną nazwę zastępczą
Nazwy w URL pomijają prefiks pa_, więc atrybut i klucz meta mogą dać tę samą nazwę, a jeden wpis w mapie zasłoniłby drugi. resolve_seo_key_conflict() przyznaje krótką nazwę zarejestrowanej taksonomii niezależnie od kolejności filtrów w ustawieniach, dzięki czemu istniejące adresy pozostają ważne, a dla drugiego filtra tworzy nazwę z jego rzeczywistego klucza, w razie potrzeby dodając numeryczny sufiks. Slugi taksonomii przetłumaczone przez WPML są rozwiązywane przed tym krokiem.
Każde zapytanie przeliczające ponownie czyta stan filtra ze ścieżki URLZapamiętywać wynik parse_url_query() dla każdego URL, ale dopiero po wp_loaded
get_request_data() jest wywoływane z każdego zapytania przeliczającego, a przy adresach SEO każde wywołanie przechodzi przez woof_get_request_data do parse_url_query(). Przy Dynamic Recount i Hide Empty (premium) jedna strona może wykonać ponad tysiąc takich wywołań, dlatego wynik parsowania jest cache'owany dla każdego URL.
Cache włącza się dopiero po wp_loaded, żeby wywołanie wykonane, zanim istnieją taksonomie i ustawienia, nie mogło zamrozić niepełnej mapy nazw na resztę żądania.
Które strony filtrów indeksować i z jakim tytułem oraz metaIndeksować według głębokości i reguł regex z meta dla każdej reguły; błędne wzorce po cichu pomijać
WOOF_SEO liczy aktywne filtry. Powyżej index_deep (domyślnie 2) strona dostaje ustawienie 'Disable page indexing' (domyślnie tak, czyli noindex,nofollow przez wp_robots) i reguły nie są sprawdzane. Na tym poziomie lub niżej pasująca reguła czyni stronę indeksowalną. Reguły to wzorce URL z symbolami {any}, kompilowane do wyrażeń regularnych, każda z tytułem, meta description, H1 i tekstem SEO, które mogą zawierać nazwy wybranych terminów, i są przechowywane osobno dla każdego locale. Tagi canonical są wypisywane tylko dla adresów pasujących do reguły.
W wersji premium preg_match działa pod tymczasowym handlerem błędów, więc reguła, której URL nie jest poprawnym wyrażeniem, jest pomijana i wymieniana w komunikacie administracyjnym, a pusta reguła jest pomijana. Wersja darmowa używa tylko dwóch pierwszych reguł.
WooCommerce 11 przekierowywał adresy filtrów na stronie głównej będącej sklepemAnulować tylko przekierowanie na home_url i tylko dla adresów zawierających slug filtra
Gdy strona sklepu jest jednocześnie statyczną stroną główną, WooCommerce 11.0.0 ustawia ją jako zapytany obiekt w archiwach produktów. redirect_canonical() traktuje wtedy /swoof/... jako niekanoniczną kopię strony głównej i przekierowuje na /, gubiąc wszystkie filtry. fix_front_page_shop_canonical() zwraca false tylko wtedy, gdy stroną główną jest strona sklepu, ścieżka zawiera slug filtra, a cel przekierowania to adres główny, więc normalizacja ukośników i sluga działa poprawnie w pozostałych miejscach.
Dynamiczne przeliczanie i cache zapytań
w produkcieCo to dajeKażda opcja filtra pokazuje, ile produktów zwróci, więc kupujący unikają ślepych zaułków. Liczby są cache'owane, dzięki czemu sklep pozostaje szybki w dużych katalogach.
- Problem
- Każda opcja filtra pokazuje, ile produktów zwróci przy bieżącym wyborze. To jedno zapytanie o produkty na każdy widoczny termin przy każdym wyświetleniu strony i główny koszt w dużych katalogach.
- Rozwiązanie
Liczby pochodzą z uproszczonego zapytania zwracającego same ID, które odtwarza semantykę filtrów. Wyniki są cache'owane w obrębie żądania, w dedykowanej tabeli i w transients, a formularz można odłożyć do chwili, gdy odwiedzający o niego poprosi.
- Technologie
Liczba dla terminu musi uwzględniać resztę bieżącego wyboruBudować zapytanie od nowa dla każdego terminu, podstawiając termin według reguł wyboru pojedynczego lub wielokrotnego
dynamic_count() odbudowuje tax_query dla każdego terminu. Terminy już wybrane nie dostają liczby. Dla radio i select (pojedynczy wybór) termin zastępuje wybraną wartość swojej taksonomii; dla checkbox i multi-select (wielokrotny) jest do niej dodawany. Terminy bieżącej strony kategorii i taksonomii z atrybutu taxonomies w shortcode są zawsze łączone przez AND, więc liczby na stronie kategorii pozostają w obrębie tej kategorii.
Logika dla każdej taksonomii (OR, AND, NOT IN) z ustawień, zakresy ceny i oceny, warunki meta z rozszerzeń, język WPML i widoczność w katalogu są stosowane tak samo jak w zapytaniu o produkty. Rozszerzenia deklarują swoje zachowanie przy przeliczaniu przez html_type_dynamic_recount_behavior, więc typy color, image i label używają tego samego kodu.
Ukrywanie opcji bez wyników opiera się na tych liczbach: termin jest liczony i pomijany, gdy liczba wynosi 0, więc nie może być tańsze niż pokazywanie liczb. Ten przełącznik jest dostępny tylko w premium; wersja darmowa wymusza jego wyłączenie w widokach.
Zapytanie liczące nie powinno ładować wpisów ani uruchamiać FOUND_ROWSUtworzyć podklasę WP_Query z metodami found-rows i pętli jako no-op i pobierać tylko ID
WP_QueryWOOFCounterIn rozszerza WP_Query i nadpisuje set_found_posts(), setup_postdata(), the_post() i have_posts(). Zapytanie liczące działa z fields=ids i nopaging, więc MySQL zwraca listę ID, WordPress nie buduje obiektów wpisów i nie wykonuje drugiego zapytania FOUND_ROWS. Liczbą jest długość listy.
Filtry posts_clauses WooCommerce dla popularności i oceny są usuwane przed zapytaniem liczącym, ponieważ kolejność nie ma znaczenia dla liczby, a te filtry dodają do SQL złączenia (joins).
Te same liczby są liczone od nowa dla każdego odwiedzającego i każdej stronyPrzechowywać liczby w tabeli klucz-wartość z kluczem md5 pełnych argumentów zapytania, czyścić przez cron
Przy włączonym 'cache count data' WP_QueryWoofCounter haszuje JSON ostatecznych argumentów zapytania, które zawierają już część językową i część widoczności, i wyszukuje hasz w {prefix}woof_query_cache, dwukolumnowej tabeli z haszem jako kluczem głównym. Zapisy używają INSERT ... ON DUPLICATE KEY UPDATE, więc dwa wyświetlenia strony zapisujące ten sam klucz jednocześnie nie powodują błędu. Witryna z trwałym object cache może przenieść liczby do wp_cache_* przez filtr woof_use_wp_cache.
Nie ma taniego sposobu, by ustalić, na które zcache'owane liczby wpływa nowy lub edytowany produkt, więc zaplanowane zdarzenie opróżnia (TRUNCATE) całą tabelę w skonfigurowanym okresie. Jeśli użytkownik MySQL nie ma uprawnienia CREATE, strona ustawień wypisuje SQL do ręcznego uruchomienia i pozostawia opcję wyłączoną.
Zapytanie o min/max suwaka ceny skanuje postmeta w dużych sklepachZapamiętywać min/max dla każdego hasza SQL w obrębie żądania i w transient, czyścić przy zapisie produktu
get_filtered_price() buduje jedno SELECT MIN/MAX na _price, połączone z bieżącym zapytaniem taksonomii i meta. Ten sam zakres był żądany kilka razy na stronę (min i max dla każdego filtra ceny), więc wynik jest zapamiętywany w statycznej tablicy z kluczem md5 z SQL. Przy włączonym 'Optimize price filter' wyniki trafiają też do jednego transient na dobę, a każde woocommerce_update_product go usuwa. SQL przechodzi przez woof_get_filtered_price_query, więc witryna może dodać wskazówki indeksów.
Drzewa terminów są budowane od zera dla każdej taksonomii na każdej stronieCache'ować zbudowane drzewa w transients z kluczem z argumentów i bieżącego języka
WOOF_HELPER::get_terms() ładuje wszystkie terminy taksonomii przez get_categories() i składa drzewo rodzic-dziecko w PHP. Wynik jest zapamiętywany w obrębie żądania i, przy włączonym 'Cache terms', zapisywany w transient, którego klucz zawiera taksonomię, hide_empty, flagi głębokości i bieżący język WPML lub Polylang, więc każdy język ma własne zcache'owane drzewo.
Czyszczenie przechodzi po wp_options w poszukiwaniu _transient_woof_terms_cache_% i usuwa każdy transient, bo WordPress nie ma usuwania transients według prefiksu.
Przeliczanie działa, nawet jeśli odwiedzający nigdy nie otwiera filtraWyświetlić przycisk zamiast formularza i pobrać formularz przez AJAX przy pierwszym kliknięciu
Przy start_filtering_btn=1 serwer wypisuje tylko przycisk, więc przy ładowaniu strony nie działa żadne zapytanie o terminy. Po kliknięciu front.js wysyła woof_draw_products z shortcode produktów ustawionym na 'woof_nothing': serwer renderuje formularz filtra i żadnych produktów, strefa odświeżania jest podmieniana, a widżety inicjalizowane od nowa. Przycisk jest pomijany, gdy wyszukiwanie jest już aktywne, więc przefiltrowana strona nadal pokazuje swój wybór.
Ukrycie formularza przez CSS lub reguły display nic nie oszczędza, bo liczby są wyliczane na serwerze, zanim HTML zostanie wysłany. W wątkach o wydajności na forum wsparcia często trzeba było to wyjaśniać.
Praca wewnątrz zapytania o produkty WooCommerce
w produkcieCo to dajeFiltrowanie nadal działa z własnym motywem sklepu, stronami kategorii, produktami z wariantami, podatkami i walutami, więc kupujący nigdy nie widzą towarów w rozmiarach, które są wyprzedane.
- Problem
- Filtr zmienia główne zapytanie sklepu i polega na szablonach WooCommerce, których nie kontroluje. Musi działać mimo zmian modelu danych WooCommerce, z produktami warunkowymi (z wariantami), widocznością w katalogu, podatkami i wtyczkami walutowymi.
- Rozwiązanie
Podpiąć się późno w parse_query i zapytanie o produkty WooCommerce, przywrócić stan archiwum, którego oczekują szablony, i obsłużyć każdą różnicę modelu danych jawnie, według wersji lub tabeli.
- Technologie
Przefiltrowane wyniki muszą być renderowane przez szablon sklepu z motywuPrzepisać flagi głównego zapytania w parse_query z priorytetem 9999, by renderowało się jako archiwum produktów
Gdy w żądaniu jest slug wyszukiwania, parse_query() ustawia post_type=product i is_post_type_archive oraz czyści is_tax, is_tag, is_home, is_single i is_search. WooCommerce i motyw renderują wtedy pętlę sklepu dla dowolnego przefiltrowanego adresu. Rozszerzenia dodają swoje warunki meta przez assemble_query_params(), a operatory dla poszczególnych taksonomii pochodzą z filtra woof_main_query_tax_relations.
Te same hooki nie mogą dotykać żądań REST i Store API. Handlery kończą działanie od razu dla adresów /wp-json/, a parse_query jest odpinane na rest_api_init, więc motywy blokowe i klienci headless dostają wyniki samego WooCommerce.
Filtrowanie na stronie kategorii gubi kategorięPrzenosić termin jako really_curr_tax i trzymać go w transient z kluczem z IP odwiedzającego
Adres filtra nie jest już archiwum terminu, więc get_queried_object() przestaje zwracać kategorię. Gdy archiwum jest renderowane, set_really_current_term() zapisuje termin przez WOOF_STORAGE, który wtyczka uruchamia w trybie transient: jeden transient na odwiedzającego, z kluczem z hasza IP odwiedzającego, przechowywany przez dobę. JS dodaje też really_curr_tax=<term_id>-<taxonomy> do linku filtra, a get_really_current_term() sięga po niego, gdy nic nie jest zapisane. Termin jest następnie łączony przez AND z zapytaniem o produkty, zapytaniami przeliczającymi i wyszukiwaniem tekstowym. Żądania AJAX czytają zapisany termin, ale nigdy go nie nadpisują.
Widoczność w katalogu jest przechowywana inaczej w zależności od wersji WooCommerceRozgałęziać według wersji: meta _visibility przed 3.0, product_visibility NOT IN po
WooCommerce 3.0 przeniósł flagi widoczności, wyróżnienia i braku na stanie z postmeta do taksonomii product_visibility. product_visibility_not_in() zamienia potrzebne flagi na klauzule term_taxonomy_id NOT IN. generate_visibility_keys() dodaje outofstock, gdy sklep ukrywa towary niedostępne; gdy opcja 'listen catalog visibility' jest włączona, dodaje też zawsze exclude-from-search, a poza wyszukiwaniem exclude-from-catalog. Starsze sklepy dostają zamiast tego warunek meta _visibility.
Te same reguły są stosowane w głównym zapytaniu, zapytaniu shortcode, zapytaniach przeliczających, wyszukiwaniu tekstowym i danych trybu turbo. Jeśli któreś z nich się różni, liczby i wyniki się rozjeżdżają.
Produkt z wariantami jest pokazywany, gdy wybrany rozmiar jest niedostępnyZnaleźć w tabeli wyszukiwania atrybutów rodziców wyprzedanych pasujących wariantów i wykluczyć je
Produkt z wariantami liczy się jako dostępny, jeśli dostępny jest którykolwiek wariant, więc zwykłe zapytanie 'dostępne + rozmiar M' zwraca koszulę, której wariant M jest wyprzedany. Gdy filtr dostępności jest połączony z filtrami atrybutów, posts_where() zbiera wybrane terminy pa_*, w tym termin liczony podczas przeliczania, i wykonuje dwa zgrupowane zapytania na wc_product_attributes_lookup: rodziców z niedostępnym pasującym wariantem oraz rodziców z dostępnym. Różnica jest dołączana jako ID NOT IN.
Starszy tryb, wybierany w ustawieniach filtra dostępności, odpytuje wpisy product_variation według meta atrybutów i _stock_status zamiast tabeli wyszukiwania. Jest wolniejszy i domyślnie wyklucza każdego rodzica mającego wyprzedany pasujący wariant; rodzice mający także dostępne dopasowanie są zachowywani tylko wtedy, gdy filtr woof_exclude_existing_variations zwraca true.
Granice ceny różnią się od cen zapisanych przy podatkach i walutachPrzeliczyć granice z powrotem na walutę bazową i odjąć podatek wliczony w cenę przed zapytaniem meta
_price jest zapisywana w walucie bazowej sklepu i w wielu sklepach bez podatku. Jeśli odwiedzający używa waluty WPML (WCML), assemble_price_params() przelicza granice z powrotem na walutę bazową. Jeśli ceny są wyświetlane z podatkiem, a zapisane bez, podatek wliczony dla stawki podstawowej jest odejmowany od obu granic. Dla górnego końca suwaka get_max_price() znajduje produkt z maksymalną ceną i pyta WooCommerce o jego cenę z podatkiem.
Z FOX (WOOCS) wyświetlane wartości suwaka są przeliczane przez woocs_exchange_value, a zapytanie zachowuje liczby w walucie bazowej.
Tryb AJAX: odświeżać wyniki, liczby i fragmenty strony bez przeładowaniaJeden endpoint AJAX renderuje produkty i formularz przez shortcode; JS podmienia strefy i wypycha URL
woof_draw_products() odtwarza $_GET z linku, który otworzyłby odwiedzający, renderuje [woof_products] i [woof] z tym stanem i zwraca produkty, formularz i dodatkowe fragmenty (liczbę wyników, sortowanie, paginację) jako JSON. front.js podmienia każdy fragment, przywraca fragmenty, których odpowiedź nie zawierała, inicjalizuje widżety od nowa i wywołuje history.pushState, by URL można było udostępnić. onpopstate przeładowuje stronę, gdy zmienił się query string. Flaga blokady odrzuca kliknięcia w trakcie trwania żądania.
Pętla sklepu jest opakowana przez podpięcie woocommerce_before_shop_loop i woocommerce_after_shop_loop. Motywy, które nie wywołują obu hooków, psują tryb AJAX, co tłumaczy całą rodzinę wątków wsparcia o różnych objawach i jednej przyczynie.
Tryb turbo: filtrowanie w przeglądarce
w produkcieCo to dajeW dużych katalogach filtrowanie działa w przeglądarce kupującego, a nie na serwerze. Wyniki pojawiają się szybko, a serwer jest mniej obciążony w godzinach szczytu.
- Problem
- W dużych katalogach każde kliknięcie filtra kosztuje na serwerze zestaw złączeń taksonomii i meta oraz jedno zapytanie liczące na każdy widoczny termin.
- Rozwiązanie
Wstępnie obliczyć płaski indeks JSON produktów i wariantów, załadować go w przeglądarce i uruchomić filtr jako SQL na nim przy użyciu AlaSQL. Serwer renderuje tylko pasujące ID. Dostępne w obu edycjach.
- Technologie
Dane produktu są rozproszone po wpisach, postmeta, terminach i wariantachSpłaszczyć każdy produkt i wariant do jednego rekordu JSON: ceny, terminy, meta, stan magazynowy
get_data_by_id() zapisuje ID, tytuł, SKU, autora, rodzica, status magazynowy, slugi terminów każdej filtrowalnej taksonomii (atrybuty wariantów dla wariantów), skonfigurowane pola meta i listę par ceny regularnej i promocyjnej. Produkt z wariantami zapisuje jedną parę na każde dziecko, więc przeglądarka może obliczyć jego zakres cen tą samą regułą co WooCommerce: cena promocyjna liczy się tylko wtedy, gdy jest niższa od regularnej.
Ceny są zapisywane z podatkiem, gdy sklep wyświetla ceny z podatkiem, a FOX jest przełączany na walutę domyślną przed ich odczytem, więc indeks jest w jednej walucie.
Indeksowanie tysięcy produktów przekracza limity czasu PHPBudować w krokach AJAX po 10 produktów, jeden plik na język WPML i na blog multisite
create_data_search_files() przyjmuje offset, przetwarza 10 ID produktów i wariantów z zastosowanymi regułami widoczności, scala je z plikiem JSON i zwraca następny offset; skrypt administracyjny działa w pętli, aż dostanie -1. WPML jest przełączany na język domyślny, a każdy produkt trafia do pliku każdego języka przez swoje ID tłumaczenia. check_id_file() dodaje ID bloga w multisite, żeby witryny nie dzieliły jednego pliku.
Domyślny limit to 15,000 rekordów, regulowany filtrem woof_turbo_mode_products_limit.
Zaplanowana przebudowa nie może serwować do połowy zapisanego indeksuWłasny runner cron obsługuje porcje ID w kolejnych żądaniach i na końcu podmienia plik _temp
PN_WP_CRON_WOOF_TURBO_MODE trzyma stan zdarzenia (pełną listę ID, pozycję, następne uruchomienie) w opcji i działa na init. Każde wywołanie process() obsługuje jedną porcję 10 ID, a 5-sekundowy throttle zapisany w woof_cron_limit_do nie pozwala, by seria wyświetleń strony uruchamiała porcje jedna po drugiej. Porcje są zapisywane do data_<id>_temp_<lang>.json; gdy lista się wyczerpie, do_recreate_file() przenosi plik tymczasowy na miejsce aktywnego, a odwiedzający korzystają ze starego indeksu, dopóki nowy nie będzie kompletny.
Ceną tego rozwiązania jest nieaktualność: produkty dodane po ostatniej przebudowie są dla filtra niewidoczne, a przebudowa zależy od ruchu w witrynie tak samo jak WP-Cron.
Stan filtra musi stać się zapytaniem, które przeglądarka potrafi wykonaćGenerować klauzule WHERE dla AlaSQL z własnymi funkcjami dla zakresów cen i widoczności
turbo_mode.js zamienia bieżące wartości w ciąg WHERE: przynależność do taksonomii przez indexOf na tablicach terminów, porównania meta, LIKE dla SKU i tytułu, BETWEEN dla suwaków i dat. Własne funkcje AlaSQL obliczają minimalną i maksymalną cenę produktu z listy jego wariantów i stosują widoczność w katalogu. Dla produktów z wariantami filtr dostępności najpierw wybiera rodziców niedostępnych wariantów, które pasują do pozostałych filtrów. Przeliczanie działa na tej samej tabeli w pamięci.
Uzyskane ID są wysyłane do woof_draw_products jako turbo_mode_ids, a serwer renderuje zapytanie post__in zamiast złączeń filtra.
Przeglądarka potrzebuje ustawień, ale zawierają one sekrety serweraUsunąć z lokalizowanych ustawień dane dostępowe bazy statystyk i tajne klucze crona
JS odtwarza logikę filtra z woof_settings, więc potrzebuje na stronie większości tej opcji. get_public_settings() usuwa blok statystyk, który zawiera dane dostępowe zewnętrznego MySQL, oraz każdy cron_secret_key, a resztę przekazuje przez filtr do dalszego usuwania. W wersji premium endpoint przebudowy wymaga nonce i manage_woocommerce.
Statystyki wyszukiwania
w produkcieCo to dajeWłaściciel widzi, według czego kupujący naprawdę filtrują i czego szukają, bez spowalniania sklepu i bez zapełniania jego bazy stale rosnącym logiem.
- Problem
- Zapisywanie tego, według czego kupujący filtrują, oznacza zapis na gorącej ścieżce każdego wyświetlenia przefiltrowanej strony, od anonimowych odwiedzających i botów, często na hostingu współdzielonym.
- Rozwiązanie
Zapisywać jeden mały surowy wiersz na żądanie w tabeli pośredniej, a statystyki długoterminowe trzymać na osobnym serwerze MySQL, którego wymaga rozszerzenie.
- Technologie
Logowanie nie może mnożyć zapisów ani dawać się zalać jednemu klientowiLogować raz na żądanie, deduplikować według hasza żądanie+IP+dzień, ograniczyć liczbę wierszy w tabeli pośredniej na IP
Rozszerzenie statystyk podpina się pod woof_get_request_data, które działa wiele razy na stronę; flaga statyczna ogranicza logowanie do pierwszego wywołania, a wywołania AJAX inne niż renderowanie produktów są ignorowane. Zachowywane są tylko klucze filtrów wybrane w ustawieniach, żądanie jest przycinane do request_max_deep kluczy (domyślnie 5) z zachowaniem zakresu cen i jest haszowane razem z IP i datą.
Wiersz jest wstawiany tylko wtedy, gdy hasz jest nowy, a IP ma w tabeli pośredniej mniej niż user_max_requests wierszy (domyślnie 10).
Statystyki rosną bez ograniczeń w bazie sklepuPrzechowywać statystyki na osobnym serwerze MySQL przez PDO, jedna tabela na miesiąc
Rozszerzenie potrzebuje własnego serwera MySQL, ustawianego w opcjach. init_pdo() łączy się z nim przez PDO, bo $wpdb nie może utrzymywać drugiego połączenia z innymi danymi dostępowymi. place_statdata_into_db() tworzy tabelę na miesiąc o nazwie <year>_<month> i wstawia wiersze za pomocą prepared statements.
Raporty odpytują tabele miesięczne według zakresu dat, więc baza sklepu nie przechowuje statystyk długoterminowych.
Wyszukiwanie tekstowe i zapisane wyszukiwania
w produkcieCo to dajeKupujący znajdują produkty po SKU, atrybucie lub słowach w opisach wariantów. Zarejestrowani klienci mogą zapisać wyszukiwanie i dostać powiadomienie, gdy pojawią się nowe pasujące produkty.
- Problem
- Wyszukiwanie WordPressa obejmuje tytuł i treść, a kupujący wpisują SKU, nazwy atrybutów i słowa występujące tylko w opisach wariantów. Endpoint podpowiedzi jest publiczny, a zapisane wyszukiwania (rozszerzenie Products Messenger) trzeba później uruchomić ponownie bez ufania zapisanym danym wejściowym.
- Rozwiązanie
Budować klauzulę WHERE z przygotowanych fragmentów dla każdego słowa, z złączeniami dodawanymi tylko w razie potrzeby, rozwiązywać warianty do rodziców w osobnym zapytaniu, ograniczyć to, co może zażądać publiczny endpoint, i uruchamiać zapisane wyszukiwania ponownie z SQL przechwyconego po stronie serwera.
- Technologie
Przeszukiwać tytuł, treść, SKU, meta i nazwy terminów jednym zapytaniemPrzygotowane grupy OR dla każdego słowa, połączone przez AND, ze złączeniami dodawanymi tylko gdy są używane
create_where_query() dzieli frazę na słowa. Dla każdego słowa łączy przez OR wybrane pola wpisu i wymienione klucze meta (SKU to po prostu _sku), plus dopasowanie nazwy terminu do całej frazy, a grupy słów są łączone przez AND. Początkowy '-' zamienia słowo na NOT LIKE łączone przez AND. Fragmenty przechodzą przez $wpdb->prepare().
Złączenia terminów i postmeta są dodawane w posts_join tylko wtedy, gdy te opcje są włączone, a posts_groupby scala zduplikowane wiersze tworzone przez złączenia.
Dopasowywać słowa występujące tylko w opisach wariantówNajpierw znaleźć pasujące warianty, zmapować je na ID rodziców i dołączyć przez OR do głównej klauzuli
Warianty to osobne wpisy, a zapytanie o rodziców nie widzi _variation_description. Gdy opcja jest włączona, jedno zapytanie znajduje wpisy product_variation, których opis zawiera wszystkie słowa, drugie mapuje je na post_parent, a główna klauzula przyjmuje postać (warunki słów OR ID IN (rodzice)). Listę rodziców można cache'ować w object cache dla każdej listy słów.
Endpoint podpowiedzi jest publiczny i bierze rozmiar strony z żądaniaStronicować zapytanie w SQL; ograniczyć max_posts do 1..max(100, ustawienie administratora) (premium)
ajax_search() jest otwarte dla gości i stronicuje zapytanie w SQL z max_posts jako rozmiarem strony, więc ładowana jest tylko jedna strona dopasowań. W wersji premium max_posts z żądania jest ograniczane między 1 a większą z wartości: ustawienie administratora i filtrowalne 100. Podpowiedzi uwzględniają bieżący stan filtra, stronę kategorii i ograniczenie do promocji.
Niektóre witryny potrzebują dopasowań tekstowych z dedykowanej wyszukiwarkiUdostępnić woof_text_search_ids, by deweloper mógł dostarczyć pasujące ID
Filtr tekstowy buduje własne zapytanie wyszukiwania, więc osobna wtyczka wyszukiwania nie może rozstrzygać zestawu wyników w tym samym żądaniu; wątki wsparcia wymieniają wśród takich konfliktów Ivory Search, Relevanssi i Advanced Woo Search. get_all_ids() najpierw stosuje filtr woof_text_search_ids. Gdy callback zwraca tablicę, na przykład ID z SearchWP, ta tablica jest zbiorem dopasowań, a wbudowane SQL jest pomijane. Podłączenie konkretnej wyszukiwarki wymaga własnego kodu.
Powiadamiać zalogowanego klienta, gdy nowe produkty pasują do zapisanego filtra (rozszerzenie Products Messenger)Zapisać SQL rzeczywistego zapytania o wyniki, uruchamiać je ponownie z crona i porównywać ID produktów
Widok Products Messenger zapisuje $wp_query->request, czyli SQL wygenerowane przez WordPress dla przefiltrowanych wyników, w magazynie wtyczki przypisanym do odwiedzającego, pod ID użytkownika. Przy subskrypcji, która wymaga zalogowanego użytkownika, serwer odczytuje je z tego magazynu, a nie z żądania, usuwa LIMIT, sprawdza, że to pojedyncze SELECT bez słów kluczowych DML ani DDL, uruchamia je raz, by zapisać bazową listę ID, i zapisuje subskrypcję w user meta.
Zaplanowane zadanie uruchamia każde zapisane zapytanie ponownie i wysyła e-mailem tylko ID, których nie ma w bazowej liście, po czym rozszerza tę listę. Subskrypcja kończy się po liczbie wiadomości lub po dacie. User meta jest aktualizowane tylko wtedy, gdy wp_mail() się powiedzie, więc nieudana wysyłka jest ponawiana przy następnym uruchomieniu.
Utwardzanie i integralność ustawień
w produkcieCo to dajeAnonimowi odwiedzający nie mogą użyć filtra do uruchomienia kodu ani dostępu do ukrytych danych, a zapisane ustawienia nie giną i nie są nadpisywane przy wprowadzaniu zmian.
- Problem
- Filtr przyjmuje query string i żądania AJAX od anonimowych odwiedzających, renderuje shortcode i szablony wskazane w tych żądaniach oraz przechowuje ustawienia zawierające HTML, JavaScript i SQL. Changelog wymienia zewnętrznych badaczy za serię zgłoszeń bezpieczeństwa z lat 2022-2026.
- Rozwiązanie
Traktować każdą wartość z żądania jako dane: dopuszczać tylko znane shortcode i szablony, sanityzować ustawienia rekurencyjnie według ścieżki kluczy, wymagać uprawnień do wszystkiego, co zapisuje kod, i scalać ustawienia zamiast je nadpisywać.
- Technologie
Żądania AJAX niosą nazwę szablonu PHP do wyrenderowaniaAkceptować tylko nazwy szablonów po raz pierwszy zobaczone w treści strony; AJAX nie może dodawać nowych
[woof_products custom_tpl=...] jest zgodnie z projektem przekazywane z powrotem w wywołaniach AJAX, więc wartości nie można ani zaufać, ani odrzucić. sanitize_custom_tpl() zachowuje tylko litery, cyfry, '_', '-' i '/', co usuwa kropki i sekwencje przejścia po katalogach (traversal). Nazwa wyrenderowana z rzeczywistej treści strony jest dodawana do opcji z listą dozwolonych (zachowywane jest ostatnie 20); podczas AJAX akceptowane są tylko nazwy już znajdujące się na tej liście.
Ścieżki include budowane z nazw typów widoku przechodzą przez safe_view_slug(), które odrzuca wszystko, co nie jest zwykłym slugiem.
Żądania AJAX niosą tekst shortcode, który nie może uruchamiać dowolnych shortcodeWyciągać z przesłanego tekstu tylko własne tagi wtyczki przez get_shortcode_regex
woof_draw_products() otrzymuje ze strony tekst shortcode produktów i formularza, aby wynik AJAX odpowiadał oryginalnemu wywołaniu. check_shortcode() buduje get_shortcode_regex() tylko dla tagów wtyczki ([woof_products] lub [woof], plus aliasy shortcode produktów wtyczki) i zachowuje pierwsze dopasowanie; wszystko inne w ciągu jest odrzucane przed do_shortcode().
Ustawienia mieszają HTML, JavaScript, slugi i zwykły tekst na dowolnej głębokościSanityzować rekurencyjnie; klasyfikować każde pole według klucza lub jego ścieżki w drzewie ustawień
sanitize_array() przechodzi po drzewie ustawień, zapisuje każdą wartość pod jej sanityzowanym kluczem i klasyfikuje każde pole według nazwy lub ścieżki w drzewie: tekst reguły SEO jako HTML przez wp_kses_post, opisy jako textarea, klucze używane w ścieżkach include jako ścisłe slugi, wszystko inne jako zwykły tekst. Reguły ścieżek są potrzebne, bo klucze głęboko w drzewie, takie jak text czy description, są zbyt ogólne, by wymieniać je globalnie.
Ustawienie 'JavaScript after AJAX' mogą zmieniać tylko użytkownicy z unfiltered_html.
Odpowiedź AJAX wstrzykuje fragmenty HTML, a żądanie jest odtwarzane z URL odwiedzającegoUsunąć woof_redraw_elements z sparsowanego linku (premium); przepuścić każdy fragment przez wp_kses
Renderowanie AJAX odtwarza $_GET z linku strony wysłanego przez przeglądarkę, a odpowiedź wstrzykuje woof_redraw_elements do strony jako HTML, więc spreparowany link nie może mieć możliwości dostarczenia tego klucza. W wersji premium klucz jest usuwany ze sparsowanego linku. Prawidłowe fragmenty (tytuł SEO i H1, liczba wyników, sortowanie) są dodawane przez filtry po stronie serwera, a każdy fragment przechodzi przez wp_kses, zanim zostanie zwrócony.
Kolory i obrazy terminów powinny być ustawiane raz dla wszystkich językówScalać przesłane kolory terminów z zapisanymi; w razie braku użyć terminu z języka domyślnego
Strona ustawień wyświetla tylko terminy bieżącego języka WPML lub Polylang, więc zapis przesyła kolory tylko dla tych terminów. woof_save_options() scala zapisane wpisy color, color_img i images_term_* pod przesłanymi wartościami, dzięki czemu pozostałe języki zachowują swoje.
Przetłumaczony termin bez własnego koloru używa swojego oryginału w języku domyślnym, znajdowanego przez get_original_language_term(). Odczytuje slug bezpośrednio z wp_terms, ponieważ przy dopasowywaniu ID przez WPML get_term() może ponownie zwrócić przetłumaczony termin.
Debugowanie w terenie
support forumCo to dajeProblemy w działającym sklepie są znajdowane u ich rzeczywistej przyczyny, nawet gdy jest nią motyw, serwer, cache lub inna wtyczka.
- Problem
- Zgłoszenie z działającego sklepu wskazuje objaw, nie przyczynę. Ten sam objaw może pochodzić z szablonu motywu, warstwy cache lub optymalizacji, serwera, innej wtyczki albo samego filtra.
- Rozwiązanie
Odtworzyć błąd na czystej instalacji lub kopii witryny, wykluczać po kolei jedną warstwę i naprawiać tam, gdzie leży przyczyna: we wtyczce dla wszystkich albo ukierunkowaną zmianą na tej witrynie.
- Technologie
Przełączniki podkategorii znikają, a filtrowanie przestaje działać, gdy URL ma jakikolwiek query stringBłąd zawężono do serwera testem z dowolnym parametrem GET, po czym poprawiono rewrite Nginx
Nawet bezsensowny parametr, taki jak ?dwdadd=1adw, psuł stronę, co dowodziło, że błąd leży w obsłudze query string, a nie w filtrze. Konfiguracja Nginx wstawiała zbłąkany ? po php i odrzucała wszystkie parametry, więc filtr nigdy nie dostawał swoich argumentów.
Sklep przestaje się odświeżać po filtrowaniu, gdy zainstalowany jest LiteSpeed CacheZalogowano odpowiedź AJAX, by ujawnić błąd parsowania JSON, po czym metodą bisekcji zawężono ustawienia LiteSpeed do CDN Mapping
Odpowiedź docierała, ale JSON.parse kończył się błędem przez nieprawidłowy znak ucieczki, co oznaczało, że CDN Mapping w LiteSpeed przepisywał w niej adresy URL. Wyłączenie CDN Mapping przywróciło filtrowanie.
Przyciski Filter i Reset zmieniają URL, ale lista produktów się nie przeładowujePrzyczynę znaleziono w odchudzonej wersji jQuery, przywrócono pełne jQuery i zastąpiono zmodyfikowany rdzeń WordPressa od hostingodawcy czystą instalacją
Witryna ładowała jQuery slim, które nie ma modułu AJAX, więc żądanie przefiltrowanych wyników nigdy nie było wysyłane, choć URL zmieniał się po stronie klienta; wyłączenie wszystkich innych wtyczek nic nie zmieniło. Wersja slim pochodziła ze zmodyfikowanego rdzenia WordPressa z automatycznego instalatora hostingodawcy; przywrócenie pełnego jQuery i ponowna instalacja rdzenia rozwiązały problem.
Suwaki meta na małych wartościach dziesiętnych ACF (około .005) nie filtrują niczego albo wszystkoDodano filtr woof_slider_meta_query_type, by rzutowanie SQL można było ustawić na DECIMAL dla każdego klucza meta
Zgłaszający odtworzył pole jako zwykłe pole własne, a błąd pozostał, więc przyczyną nie był ACF; wsparcie odtworzyło problem. Zapytanie meta porównywało wartości bez rzutowania na DECIMAL, a wtyczka dostała hook woof_slider_meta_query_type, który otrzymuje $type i $key.
Filtr zawiesza się po przycisku Wstecz w przeglądarce; Search nic nie robiDodano nasłuch pageshow, który resetuje blokadę wysyłania i popup przy przywróceniu z bfcache, wydany w aktualizacjach
Strony przywrócone z pamięci podręcznej wstecz/dalej (back/forward cache) zachowywały ustawione woof_submit_link_locked i otwarty popup ładowania. Nasłuch sprawdza event.persisted lub typ nawigacji 2 i resetuje oba; zmiana trafiła do wtyczki.
Liczby
wordpress.org- Aktywne instalacje 80 000+ licznik wordpress.org.
- Opinie 336 Średnia ocena: 4.6 na 5.
- Lat wsparcia 12 Od 1 grudnia 2014, ostatnia aktualizacja 9 października 2026.