Język

Napisz do mnie

Wtyczka WooCommerce · od 2014 · aka WOOF

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
4.6 / 5 (336)Czytaj opinie
  • PHP
  • WordPress
  • WooCommerce
  • WP_Query
  • WP_Tax_Query
  • WP_Meta_Query
  • MySQL
  • Transients
  • Object cache
  • jQuery
  • AlaSQL
  • PDO
  • WPML
  • Polylang

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

AKTYWNYod 2014

Adresy SEO dla stanów filtra

w produkcie

Co 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
  • WordPress rewrite API
  • PHP
  • JavaScript
  • Yoast SEO
Zadania · 6
/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 produkcie

Co 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
  • WP_Query
  • MySQL
  • Transients
  • Object cache
Zadania · 6
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 produkcie

Co 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
  • WP_Query
  • WooCommerce
  • MySQL
  • jQuery
Zadania · 6
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 produkcie

Co 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
  • PHP
  • JSON
  • AlaSQL
  • JavaScript
  • Cron
Zadania · 5
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 produkcie

Co 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
  • MySQL
  • PDO
Zadania · 2
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.

Utwardzanie i integralność ustawień

w produkcie

Co 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
  • PHP
  • WordPress sanitization API
  • WPML
  • Polylang
Zadania · 5
Żą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 forum

Co 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
  • Browser DevTools
  • Nginx
  • LiteSpeed Cache
  • jQuery
  • ACF
  • MySQL
Zadania · 5
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.