MDTF
Búsqueda facetada para entradas y tipos de contenido personalizados de WordPress por campos post meta y taxonomías a la vez.
- Primera versión
- Instalaciones activas
- 1000+
- Valoración
- Leer reseñas
Sobre mí
MDTF filtra cualquier tipo de contenido por campos meta y taxonomías a la vez. Se creó para catálogos que no son tiendas: inmobiliarias, concesionarios de coches, directorios. El propietario del sitio define los campos del filtro en un constructor integrado (checkbox, desplegable, slider de rango, desplegable de rango, calendario, campo de texto, etiqueta), los rellena en cada entrada y coloca los formularios de búsqueda como widgets o shortcodes.
La parte difícil es que WordPress guarda el meta como cadenas sin tipo en wp_postmeta. Cada condición de rango, fecha y texto tiene que convertirse en una meta_query que compare correctamente, cada opción visible necesita un recuento en vivo bajo la búsqueda actual, y el AND/OR entre grupos nunca debe debilitar las condiciones que ocultan entradas.
El plugin se mantiene desde 2014, a lo largo de PHP 7.x y 8.x, los cambios de almacenamiento de WooCommerce 3.0, el editor de widgets de bloques y una larga serie de informes de seguridad gestionados a través de Patchstack y Wordfence. Las ramas premium y gratuita comparten la misma base de código.
MDTF · filtro de metadatos
qué se resolvió
Estado de la búsqueda y enlaces
shipped · 2014Qué aportaLos enlaces de resultados de búsqueda siguen siendo cortos y se pueden guardar en favoritos o compartir. Repetir la misma búsqueda reutiliza un solo enlace en lugar de acumular copias almacenadas.
- Problema
- Una búsqueda sobre muchos campos meta y taxonomías es una estructura anidada grande. Transportada en parámetros GET genera enlaces muy largos y expone la forma de la consulta, y cada consulta de la página (resultados, contadores, paginación, ordenación, recuento por ajax) tiene que leer el mismo estado.
- Solución
El estado del formulario se codifica como JSON, se guarda en el servidor en un transient o en una sesión PHP, y el enlace lleva solo una clave corta. Un único accesor lo decodifica y valida para todas las consultas de la petición.
- Stack
El estado de la búsqueda es demasiado grande para viajar en la URLGuardar JSON en base64 en el servidor y poner en el enlace solo una clave corta page_mdf
encode_search_get_params() recibe por ajax el formulario serializado, lo analiza y lo sanea, elimina los valores '~' (cualquiera) y guarda el resto como JSON en base64 con set_page_mdf_session(). Según el ajuste keep_search_data_in, los datos van a un transient durante un día o a $_SESSION. La respuesta es mdf_cat=...&page_mdf=<key>, que el front end convierte en el enlace de resultados.
La misma búsqueda creaba una nueva copia almacenada cada vezLa clave es el md5 del JSON normalizado: claves asociativas ordenadas, escalares recortados, listas en su orden
get_search_data_key() calcula el hash de la salida de normalize_search_data(), que recorre el array de forma recursiva, ordena las claves asociativas y mantiene el orden de las listas. El orden de los campos que envía el navegador ya no importa, así que una búsqueda se corresponde con un transient y un enlace. Los enlaces con claves anteriores siguen funcionando mientras viva su transient.
Un endpoint público guarda cada búsqueda durante un díaRechazar entradas que no sean cadenas o sean demasiado grandes (filtrable, 65536 bytes) antes de guardar nada
mdf_encode_search_get_params está registrado para visitantes sin sesión, y cada búsqueda distinta escribe un transient que vive un día. El manejador comprueba que vars sea una cadena dentro de mdf_search_data_max_length y, si no, responde mdf_cat=-1 sin guardar. Una clave suministrada se acepta solo si coincide con /^[0-9a-f]{32}$/; en caso contrario, la clave se deriva de los datos.
El estado guardado no es de confianza cuando se vuelve a leerDecodificar a un array o a nada; forzar a número los extremos from/to de los rangos
get_page_mdf_data() trata como ausencia de búsqueda todo lo que no se decodifica a un array. Combina las opciones por defecto del widget y, en cada campo medafi_, convierte 'from' y 'to' a números, de modo que los valores de rango y de calendario llegan a meta_query como números, sea lo que sea lo que envió el visitante. El resultado decodificado se conserva en una variable estática durante el resto de la petición.
Las búsquedas fijas necesitan enlaces permanentes que nunca caduquenUna clave numérica se resuelve en el post meta page_mdf_string en lugar de en un transient
Los enlaces constantes son un tipo de contenido oculto aparte. Su save_post() guarda la búsqueda codificada en page_mdf_string. get_page_mdf_session() comprueba la clave: numérica significa un ID de entrada y lee el meta guardado, cualquier otra cosa va al almacén de transients o de sesión. Los resultados, los contadores y las plantillas no necesitan saber de qué tipo de enlace se trata.
Construcción de meta queries
shipped · 2014Qué aportaLos visitantes pueden buscar por precio, año, kilometraje, fechas o palabras, incluidos rangos abiertos como 'y más', y los filtros pueden reutilizar datos ya introducidos en las entradas.
- Problema
- Los valores meta son cadenas en wp_postmeta. Los precios, años, kilometrajes, rangos de fechas y el texto libre deben convertirse en cláusulas de meta_query que comparen como números o fechas, admitan rangos abiertos y que MySQL aún pueda ejecutar.
- Solución
Cada tipo de elemento de filtro tiene su propia traducción a meta_query o a un fragmento de posts_where, con comparaciones DECIMAL para sliders y desplegables de rango, límites superiores abiertos y modelos de rango de dos claves guardados por entrada.
- Stack
Un slider en su máximo debe significar 'y más', no 'hasta el máx.'Sustituir el extremo superior por 2147483647 cuando el control está en el máx. configurado
En get_meta_query_array(), el caso del slider compara el 'to' seleccionado con el máx. del propio slider; si son iguales, el límite superior pasa a ser MetaDataFilterCore::$max_int, de modo que las entradas con valores por encima de la escala configurada siguen coincidiendo. Los desplegables de rango usan la misma idea con un sufijo 'i' (infinito), como '1001-i'. Los sliders simples y los desplegables de rango usan BETWEEN con tipo DECIMAL.
El elemento tiene un rango de valores, el visitante busca un númeroSlider de valor doble: key <= v AND key_to >= v sobre dos campos meta
En los sliders con slider_range_value = 1, una entrada guarda dos valores, la clave del campo y key_to (por ejemplo un tramo de precio o de tamaño). El visitante elige un valor, y la consulta exige que caiga dentro del tramo propio de la entrada, con ambos lados de tipo DECIMAL.
Rangos de fechas en los que cualquiera de los extremos del calendario puede estar vacíoCuatro ramas sobre las claves _from/_to: inicio abierto, fin abierto, día único, BETWEEN
El elemento de calendario guarda <key>_from y <key>_to. Solo 'to' definido da _to <= to; solo 'from' da _from >= from; valores iguales coinciden con los elementos cuyo tramo cubre ese día; un tramo completo se convierte en BETWEEN sobre _from. Un comentario del código fecha la última rama el 08-10-2014.
Campo de texto que busca en el título o el contenido palabra por palabraposts_where añade un LIKE por palabra mediante $wpdb->prepare y esc_like, unidos por un filtro
Un elemento de texto puede apuntar a su propio valor meta o a post_title, post_content, o a ambos con OR o AND. Para las columnas de la entrada, la condición se pasa a los filtros de MDTF_HELPER en posts_where, que dividen la frase en palabras (se puede cambiar con mdf_txtsearch_some_words_behavior) y las unen con mdf_txtsearch_some_words_condition, AND por defecto. Los espacios repetidos se colapsan primero.
Los campos del filtro deben reutilizar meta que ya existe en el sitioLos elementos reflejados asocian una clave de filtro con una clave meta existente en el momento de la consulta
Un campo marcado is_reflected lleva reflected_key. El constructor de consultas, el separador de OR y la API pública get_field() usan la clave reflejada, de modo que un filtro puede trabajar sobre meta escrito por el tema u otro plugin sin copiar valores a claves medafi_.
Lógica AND/OR entre grupos de filtros
en el productoQué aportaLos visitantes pueden elegir si los resultados coinciden con cualquiera o con todas las opciones que marcan. Los elementos ocultos, sin stock o no relacionados siguen quedando fuera de los resultados.
- Problema
- Los visitantes pueden querer OR entre las opciones marcadas, pero un OR simple en la raíz de meta_query también aplica OR a las condiciones de servicio: categoría del filtro, entradas ocultas, visibilidad de WooCommerce y stock. Entonces las entradas ocultas y ajenas se filtran a los resultados. El modo elegido también tiene que llegar a todas las consultas de una búsqueda.
- Solución
Las condiciones se dividen en condiciones del visitante, que pueden combinarse con OR, y condiciones obligatorias. La consulta final es AND en el nivel superior con un grupo OR anidado, tanto para meta como para taxonomías. El modo viaja dentro del estado de búsqueda guardado.
- Stack
Un OR simple también aplicaría OR a las condiciones que ocultan entradaswrap_meta_query(): AND arriba, grupo OR del visitante anidado, condiciones de servicio obligatorias
wrap_meta_query() construye una relación AND y anida las condiciones combinables con OR como un único grupo de relación OR, solo cuando hay dos o más. mdf_hide_post NOT EXISTS, meta_data_filter_cat, _visibility y _stock_status van en el nivel superior y se aplican siempre. La misma envoltura la usan la consulta de resultados y los contadores.
Solo las condiciones checkbox y label tienen sentido con ORsplit_meta_query_array() tipifica cada cláusula según su elemento de filtro; los sliders siguen siendo obligatorios
El divisor asocia cada clave de búsqueda (propia o reflejada) con el tipo de su elemento a partir de las secciones de filtro de la búsqueda. Las cláusulas de los elementos checkbox y label pasan a ser combinables con OR; los sliders, desplegables, campos de texto, calendarios y desplegables de rango siguen siendo obligatorios. En el modo AND, el array se devuelve sin cambios, de modo que las búsquedas AND existentes producen el mismo SQL que antes.
El OR de taxonomías no debe descartar la visibilidad ni las taxonomías adicionalessplit_tax_query_array() separa por identidad los términos del visitante de los añadidos por el filtro
Después de que los filtros de terceros (mdf_filter_taxonomies, taxonomías adicionales, product_visibility) hayan modificado la tax_query, el divisor compara cada cláusula con la lista construida a partir del formulario mediante array_search estricto. Las coincidencias son condiciones del visitante; el resto es obligatorio. Las cláusulas repetidas del visitante que el filtro recopiló por petición se descartan. wrap_tax_query() anida entonces solo la parte del visitante bajo OR.
El modo AND/OR debe llegar al recuento, la paginación y la ordenaciónGuardar el modo en las opciones del widget page_mdf; usar la petición solo como alternativa
encode_search_get_params() escribe meta_data_filter_bool y mdf_tax_bool en mdf_widget_options de la búsqueda guardada, tomando la elección del visitante cuando el widget permite ambos. get_meta_data_filter_bool() y get_mdf_tax_bool() leen primero el valor guardado y normalizan a 'AND' todo lo que no sea 'OR'. Los resultados, los contadores, el recuento por ajax, la paginación y la ordenación leen el mismo estado.
Activar el OR de taxonomías no debe cambiar los resultados de widgets que ya están en sitios en producciónAplicar el OR de taxonomías solo a widgets guardados con una bandera de versión; los anteriores siguen en AND
Los widgets guardados por versiones anteriores siempre han buscado en las taxonomías con AND, dijera lo que dijera su opción OR. Aplicarles la opción cambiaría los resultados en sitios en producción sin ninguna acción del propietario. get_widget_tax_bool() respeta la opción solo cuando taxonomies_options_behaviour_ver está definido, lo que ocurre cuando el widget se guarda con la nueva versión.
Un padre marcado y su hijo marcado: el padre coincidiría con todo lo que hay por debajoDescartar un término marcado cuando también está marcado alguno de sus hijos
tax_query incluye los hijos por defecto, así que un padre marcado coincidiría con todas las entradas que hay bajo él y dejaría sin sentido la selección del hijo. get_tax_query_array() elimina un término marcado si get_term_children() de él se interseca con el conjunto marcado, y luego reindexa la lista.
Contadores dinámicos y caché
shipped · 2014Qué aportaCada opción muestra cuántos elementos devolvería, así que los visitantes evitan resultados vacíos. Los recuentos se guardan en caché, por lo que el sitio sigue siendo rápido.
- Problema
- Cada checkbox, opción de desplegable y término muestra cuántas entradas devolvería con la búsqueda actual. Eso es una consulta por cada elemento visible, con joins sobre wp_postmeta, en cada visualización de página y en cada recuento por ajax.
- Solución
Las consultas de recuento obtienen solo IDs y se saltan found-rows y el precalentamiento de caché. Los recuentos pueden guardarse en una tabla clave/valor indexada por un hash de la consulta, y los árboles de términos en transients con una vida útil definida.
- Stack
Un recuento debe mostrar la búsqueda más esta única opciónPor elemento: añadir su cláusula, sustituir el valor hermano del mismo select y volver a ejecutar
get_item_posts_count() parte de la búsqueda actual. Un checkbox añade key = 1 si todavía no está. Una opción de desplegable elimina primero la cláusula de la misma clave, porque las opciones hermanas se excluyen entre sí, y luego se añade a sí misma (como BETWEEN para las opciones de rango). Para los términos, get_tax_count() inyecta el término en una copia del estado de la taxonomía con la caché de taxonomías por petición desactivada y la restaura después.
Las consultas de recuento pagaban por datos que nunca usabanfields=ids, nopaging, sin precalentamiento de caché de meta/términos, set_found_posts() sobrescrito
Las consultas de contadores piden solo IDs con update_post_meta_cache, update_post_term_cache y cache_results desactivados. WP_QueryMDFCounterIn extiende WP_Query y sobrescribe set_found_posts() para que no haga nada, de modo que WordPress no ejecuta el paso adicional de found-rows; post_count es la respuesta.
Los mismos recuentos se recalculan en cada visualización de páginaGuardar los recuentos en caché en {prefix}mdf_query_cache, indexados por el md5 de los argumentos de la consulta
WP_QueryMDFCounter calcula el hash del array completo de argumentos y lo busca en una tabla de dos columnas con un índice en mkey; si no lo encuentra, ejecuta la consulta e inserta el recuento. La página de ajustes crea la tabla y desactiva la opción mostrando la sentencia CREATE si el usuario de la base de datos carece del privilegio. Un administrador vacía la caché desde la página de ajustes, lo que ejecuta un TRUNCATE protegido por manage_options y un nonce.
Las listas jerárquicas de términos cuestan una consulta por nivelUna llamada a get_categories() por taxonomía, árbol construido en PHP, guardado en caché como transient
get_terms() carga una vez todos los términos de la taxonomía y assemble_terms_childs() construye las listas de hijos a partir de ese array. Con cache_terms_data activado, el resultado se guarda bajo mdf_terms_cache_<md5 de los argumentos> como un transient cuya vida útil procede del ajuste del periodo de caché de términos (de una hora a 7 días; sin caducidad cuando no se define periodo).
El filtro no debería cargarse en páginas que no tienen búsquedaLista de URL permitidas opcional: omitir la inicialización del front end cuando la ruta no figura en ella
is_should_init() se ejecuta antes de que el plugin registre sus tipos de contenido y hooks. Si init_on_pages_only está activado, normaliza la URL actual (sin cadena de consulta ni /page/N) y termina pronto cuando la URL no está en la lista. Las peticiones de administración siempre se inicializan.
Modelo de datos e integración con WordPress
shipped · 2014Qué aportaLos resultados aparecen en el diseño que ya tiene el sitio, y el personal puede renombrar o reorganizar los campos del filtro sin perder los datos ya guardados en las entradas.
- Problema
- Los campos del filtro los define el propietario del sitio, sus valores viven en el post meta de tipos de contenido arbitrarios, y los resultados tienen que aparecer en las plantillas propias del tema sin modificar el tema.
- Solución
Las definiciones de campos viven en un tipo de contenido oculto. Los valores de las entradas se guardan dos veces: como un array para la edición y como claves meta separadas para la consulta. La consulta principal se sustituye desde pre_get_posts con una protección contra reentrada.
- Stack
Los valores meta deben poder editarse como un conjunto y consultarse uno a unoGuardar el conjunto como un array y cada campo medafi_ como su propia clave meta
save_post() escribe page_meta_data_filter como un único array, que la pantalla de edición vuelve a leer, y después escribe cada campo medafi_ con update_post_meta(), que meta_query puede usar. Las secciones del filtro y sus elementos se guardan como meta html_items del tipo de contenido oculto meta_data_filter.
Cambiar la categoría de filtro de una entrada no debe dejar valores obsoletosAl cambiar de categoría, borrar todo el meta medafi_ de la entrada antes de guardar el nuevo
Los campos de la categoría de filtro anterior permanecerían en wp_postmeta y coincidirían con las búsquedas de esa categoría. save_post() compara la categoría enviada con la guardada y elimina todas las filas medafi_% de la entrada cuando ha cambiado.
Renombrar la clave de un campo no debe dejar datos huérfanos en las entradasComprobar la unicidad en todas las secciones del filtro y luego renombrar en postmeta con un único UPDATE
change_meta_key() exige el prefijo medafi_, rechaza una clave ya usada en esta o en cualquier otra sección del filtro y nombra la sección en conflicto. Después renombra la clave dentro de html_items, reescribe los arrays page_meta_data_filter de las entradas afectadas y ejecuta UPDATE postmeta SET meta_key. Necesita un nonce, edit_post y edit_others_posts porque toca todas las entradas.
Los resultados deben renderizarse en la plantilla de búsqueda propia del temapre_get_posts ejecuta la consulta de resultados y sustituye el $wp_query global, con una protección de bucle
parse_query() marca la consulta principal como una búsqueda del tipo de contenido solicitado. pre_get_posts() renderiza [meta_data_filter_results] sin plantilla; esa vista declara global $wp_query y le asigna un nuevo WP_Query construido a partir de la búsqueda. Una bandera de petición impide la reentrada, porque la nueva consulta vuelve a disparar pre_get_posts. El filtro mdtf_prohibited_post_types mantiene fuera las consultas internas de ACF y Contact Form 7.
WooCommerce 3.0 movió los productos ocultos del meta a una taxonomíaConmutador por versión: product_visibility NOT IN para 3.0+, _visibility meta antes
Los resultados, los contadores de elementos y los contadores de términos se ramifican según WOOCOMMERCE_VERSION: desde la 3.0 añaden una tax_query que excluye exclude-from-catalog; las versiones anteriores reciben una cláusula meta _visibility IN (catalog, visible). Una cláusula opcional _stock_status oculta los productos sin stock. Con WPML activo, cada consulta recibe además el idioma actual.
Las opciones de desplegable antiguas no tienen clave, mientras que las entradas guardan sus títulos como valoresfill_select_keys() usa el título de la opción como clave al leer, coincidiendo con los valores guardados en las entradas
Las opciones de desplegable guardadas por versiones anteriores del constructor tienen una clave vacía, mientras que el meta box de la entrada guarda el título de la opción como valor. En lugar de una migración, fill_select_keys() se ejecuta dentro de get_html_items() y al guardar, rellenando las claves vacías con el título recortado y dejando intactas las claves explícitas, incluido '0'. Los valores existentes de las entradas coinciden sin volver a guardarlas.
Refuerzo de seguridad
en el productoQué aportaLa búsqueda pública no puede usarse para escribir datos no deseados ni para llegar a acciones de administración, lo que protege el sitio y su contenido.
- Problema
- El plugin expone endpoints ajax públicos, renderiza plantillas por nombre a partir de opciones y escribe post meta desde formularios.
- Solución
Firmas en el servidor para los datos que el navegador devuelve, listas blancas para rutas de plantillas y claves meta, y comprobaciones de capacidad y nonce en las acciones de administración.
- Stack
Un widget dentro de un bloque debe redibujarse por recuento ajax a partir de opciones que lleva la páginaFirmar la clase y las opciones del widget con wp_hash; aceptar solo dos clases de widget conocidas
Un Legacy Widget dentro de un bloque no tiene una barra lateral donde el recuento pueda encontrarlo por ID, así que su formulario lleva la clase y las opciones del widget. get_widget_redraw_data() las firma con wp_hash(); get_widget_from_redraw_data() verifica con hash_equals() y acepta solo MetaDataFilter_Search y MetaDataFilter_TaxSearch antes de llamar a the_widget().
La ruta de la plantilla de resultados procede de las opciones del shortcode y del widgetReducir cada segmento de la ruta a [A-Za-z0-9_-]; subcarpetas permitidas, '..' imposible
sanitize_template_path() normaliza las barras, filtra cada segmento a un juego de caracteres seguro y descarta los vacíos, de modo que un valor no puede salir de la carpeta de plantillas mientras que subcarpetas como any/my_posts siguen funcionando.
El formulario de edición de entradas no debe escribir claves meta distintas de los campos del filtroAceptar solo claves medafi_ y exigir edit_post antes de guardar valores
save_post() elimina del array enviado toda clave sin el prefijo medafi_ antes de guardarlo y vuelve a comprobar el prefijo antes de cada llamada a update_post_meta(), de modo que el formulario no puede establecer _edit_lock, _price ni claves similares. Guardar valores requiere current_user_can('edit_post').
Las acciones ajax de administración no deben poder ser llamadas por cualquier visitanteCapacidad más nonce en el vaciado de caché, el renombrado de claves y la edición de widgets
Vaciar la caché requiere manage_options y mdtf_ajax_nonce. Renombrar claves requiere su propio nonce, edit_post y edit_others_posts. Las acciones sobre los campos de widgets requieren edit_theme_options. Los visitantes sin sesión solo llegan a las acciones de búsqueda del front end (hijos de términos, codificación de búsqueda, recuento, panel de búsqueda, contenido del botón de búsqueda). Los demás manejadores registrados para ellos comprueban el acceso por sí mismos: las acciones de suscripción del messenger requieren inicio de sesión y un nonce, y el ayudante de estadísticas requiere manage_options.
Cifras
wordpress.org- Instalaciones activas 1000+ contador de wordpress.org.
- Reseñas 28 Media de 4.5 sobre 5.
- Años de soporte 12 Desde 22 de abril de 2014, última actualización 13 de octubre de 2026.