Idioma

Entrar em contacto

Plugin para WooCommerce · desde 2014 · aka WOOF

HUSKY

Um filtro de produtos por facetas para WooCommerce: taxonomias, atributos, preço, stock, campos meta e texto, como widget ou shortcode, com recarregamento da página ou AJAX.

Primeira versão
Instalações ativas
80 000+
Classificação
4.6 / 5 (336)Ler avaliações
  • PHP
  • WordPress
  • WooCommerce
  • WP_Query
  • WP_Tax_Query
  • WP_Meta_Query
  • MySQL
  • Transients
  • Object cache
  • jQuery
  • AlaSQL
  • PDO
  • WPML
  • Polylang
Ver em wordpress.org wordpress.org

Sobre

O HUSKY filtra os produtos do WooCommerce por categorias, atributos, etiquetas, preço, estado de stock, avaliação, autor, SKU, campos meta personalizados e texto. Está no wordpress.org desde 2014. As edições gratuita e premium são construídas a partir de uma única base de código; a edição é escolhida pelo número da versão principal, e as partes exclusivas da premium ficam desativadas ou limitadas na versão gratuita.

Cada estado do filtro acaba numa consulta WordPress sobre wp_posts, wp_postmeta e as tabelas de termos, e as lojas têm dezenas de milhares de produtos e variações. Mostrar quantos produtos cada opção devolveria significa mais uma consulta por cada termo visível em cada visualização de página. Produtos variáveis, visibilidade do catálogo, impostos e plugins de moeda alteram, cada um, o que conta como correspondência.

O plugin também corre dentro de um tratamento de pedidos que não lhe pertence: a interpretação de reescrita do WordPress, a consulta principal de produtos e os templates do WooCommerce, construtores de páginas, plugins multilingues e camadas de cache. Grande parte do código descrito abaixo existe porque uma dessas camadas tornou a abordagem direta errada ou demasiado lenta.

HUSKY · filtro de produtos

o que foi resolvido

ATIVOdesde 2014

URLs SEO para estados de filtro

no produto

O que ofereceAs páginas filtradas obtêm endereços limpos e estáveis que os motores de pesquisa podem indexar. O proprietário decide que páginas de filtro aparecem no Google e com que título.

Problema
Os estados de filtro são query strings, que os motores de pesquisa ou ignoram ou rastreiam sem limite. URLs em caminho como /swoof/color-red/size-m/ são desconhecidos das regras de reescrita do WordPress e entram em conflito com os redirecionamentos canónicos.
Solução

Intercetar a interpretação do pedido, retirar o segmento do filtro, deixar o WordPress corresponder ao resto e reconstruir o estado do filtro a partir do segmento, com uma codificação fixa e regras de índice por profundidade.

Stack
  • WordPress rewrite API
  • PHP
  • JavaScript
  • Yoast SEO
Tarefas · 6
/shop/swoof/color-red/size-xl/ não corresponde a nenhuma regra de reescrita e devolve 404Cortar o segmento do filtro, encaminhar o resto pelas regras de reescrita do núcleo e reconstruir os filtros a partir dele

Um link de filtro é o URL do arquivo mais o slug do filtro (swoof por omissão) e um segmento por filtro: /product-category/shoes/swoof/color-red-and-blue/size-xl/. O plugin não regista regras de reescrita. url_process() corre em do_parse_request no front end e retorna de imediato, a menos que get_search_request() encontre /<slug>/ no caminho. Toma o segmento do filtro até /page/, ? ou #, remove-o e faz corresponder o restante (/product-category/shoes/ ou /shop/page/2) a $wp_rewrite->wp_rewrite_rules() com código copiado de WP::parse_request(): regras de página verbosas, variáveis de consulta públicas e privadas, tipos de conteúdo visíveis, taxonomias não consultáveis, wp_resolve_numeric_slug_conflicts() e o filtro request. Depois chama query_posts(), handle_404() e register_globals() (a ação parse_request antes do WordPress 6.0) e devolve false, para que o núcleo não volte a interpretar o URL.

parse_url_query() divide o segmento por '/' e faz corresponder cada parte ao mapa de nomes de get_all_items(): slugs de atributos sem pa_, slugs de taxonomias traduzidos pelo WPML, price e as palavras fixas instock, onsale, featured, backorder_not_in. -and- e -or- passam a vírgulas, -to- a um separador de intervalo, + a um espaço; price-10-to-50 passa a min_price e max_price. add_request() junta o resultado através do filtro woof_get_request_data, de modo que get_request_data() devolve o mesmo array que para uma query string ?swoof=1&pa_color=red. Na consulta principal, parse_query() define então post_type product, marca a consulta como arquivo de tipo de conteúdo e acrescenta as meta queries, e tax_query() em woocommerce_product_query_tax_query substitui as entradas do WooCommerce para as mesmas taxonomias pela tax query do plugin.

Como o restante é encaminhado pelas regras existentes, a base da loja, a base das categorias de produto e a paginação dos arquivos continuam a funcionar sem novas regras: /page/N/ é cortado antes de o segmento do filtro ser localizado e permanece no caminho encaminhado. parse_query() desativa is_tax nas páginas de filtro, pelo que a categoria atual é recuperada a partir do último segmento do URL limpo (get_term_from_cleared_url()). Quando a página da loja é também a página inicial, a correção de redirect_canonical abaixo impede o WooCommerce de redirecionar o URL do filtro para /.

Para um URL que corresponde a uma regra SEO, meta_head() imprime o canónico: o URL do filtro sem query string, se o estado for indexável, com os canónicos do Yoast e do SEOPress desativados; caso contrário, o URL cortado no slug do filtro, que é o arquivo simples. Com o Yoast SEO, sitemap_index() em wpseo_sitemap_index acrescenta os URLs que um administrador lista numa textarea das definições, um por linha; a build premium escapa-os com esc_url_raw() e esc_xml().

A ordem pela qual os filtros são usados não deve alterar o URLEscrever os filtros por ordem de chave; espelhar em PHP e JS a gramática -and- / -to- / +

woof_get_submit_link() em url_parser.js ordena os filtros ativos por chave e escreve cada um como <name>-<values>, com ',' como -and-, um separador de intervalo como -to- e espaços como +. O preço passa a price-<min>-to-<max>; em stock, em promoção, em destaque e backorder passam a palavras fixas. parse_url_query() inverte o mapeamento.

Por causa da ordenação, escolher primeiro a cor e depois o tamanho dá o mesmo caminho que o inverso. Os valores dentro de um mesmo filtro são escritos pela ordem em que foram selecionados.

O atributo pa_niveau e o campo meta niveau pedem o mesmo nome de URLDetetar colisões de nomes: a taxonomia mantém o nome curto, o outro recebe um nome alternativo único

Os nomes de URL perdem o prefixo pa_, pelo que um atributo e uma chave meta podem produzir o mesmo nome, e uma entrada do mapa esconderia a outra. resolve_seo_key_conflict() dá o nome curto à taxonomia registada, independentemente da ordem dos filtros nas definições, para que os URLs existentes continuem válidos, e deriva um nome para o outro filtro a partir da sua chave real, acrescentando um sufixo numérico se necessário. Os slugs de taxonomias traduzidos pelo WPML são resolvidos antes deste passo.

Cada consulta de recontagem volta a ler o estado do filtro a partir do caminho do URLMemorizar parse_url_query() por URL, mas só depois de wp_loaded

get_request_data() é chamado a partir de cada consulta de recontagem e, com URLs SEO, cada chamada passa por woof_get_request_data até parse_url_query(). Com Dynamic Recount e Hide Empty (premium), uma página pode fazer mais de mil destas chamadas, pelo que o resultado da interpretação é guardado em cache por URL.

A cache só é ativada depois de wp_loaded, para que uma chamada feita antes de as taxonomias e as definições existirem não possa fixar um mapa de nomes incompleto durante o resto do pedido.

Que páginas de filtro indexar, e com que título e metaIndexar por profundidade e por regras regex com meta por regra; ignorar em silêncio os padrões inválidos

WOOF_SEO conta os filtros ativos. Acima de index_deep (2 por omissão), a página recebe a definição 'Disable page indexing' (sim por omissão, ou seja, noindex,nofollow através de wp_robots) e as regras não são verificadas. Até esse valor, uma regra correspondente torna a página indexável. As regras são padrões de URL com marcadores {any} compilados para expressões regulares, cada uma com título, meta descrição, H1 e texto SEO que podem incluir os nomes dos termos selecionados, e são guardadas por idioma. As etiquetas canónicas só são impressas para URLs que correspondem a uma regra.

Na build premium, preg_match corre sob um gestor de erros temporário, de modo que uma regra cujo URL não é uma expressão válida é ignorada e listada num aviso de administração, e uma regra vazia é ignorada. A edição gratuita usa apenas as duas primeiras regras.

O WooCommerce 11 redirecionava os URLs de filtro numa página inicial que é a lojaCancelar apenas o redirecionamento para home_url, e só para URLs que contêm o slug do filtro

Quando a página da loja é também a página inicial estática, o WooCommerce 11.0.0 define-a como o objeto consultado nos arquivos de produtos. redirect_canonical() passa então a tratar /swoof/... como uma cópia não canónica da página inicial e redireciona para /, perdendo todos os filtros. fix_front_page_shop_canonical() devolve false apenas quando a página inicial é a página da loja, o caminho contém o slug do filtro e o destino do redirecionamento é o URL da página inicial, de modo que a normalização de barras e slugs continua a funcionar em todos os outros casos.

Recontagem dinâmica e cache de consultas

no produto

O que ofereceCada opção do filtro mostra quantos produtos devolverá, pelo que os compradores evitam becos sem saída. As contagens ficam em cache, por isso a loja mantém-se rápida em catálogos grandes.

Problema
Cada opção do filtro mostra quantos produtos devolveria com a seleção atual. Isso é uma consulta de produtos por cada termo visível em cada visualização de página, e é o principal custo em catálogos grandes.
Solução

As contagens vêm de uma consulta simplificada, só de IDs, que reproduz a semântica do filtro. Os resultados ficam em cache por pedido, numa tabela dedicada e em transients, e o formulário pode ser diferido até o visitante o pedir.

Stack
  • WP_Query
  • MySQL
  • Transients
  • Object cache
Tarefas · 6
A contagem de um termo tem de refletir o resto da seleção atualReconstruir a consulta por termo, trocando o termo segundo as regras de seleção simples ou múltipla

dynamic_count() reconstrói a tax_query para cada termo. Os termos já selecionados não recebem contagem. Em radio e select (single), o termo substitui o valor selecionado da sua taxonomia; em checkbox e multi-select (multi), é acrescentado a esse valor. Os termos da página de categoria atual e da taxonomia indicada no atributo taxonomies do shortcode entram sempre com AND, de modo que as contagens numa página de categoria ficam dentro dessa categoria.

A lógica por taxonomia (OR, AND, NOT IN) definida nas opções, os intervalos de preço e avaliação, as condições meta das extensões, o idioma do WPML e a visibilidade do catálogo são aplicados da mesma forma que na consulta de produtos. As extensões declaram o seu comportamento de recontagem através de html_type_dynamic_recount_behavior, pelo que os tipos de cor, imagem e etiqueta reutilizam este código.

Esconder as opções sem resultados assenta nestas contagens: o termo é contado e ignorado quando a contagem é 0, pelo que não pode ser mais barato do que mostrar contagens. Esta opção é exclusiva da premium; a edição gratuita força-a a desligada nas vistas.

Uma consulta de contagem não deve carregar posts nem executar FOUND_ROWSCriar uma subclasse de WP_Query com os métodos de found-rows e de ciclo vazios, obter só os IDs

WP_QueryWOOFCounterIn estende WP_Query e substitui set_found_posts(), setup_postdata(), the_post() e have_posts(). A consulta de contagem corre com fields=ids e nopaging, pelo que o MySQL devolve uma lista de IDs, o WordPress não constrói objetos de post e não emite uma segunda consulta FOUND_ROWS. A contagem é o comprimento da lista.

Os filtros posts_clauses de popularidade e avaliação do WooCommerce são removidos antes da consulta de contagem, porque a ordenação não importa para uma contagem e esses filtros acrescentam joins ao SQL.

As mesmas contagens são recalculadas para cada visitante e páginaGuardar as contagens numa tabela chave-valor indexada pelo md5 dos argumentos completos da consulta, esvaziada por cron

Com 'cache count data' ativo, WP_QueryWoofCounter calcula o hash do JSON dos argumentos finais da consulta, que já incluem as partes de idioma e visibilidade, e procura o hash em {prefix}woof_query_cache, uma tabela de duas colunas com o hash como chave primária. As escritas usam INSERT ... ON DUPLICATE KEY UPDATE, pelo que duas visualizações de página a escrever a mesma chave em simultâneo não falham. Um site com cache de objetos persistente pode mover as contagens para wp_cache_* através do filtro woof_use_wp_cache.

Não há maneira barata de saber quais as contagens em cache afetadas por um produto novo ou editado, pelo que um evento agendado esvazia a tabela inteira no período configurado. Se o utilizador MySQL não tiver o privilégio CREATE, a página de definições imprime o SQL a executar manualmente e mantém a opção desligada.

A consulta de mín./máx. do slider de preço percorre o postmeta em lojas grandesMemorizar mín./máx. por hash do SQL, por pedido e num transient, e limpar ao guardar um produto

get_filtered_price() constrói um SELECT MIN/MAX sobre _price, ligado à tax query e meta query atuais. O mesmo intervalo era pedido várias vezes por página (mín. e máx. para cada filtro de preço), pelo que o resultado é memorizado num array estático indexado pelo md5 do SQL. Com 'Optimize price filter' ativo, os resultados vão também para um transient durante um dia, e qualquer woocommerce_update_product apaga-o. O SQL passa por woof_get_filtered_price_query, pelo que um site pode acrescentar dicas de índice.

As árvores de termos são reconstruídas de raiz para cada taxonomia em cada páginaGuardar em cache as árvores montadas em transients indexados pelos argumentos e pelo idioma atual

WOOF_HELPER::get_terms() carrega todos os termos de uma taxonomia com get_categories() e monta a árvore pai-filho em PHP. O resultado é memorizado por pedido e, com 'Cache terms' ativo, guardado num transient cuja chave inclui a taxonomia, hide_empty, os indicadores de profundidade e o idioma atual do WPML ou Polylang, de modo que cada idioma tem a sua árvore em cache.

A limpeza percorre wp_options à procura de _transient_woof_terms_cache_% e apaga cada transient, uma vez que o WordPress não tem eliminação por prefixo para transients.

A recontagem corre mesmo que o visitante nunca abra o filtroApresentar um botão em vez do formulário e obter o formulário por AJAX no primeiro clique

Com start_filtering_btn=1, o servidor devolve apenas um botão, pelo que não correm consultas de termos no carregamento da página. Ao clicar, front.js envia woof_draw_products com o shortcode de produtos definido como 'woof_nothing': o servidor apresenta o formulário do filtro e nenhum produto, a zona de redesenho é substituída e os widgets são reinicializados. O botão é omitido quando já há uma pesquisa ativa, para que uma página filtrada continue a mostrar a sua seleção.

Esconder o formulário com CSS ou regras de display não poupa nada, porque as contagens são calculadas no servidor antes de o HTML ser enviado. Os tópicos de desempenho no fórum de suporte tiveram muitas vezes de esclarecer este ponto.

Trabalhar dentro da consulta de produtos do WooCommerce

no produto

O que ofereceO filtro continua a funcionar com o tema da própria loja, as páginas de categoria, produtos variáveis, impostos e moedas, pelo que os compradores nunca veem artigos em tamanhos esgotados.

Problema
O filtro altera a consulta principal da loja e depende de templates do WooCommerce que não controla. Tem de continuar a funcionar através das mudanças no modelo de dados do WooCommerce, com produtos variáveis, visibilidade do catálogo, impostos e plugins de moeda.
Solução

Ligar-se tarde a parse_query e à consulta de produtos do WooCommerce, restaurar o estado de arquivo que os templates esperam e tratar cada diferença do modelo de dados de forma explícita, por versão ou por tabela.

Stack
  • WP_Query
  • WooCommerce
  • MySQL
  • jQuery
Tarefas · 6
Os resultados filtrados têm de ser apresentados pelo template da loja do temaReescrever os indicadores da consulta principal em parse_query com prioridade 9999 para que seja apresentada como arquivo de produtos

Quando o slug de pesquisa está no pedido, parse_query() define post_type=product e is_post_type_archive, e limpa is_tax, is_tag, is_home, is_single e is_search. O WooCommerce e o tema apresentam então o ciclo da loja para qualquer URL filtrado. As extensões acrescentam as suas condições meta através de assemble_query_params(), e os operadores por taxonomia vêm do filtro woof_main_query_tax_relations.

Os mesmos hooks não devem tocar nos pedidos REST e Store API. Os handlers retornam cedo em URLs /wp-json/ e parse_query é desligado em rest_api_init, pelo que temas de blocos e clientes headless recebem os resultados do próprio WooCommerce.

Filtrar numa página de categoria perde a categoriaTransportar o termo como really_curr_tax e guardá-lo num transient indexado pelo IP do visitante

Um URL de filtro já não é o arquivo do termo, pelo que get_queried_object() deixa de devolver a categoria. Quando o arquivo é apresentado, set_really_current_term() guarda o termo através de WOOF_STORAGE, que o plugin executa em modo transient: um transient por visitante, indexado por um hash do IP do visitante, mantido durante um dia. O JS acrescenta também really_curr_tax=<term_id>-<taxonomy> ao link do filtro, e get_really_current_term() recorre a isso quando nada está guardado. O termo é depois combinado com AND na consulta de produtos, nas consultas de recontagem e na pesquisa de texto. Os pedidos AJAX leem o termo guardado, mas nunca o substituem.

A visibilidade do catálogo é guardada de forma diferente consoante a versão do WooCommerceRamificar por versão: meta _visibility antes da 3.0, product_visibility NOT IN depois

O WooCommerce 3.0 moveu os indicadores de visibilidade, destaque e sem stock do postmeta para a taxonomia product_visibility. product_visibility_not_in() converte os indicadores necessários em cláusulas term_taxonomy_id NOT IN. generate_visibility_keys() acrescenta outofstock quando a loja esconde artigos sem stock; quando a opção 'listen catalog visibility' está ativa, acrescenta também exclude-from-search sempre e exclude-from-catalog fora de uma pesquisa. As lojas mais antigas recebem, em vez disso, uma condição meta _visibility.

As mesmas regras são aplicadas à consulta principal, à consulta do shortcode, às consultas de recontagem, à pesquisa de texto e aos dados do modo turbo. Se uma delas diferir, as contagens e os resultados não coincidem.

Produto variável mostrado quando o tamanho escolhido está esgotadoEncontrar na tabela de consulta de atributos os produtos pai de variações correspondentes esgotadas e excluí-los

Um produto variável conta como em stock se alguma variação estiver, pelo que uma consulta simples por 'em stock + tamanho M' devolve uma camisola cuja variação M está esgotada. Quando o filtro de stock é combinado com filtros de atributos, posts_where() reúne os termos pa_* selecionados, incluindo o termo que está a ser contado durante a recontagem, e executa duas consultas agrupadas em wc_product_attributes_lookup: os produtos pai com uma variação correspondente sem stock e os produtos pai com uma variação em stock. A diferença é acrescentada como ID NOT IN.

Um modo mais antigo, selecionável nas definições do filtro de stock, consulta os posts product_variation por meta de atributo e _stock_status em vez da tabela de consulta. É mais lento e, por omissão, exclui todos os produtos pai que têm uma variação correspondente esgotada; os pais que também têm uma correspondência em stock só são mantidos quando o filtro woof_exclude_existing_variations devolve true.

Os limites de preço diferem dos preços guardados quando há impostos e moedasConverter os limites de volta para a moeda base e retirar o imposto incluído antes da meta query

_price é guardado na moeda base da loja e, em muitas lojas, sem impostos. Se o visitante usa uma moeda do WPML (WCML), assemble_price_params() converte os limites de volta para a moeda base. Se os preços são apresentados com impostos mas guardados sem eles, o imposto incluído da taxa normal é subtraído a ambos os limites. Para o extremo superior do slider, get_max_price() encontra o produto com o preço máximo e pede ao WooCommerce o seu preço com impostos.

Com o FOX (WOOCS), os valores do slider apresentados são convertidos através de woocs_exchange_value, enquanto a consulta mantém números na moeda base.

Modo AJAX: redesenhar resultados, contagens e partes da página sem recarregar a páginaUm endpoint AJAX apresenta produtos e formulário através de shortcodes; o JS troca as zonas e atualiza o URL

woof_draw_products() reconstrói $_GET a partir do link que o visitante teria aberto, apresenta [woof_products] e [woof] com esse estado e devolve produtos, formulário e fragmentos adicionais (contagem de resultados, ordenação, paginação) em JSON. front.js substitui cada fragmento, repõe os fragmentos que a resposta não incluía, reinicializa os widgets e chama history.pushState para que o URL possa ser partilhado. onpopstate recarrega a página quando a query string mudou. Um indicador de bloqueio descarta cliques enquanto um pedido está em curso.

O ciclo da loja é envolvido ligando-se a woocommerce_before_shop_loop e woocommerce_after_shop_loop. Os temas que não disparam ambos os hooks quebram o modo AJAX, o que explica uma família de tópicos de suporte com sintomas diferentes e uma só causa.

Modo turbo: filtrar no navegador

no produto

O que ofereceEm catálogos grandes, a filtragem corre no navegador do comprador em vez de no servidor. Os resultados aparecem depressa e o servidor tem menos carga nas horas de maior movimento.

Problema
Em catálogos grandes, cada clique no filtro custa ao servidor um conjunto de joins de taxonomias e meta, mais uma consulta de contagem por cada termo visível.
Solução

Pré-calcular um índice JSON plano de produtos e variações, carregá-lo no navegador e executar o filtro como SQL sobre ele com o AlaSQL. O servidor apenas apresenta os IDs correspondentes. Incluído em ambas as edições.

Stack
  • PHP
  • JSON
  • AlaSQL
  • JavaScript
  • Cron
Tarefas · 5
Os dados do produto estão espalhados por posts, postmeta, termos e variaçõesAchatar cada produto e variação num único registo JSON: preços, termos, meta, stock

get_data_by_id() escreve ID, título, SKU, autor, pai, estado de stock, os slugs dos termos de todas as taxonomias filtráveis (atributos de variação, no caso das variações), os campos meta configurados e uma lista de pares de preço normal/promocional. Um produto variável guarda um par por filho, para que o navegador possa calcular o seu intervalo de preços com a mesma regra que o WooCommerce usa: o preço promocional só conta quando é inferior ao preço normal.

Os preços são guardados com impostos quando a loja apresenta preços com impostos incluídos, e o FOX é passado para a moeda predefinida antes de os ler, para que o índice fique numa única moeda.

Indexar milhares de produtos atinge os limites de tempo do PHPConstruir em passos AJAX de 10 produtos, um ficheiro por idioma do WPML e por blog multisite

create_data_search_files() recebe um deslocamento, processa 10 IDs de produtos e variações com as regras de visibilidade aplicadas, junta-os ao ficheiro JSON e devolve o deslocamento seguinte; o script de administração repete o ciclo até receber -1. O WPML é passado para o idioma predefinido e cada produto é escrito no ficheiro de cada idioma através do seu ID de tradução. check_id_file() acrescenta o ID do blog em multisite para que os sites não partilhem um ficheiro.

O limite predefinido é de 15,000 registos, ajustável com o filtro woof_turbo_mode_products_limit.

Uma reconstrução agendada não deve servir um índice escrito a meioUm executor cron próprio trata fatias de IDs por pedido e troca um ficheiro _temp no final

PN_WP_CRON_WOOF_TURBO_MODE guarda o estado do evento (lista completa de IDs, posição, próxima execução) numa opção e corre em init. Cada chamada de process() trata uma fatia de 10 IDs, e uma limitação de 5 segundos guardada em woof_cron_limit_do impede que uma rajada de visualizações de página execute fatias em sequência. As fatias são escritas em data_<id>_temp_<lang>.json; quando a lista se esgota, do_recreate_file() move o ficheiro temporário para cima do ficheiro ativo, e os visitantes mantêm o índice antigo até o novo estar completo.

O custo deste desenho é a desatualização: os produtos adicionados depois da última reconstrução ficam invisíveis para o filtro, e a reconstrução depende do tráfego do site da mesma forma que o WP-Cron.

O estado do filtro tem de se tornar uma consulta que o navegador consiga executarGerar cláusulas WHERE do AlaSQL com funções personalizadas para intervalos de preço e visibilidade

turbo_mode.js converte os valores atuais numa string WHERE: pertença a taxonomias através de indexOf nos arrays de termos, comparações meta, LIKE para SKU e título, BETWEEN para sliders e datas. Funções AlaSQL personalizadas calculam o preço mínimo e máximo de um produto a partir da sua lista de variações e aplicam a visibilidade do catálogo. Para produtos variáveis, o filtro de stock seleciona primeiro os pais das variações sem stock que correspondem aos outros filtros. A recontagem corre sobre a mesma tabela em memória.

Os IDs resultantes são enviados para woof_draw_products como turbo_mode_ids, e o servidor apresenta uma consulta post__in em vez dos joins do filtro.

O navegador precisa das definições, mas estas contêm segredos do servidorRetirar das definições localizadas as credenciais da base de dados de estatísticas e as chaves secretas do cron

O JS reconstrói a lógica do filtro a partir de woof_settings, pelo que precisa de grande parte dessa opção na página. get_public_settings() remove o bloco de estatísticas, que contém as credenciais do MySQL externo, e todas as cron_secret_key, e passa o resto por um filtro para mais remoções. Na build premium, o endpoint de reconstrução exige um nonce e manage_woocommerce.

Estatísticas de pesquisa

no produto

O que ofereceO proprietário vê o que os compradores realmente filtram e pesquisam, sem tornar a loja mais lenta nem encher a sua base de dados com um registo em crescimento permanente.

Problema
Registar o que os compradores filtram implica escrever no caminho crítico de cada visualização de página filtrada, por parte de visitantes anónimos e bots, muitas vezes em alojamento partilhado.
Solução

Escrever uma pequena linha em bruto por pedido numa tabela de preparação e manter as estatísticas de longo prazo num servidor MySQL separado, exigido pela extensão.

Stack
  • MySQL
  • PDO
Tarefas · 2
O registo não deve multiplicar as escritas nem ser inundado por um só clienteRegistar uma vez por pedido, eliminar duplicados por hash de pedido+IP+dia, limitar as linhas em preparação por IP

A extensão de estatísticas liga-se a woof_get_request_data, que corre várias vezes por página; um indicador estático limita o registo à primeira chamada, e as chamadas AJAX que não sejam a apresentação de produtos são ignoradas. Apenas as chaves de filtro escolhidas nas definições são mantidas, o pedido é cortado para request_max_deep chaves (5 por omissão) preservando o intervalo de preço, e é calculado o hash em conjunto com o IP e a data.

Uma linha só é inserida se esse hash for novo e o IP tiver menos de user_max_requests linhas (10 por omissão) na tabela de preparação.

As estatísticas crescem sem limite dentro da base de dados da lojaGuardar as estatísticas num servidor MySQL separado através de PDO, uma tabela por mês

A extensão precisa do seu próprio servidor MySQL, indicado nas opções. init_pdo() liga-se a ele com PDO, uma vez que $wpdb não consegue manter uma segunda ligação com outras credenciais. place_statdata_into_db() cria uma tabela por mês com o nome <year>_<month> e insere as linhas com prepared statements.

Os relatórios consultam as tabelas mensais por intervalo de datas, pelo que a base de dados da loja não guarda as estatísticas de longo prazo.

Reforço de segurança e integridade das definições

no produto

O que ofereceOs visitantes anónimos não podem usar o filtro para executar código nem aceder a dados ocultos, e as definições guardadas não se perdem nem são sobrescritas quando se fazem alterações.

Problema
O filtro recebe query strings e envios AJAX de visitantes anónimos, apresenta shortcodes e templates indicados nesses pedidos, e guarda definições que contêm HTML, JavaScript e SQL. O changelog atribui a investigadores externos uma série de relatórios de segurança entre 2022 e 2026.
Solução

Tratar cada valor do pedido como dados: permitir apenas shortcodes e templates conhecidos, sanear as definições recursivamente por caminho de chave, exigir capacidades para tudo o que guarda código e juntar as definições em vez de as sobrescrever.

Stack
  • PHP
  • WordPress sanitization API
  • WPML
  • Polylang
Tarefas · 5
Os pedidos AJAX trazem o nome de um template PHP a apresentarAceitar apenas nomes de templates vistos primeiro no conteúdo da página; o AJAX não pode acrescentar novos

[woof_products custom_tpl=...] é legitimamente devolvido nas chamadas AJAX, pelo que o valor não pode ser nem aceite sem verificação nem descartado. sanitize_custom_tpl() mantém apenas letras, dígitos, '_', '-' e '/', o que remove pontos e sequências de travessia. Um nome apresentado a partir de conteúdo real da página é acrescentado a uma opção de lista de permissões (mantêm-se os últimos 20); durante o AJAX, só são aceites nomes que já estejam nessa lista.

Os caminhos de inclusão construídos a partir de nomes de tipos de vista passam por safe_view_slug(), que rejeita tudo o que não seja um slug simples.

Os pedidos AJAX trazem texto de shortcode que não deve executar shortcodes arbitráriosExtrair do texto enviado apenas as tags do próprio plugin com get_shortcode_regex

woof_draw_products() recebe da página o texto dos shortcodes de produtos e de formulário para que o resultado AJAX coincida com a chamada original. check_shortcode() constrói get_shortcode_regex() apenas para as tags do plugin ([woof_products] ou [woof], mais os alias do shortcode de produtos do plugin) e mantém a primeira correspondência; tudo o resto na string é descartado antes de do_shortcode().

As definições misturam HTML, JavaScript, slugs e texto simples a qualquer profundidadeSanear recursivamente; classificar cada campo pela sua chave ou pelo seu caminho na árvore de definições

sanitize_array() percorre a árvore de definições, guarda cada valor sob a sua chave saneada e classifica cada campo pelo nome ou pelo caminho na árvore: o texto das regras SEO como HTML através de wp_kses_post, as descrições como textarea, as chaves usadas em caminhos de inclusão como slugs estritos, tudo o resto como texto simples. As regras por caminho são necessárias porque chaves em profundidade na árvore, como text ou description, são demasiado genéricas para serem listadas globalmente.

A definição 'JavaScript after AJAX' só pode ser alterada por utilizadores com unfiltered_html.

A resposta AJAX injeta fragmentos HTML, e o pedido é reconstruído a partir do URL do visitanteRemover woof_redraw_elements do link interpretado (premium); passar todos os fragmentos por wp_kses

A apresentação AJAX reconstrói $_GET a partir do link da página enviado pelo navegador, e a resposta injeta woof_redraw_elements na página como HTML, pelo que um link forjado não pode fornecer essa chave. Na build premium, a chave é removida do link interpretado. Os fragmentos legítimos (título SEO e H1, contagem de resultados, ordenação) são acrescentados por filtros do lado do servidor, e todos os fragmentos passam por wp_kses antes de serem devolvidos.

As cores e imagens dos termos devem ser definidas uma vez para todos os idiomasJuntar as cores por termo submetidas às guardadas; recorrer ao termo do idioma predefinido

A página de definições lista apenas os termos do idioma atual do WPML ou Polylang, pelo que um envio contém cores apenas para esses termos. woof_save_options() junta as entradas guardadas de color, color_img e images_term_* por baixo dos valores submetidos, para que os outros idiomas mantenham as suas.

Um termo traduzido sem cor própria usa o seu original no idioma predefinido, encontrado por get_original_language_term(). Lê o slug diretamente de wp_terms, porque com o ajuste de ID do WPML get_term() pode devolver de novo o termo traduzido.

Depuração em ambiente real

support forum

O que ofereceOs problemas numa loja em produção são encontrados na sua causa real, mesmo quando é o tema, o servidor, uma cache ou outro plugin.

Problema
Um relatório de uma loja em produção indica um sintoma, não uma causa. O mesmo sintoma pode vir de um template do tema, de uma camada de cache ou otimização, do servidor, de outro plugin ou do próprio filtro.
Solução

Reproduzir numa instalação limpa ou numa cópia do site, excluir uma camada de cada vez e corrigir onde está a causa: no plugin, para todos, ou com uma alteração dirigida nesse site.

Stack
  • Browser DevTools
  • Nginx
  • LiteSpeed Cache
  • jQuery
  • ACF
  • MySQL
Tarefas · 5
Os botões de subcategorias desaparecem e a filtragem falha sempre que o URL tem uma query stringIsolada a falha no servidor com um teste de parâmetro GET arbitrário; depois corrigida a reescrita do Nginx

Mesmo um parâmetro sem significado como ?dwdadd=1adw quebrava a página, o que provou que a falha estava no tratamento da query string e não no filtro. A configuração do Nginx inseria um ? a mais depois de php e descartava todos os parâmetros, pelo que o filtro nunca recebia os seus argumentos.

A loja deixa de atualizar após filtrar, depois de instalar o LiteSpeed CacheRegistada a resposta AJAX para expor um erro de análise JSON; depois isoladas as definições do LiteSpeed por bissecção até ao CDN Mapping

A resposta chegava, mas JSON.parse falhava com um carácter de escape inválido, o que significava que o CDN Mapping do LiteSpeed estava a reescrever URLs dentro dela. Desligar o CDN Mapping repôs a filtragem.

Os botões Filter e Reset atualizam o URL, mas a lista de produtos nunca é recarregadaRastreado até à build slim do jQuery; reposto o jQuery completo e substituído o núcleo do WordPress modificado do alojamento por uma instalação limpa

O site carregava o jQuery slim, que não tem módulo AJAX, pelo que o pedido dos resultados filtrados nunca era enviado enquanto o URL mudava do lado do cliente; desativar todos os outros plugins não mudou nada. A build slim vinha com um núcleo do WordPress modificado pelo instalador automático do alojamento; repor o jQuery completo e reinstalar o núcleo resolveu o problema.

Os sliders de meta sobre valores decimais pequenos do ACF (cerca de .005) não filtram nada ou filtram tudoAcrescentado o filtro woof_slider_meta_query_type para que a conversão SQL possa ser definida como DECIMAL por chave meta

O autor do relatório reconstruiu o campo como campo personalizado simples e a falha manteve-se, pelo que o ACF não era a causa; o suporte reproduziu-a. A meta query comparava os valores sem conversão para DECIMAL, e o plugin ganhou o hook woof_slider_meta_query_type, que recebe $type e $key.

O filtro bloqueia depois de usar o botão Voltar do navegador; a Pesquisa não faz nadaAcrescentado um listener pageshow que repõe o bloqueio de envio e o popup ao restaurar do bfcache, incluído nas atualizações

As páginas restauradas da cache de recuo/avanço mantinham woof_submit_link_locked definido e o popup de carregamento aberto. O listener verifica event.persisted ou o tipo de navegação 2 e repõe ambos; a alteração foi incluída no plugin.

Números

wordpress.org
  • Instalações ativas 80 000+ contador do wordpress.org.
  • Avaliações 336 Média de 4.6 em 5.
  • Anos de suporte 12 Desde 1 de dezembro de 2014, última atualização em 9 de outubro de 2026.