return (function () { /** * CONFIG */ $cfg = [ // Post type(s) que quieres listar como “relacionados” // Ej: 'post' | 'product' | 'mi_cpt' | ['post','mi_cpt'] 'target_post_type' => 'post', // Taxonomías por las que quieres relacionar (slug REAL) // Ej: ['category'] | ['post_tag'] | ['mi_taxonomia'] 'taxonomies' => ['TU_TAXONOMIA_AQUI'], // Número de resultados 'posts_per_page' => 4, // Excluir el “actual”: // - true/false // - 'auto' => solo excluye si el single actual es del mismo post_type que el target 'exclude_current' => 'auto', // Coincidencia entre taxonomías: // - 'any' => OR: comparte cualquiera (más flexible) // - 'all' => AND: debe cumplir todas (más estricto) 'match' => 'any', // Si el post “source” no tiene términos detectables: // - 'none' => no muestra nada (recomendado para related) // - 'latest' => muestra últimos del target_post_type 'fallback' => 'none', // Incluir hijos (categorías hijas, etc.) // Si te trae “demasiados”, ponlo a false 'include_children' => true, // Orden 'orderby' => 'date', // o 'rand' 'order' => 'DESC', // Debug visible (solo admins + ?rel_debug=1) 'debug' => false, 'debug_key' => 'rel_debug', 'debug_cap' => 'manage_options', 'debug_global_key' => 'FLOW_RELATED_DEBUG', ]; /** * 1) Determinar el source_id (el contenido del single) */ $source_id = (int) get_queried_object_id(); if (!$source_id) $source_id = (int) get_the_ID(); $source_pt = $source_id ? get_post_type($source_id) : null; /** * 2) exclude_current en modo auto */ $target_types = (array) $cfg['target_post_type']; $exclude = $cfg['exclude_current']; if ($exclude === 'auto') { $exclude = ($source_pt && in_array($source_pt, $target_types, true)); } else { $exclude = (bool) $exclude; } /** * 3) Construir tax_query según términos del source */ $tax_query_parts = []; $debug_terms = []; foreach ((array) $cfg['taxonomies'] as $tax) { $tax = (string) $tax; if (!taxonomy_exists($tax)) { $debug_terms[$tax] = ['error' => 'taxonomy_not_exists']; continue; } // El target post_type debe soportar esa taxonomía $tax_in_target = false; foreach ($target_types as $pt) { if (is_object_in_taxonomy($pt, $tax)) { $tax_in_target = true; break; } } if (!$tax_in_target) { $debug_terms[$tax] = ['error' => 'taxonomy_not_in_target_post_type']; continue; } $term_ids = wp_get_post_terms($source_id, $tax, ['fields' => 'ids']); if (is_wp_error($term_ids) || empty($term_ids)) { $debug_terms[$tax] = ['terms' => []]; continue; } $term_ids = array_map('intval', $term_ids); $debug_terms[$tax] = ['terms' => $term_ids]; $tax_query_parts[] = [ 'taxonomy' => $tax, 'field' => 'term_id', 'terms' => $term_ids, 'operator' => 'IN', 'include_children' => (bool) $cfg['include_children'], ]; } /** * 4) Args base */ $args = [ 'post_type' => $cfg['target_post_type'], 'post_status' => 'publish', 'posts_per_page' => (int) $cfg['posts_per_page'], 'orderby' => $cfg['orderby'], 'order' => $cfg['order'], 'no_found_rows' => true, ]; if ($exclude && $source_id) { $args['post__not_in'] = [$source_id]; } if (!empty($tax_query_parts)) { $args['tax_query'] = array_merge( ['relation' => ($cfg['match'] === 'all' ? 'AND' : 'OR')], $tax_query_parts ); } else { // Sin términos -> fallback if ($cfg['fallback'] === 'none') { $args['post__in'] = [0]; } } /** * 5) Debug (opcional) -> lo imprime un Code element aparte */ if (!empty($cfg['debug']) && current_user_can($cfg['debug_cap']) && !empty($_GET[$cfg['debug_key']])) { $test_args = $args; $test_args['fields'] = 'ids'; $test_args['posts_per_page'] = 20; $GLOBALS[$cfg['debug_global_key']] = [ 'source_id' => $source_id, 'source_post_type' => $source_pt, 'target_post_type' => $cfg['target_post_type'], 'exclude_current' => $exclude, 'taxonomies' => $cfg['taxonomies'], 'terms_found' => $debug_terms, 'final_args' => $args, 'test_ids' => get_posts($test_args), ]; } return $args; })(); /* Codigo para debug. --------- Poner con el widget de codigo */ <?php if (!current_user_can('manage_options') || empty($_GET['rel_debug'])) return; echo '<div style="padding:16px;border:1px solid #ddd;background:#fff;margin:16px 0">'; echo '<strong>FLOW_RELATED_DEBUG</strong>'; echo '<pre style="margin-top:12px;white-space:pre-wrap;">'; print_r($GLOBALS['FLOW_RELATED_DEBUG'] ?? []); echo '</pre></div>'; ?>
La idea es sencilla: cuando alguien está leyendo una entrada (o viendo un CPT), queremos que debajo aparezcan contenidos realmente relacionados, no lo último publicado.
En lugar de hacerlo a mano o depender de plugins extra, usamos el propio Query Loop de Bricks para “mirar” el contenido actual, capturar sus términos (categoría, etiqueta, o la taxonomía que uses) y construir una query que traiga solo los posts (o CPTs) que comparten esa misma clasificación.
target_post_type → lo que quieres listar (ej. post)taxonomies → slug real de la taxonomía (ej. category, mi_tax)posts_per_page → número de resultadosexclude_current = 'auto'fallback = 'none'include_children = true/falsetrue amplía el match a subcategorías. Si te trae demasiados, ponlo en false.match = any/all
any: “comparten algo” (más resultados)all: “tienen que coincidir en todo” (más estricto)latest). Para related, lo normal es fallback = none.