HUSKY aka WOOF
Pozwala kupującym zawęzić duży katalog według kategorii, atrybutu, ceny, dostępności i tekstu, ze stronami filtrów, które Google może zaindeksować.
Prowadzę produkt od pierwszej specyfikacji do kolejki zgłoszeń wsparcia po latach. 12 wtyczek w wordpress.org, SaaS zamieniający sklep WooCommerce w bota w Telegramie, rozszerzenie Chrome i aplikacje na Android.
const realmag777 = { role: ["Senior WordPress & WooCommerce Developer", "Plugins & Products Architect"], inIT: 2006, ownProductsSince: 2012, // first plugin on wordpress.org activeInstalls: 167_000, // sum of wordpress.org counters stack: ["PHP", "WordPress", "WooCommerce", "MySQL", "JavaScript", "Node.js", "HTML5", "CSS3", "Docker", "MCP"],}; export default realmag777;
Projekty
Każda strona projektu zawiera listę problemów rozwiązanych w produkcie, pogrupowaną według obszarów. Liczby instalacji i oceny pochodzą z wordpress.org, gdzie można przeczytać, co piszą użytkownicy: opinie o HUSKY, opinie o FOX, opinie o BEAR.
Pozwala kupującym zawęzić duży katalog według kategorii, atrybutu, ceny, dostępności i tekstu, ze stronami filtrów, które Google może zaindeksować.
Pokazuje ceny w walucie odwiedzającego i może przyjąć płatność w tej walucie, dzięki czemu klienci z zagranicy wiedzą, ile płacą.
Pozwala pracownikom edytować tysiące produktów jak w arkuszu, z możliwością cofnięcia każdej zmiany, i pozwala asystentom AI robić to bezpiecznie.
Pokazuje katalog jako sortowalne, filtrowalne tabele produktów, w których klienci dodają pozycje do koszyka prosto z każdego wiersza.
Pozwala redaktorom strony edytować, filtrować, eksportować i cofać zmiany w tysiącach wpisów, stron i własnych treści na jednym ekranie przypominającym arkusz.
Odwiedzający szukają ogłoszeń, wpisów lub produktów według kilku właściwości naraz, takich jak cena, rocznik i kategoria.
Pokazuje ceny zapisane w treści strony w walucie każdego odwiedzającego, bez potrzeby posiadania sklepu WooCommerce.
Pokazuje wpisy lub produkty jako sortowalne, przeszukiwalne tabele, które odwiedzający mogą filtrować i przeglądać na każdym urządzeniu.
Dołącz zestaw obrazów do dowolnego wpisu i pokaż je jako galerie lub slidery w dowolnym miejscu strony.
Rozwijane listy krok po kroku, takie jak marka, model i generacja, prowadzą odwiedzających przez duży katalog do właściwej strony lub produktu.
Sklep WooCommerce działający jako bot w Telegramie: klienci przeglądali ofertę, wypełniali koszyk i płacili bez opuszczania czatu.
Rozszerzenie Chrome, które wpisuje to, co mówisz, do dowolnego pola tekstowego na dowolnej stronie, z opcjonalnym oczyszczaniem tekstu przez AI.
Stoper na Android działający offline, z czasami okrążeń, który liczy poprawnie nawet wtedy, gdy aplikacja działa w tle.
Czytnik, który pokazuje tekst słowo po słowie w wybranym tempie i pamięta, gdzie przerwano czytanie.
Firmy jako bąbelki na wykresie kwadrantowym, które przesuwają się po zmianie wag modelu.
Zbieranie zdarzeń, agregacja i raporty z wykresami w panelu administracyjnym WordPress.
Kupujący subskrybuje zapytanie filtra i dostaje e-mail, gdy pojawią się nowe pasujące produkty.
Ostylowany PDF z każdym samochodem z bieżącego zestawu wyników katalogu pojazdów.
Co rozwiązano · 14 produktów
Wybierz produkt. Każda karta to jeden obszar jego działania i zaczyna się od tego, co daje właścicielowi, prostymi słowami. Pod spodem jest część inżynieryjna dla Twoich technicznych osób: problem i sposób jego rozwiązania, na podstawie kodu źródłowego. Otwórz wiersz, by zobaczyć szczegóły.
co rozwiązano · wybierz projekt
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.
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.
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().
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.
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.
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.
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ł.
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.
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.
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.
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.
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).
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ą.
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.
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.
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ć.
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.
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.
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.
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ą.
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 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.
_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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
Co 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.
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.
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.
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.
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.
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.
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.
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.
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ć.
[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.
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().
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.
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.
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.
Co to dajeProblemy w działającym sklepie są znajdowane u ich rzeczywistej przyczyny, nawet gdy jest nią motyw, serwer, cache lub inna wtyczka.
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.
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.
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.
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.
woof_slider_meta_query_type, by rzutowanie SQL można było ustawić na DECIMAL dla każdego klucza metaZgł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.
pageshow, który resetuje blokadę wysyłania i popup przy przywróceniu z bfcache, wydany w aktualizacjachStrony 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.
Wewnętrzny pierścień: główny stos · zewnętrzny pierścień: narzędzia i platformy
O mnie · technologie
Projektuję architekturę i model danych, potem piszę kod, wydaję go i wspieram. Żadna warstwa nie trafia do kogoś, kogo nie mogę sprawdzić.
Prace na zamówienie
Prace na zamówienie dla klientów, zwykle osób, które zaczęły od jednej z moich wtyczek. Nazwy i strony pozostają poufne.
Duży katalog dostawców B2B zbudowany na WordPress.
Ten sam portal WordPress.
Zaczęło się jako praca na zamówienie dla jednego właściciela sklepu, później wydane jako rozszerzenie Products Messenger dla HUSKY i MDTF.
Katalog pojazdów zbudowany na MDTF, własny dodatek dla klienta z Niemiec.
Wbudowane, a nie doklejone
Właściciele dostają liczby w tym samym panelu, w którym konfigurują wtyczkę.
Oś czasu
Tylko moje własne produkty. Daty wydań to daty dodania każdej wtyczki do wordpress.org.
Zanim napiszesz
Opisz zadanie w formularzu poniżej lub e-mailem na adres realmag777 [at] gmail.com: co ma zostać stworzone lub zmienione i na kiedy. Jeśli masz środowisko stagingowe, wspomnij o nim: tam praca jest wykonywana i sprawdzana. Odpowiadam w ciągu dwóch dni roboczych z pytaniami lub z wyceną czasu i kosztu. Praca zaczyna się, gdy uzgodnimy zakres.
Jasno opisane zadanie dostaje stałą wycenę za uzgodniony zakres. Prace otwarte, takie jak szukanie przyczyny problemu na działającej stronie czy bieżące wsparcie, są rozliczane godzinowo.
Czas środkowoeuropejski (UTC+1, latem UTC+2). Piszę i rozmawiam w językach: angielski, ukraiński i hiszpański. Większość pracy odbywa się przez e-mail i wiadomości, więc różnica czasu rzadko ma znaczenie.
Tak. Nazwy klientów i strony nigdy nie są tu pokazywane, a NDA podpisuję, gdy projekt tego wymaga.
Nie. Buduję funkcje na bazie moich wtyczek lub dowolnych innych, pracuję w kodzie napisanym przez innych developerów i projektuję nowe projekty od zera. Cudzy kod najpierw czytam i mówię, co znalazłem, zanim cokolwiek zmienię.
Sam. Architektura, kod, wydanie i wsparcie są moje, więc rozmawiasz z osobą, która to buduje, i nic nie ginie między ludźmi.
Tak, także pod marką agencji. Napisz, kim jesteś i czego potrzebujesz.
Do uzgodnienia. Opowiedz o projekcie i spodziewanym zakresie, a ustalimy to przed rozpoczęciem pracy.
W euro.
Środowiska stagingowego, czyli kopii działającej strony, z dostępem administracyjnym i do plików, oraz kopii bazy danych, gdy zadanie dotyczy danych. Jeśli nie ma staging, sam go przygotowuję. Zmiany trafiają na działającą stronę dopiero po tym, jak zadziałają na staging.
Do Ciebie, po opłaceniu pracy.
Błędy w dostarczonej pracy są poprawiane. Dalsze zmiany i wsparcie można uzgodnić osobno. Moje własne wtyczki są wspierane od ponad dziesięciu lat, więc długoterminowa opieka to normalna sytuacja, a nie wyjątek.
Kontakt
Napisz, co ma zostać stworzone lub zmienione i na kiedy. Odpowiadam w ciągu dwóch dni roboczych.
realmag777 [at] gmail.com linkedin.com/in/realmag777