¿Puedo excluir una entrada por meta key usando la función pre_get_posts?
Veo que mucha gente prefiere usar el hook pre_get_posts
en lugar de query_posts
. El código siguiente funciona y muestra todas las entradas que tienen la meta key "featured"
function show_featured_posts ( $query ) {
if ( $query->is_main_query() ) {
$query->set( 'meta_key', 'featured' );
$query->set( 'meta_value', 'yes' );
}
}
add_action( 'pre_get_posts', 'show_featured_posts' );
Pero quiero que las entradas que tienen la meta_key 'featured
' sean excluidas de la consulta principal. ¿Hay alguna manera fácil de hacer esto?
Veo que muchas personas prefieren usar el hook pre_get_posts en lugar de query_posts
¡Sí!
El pre_get_posts
filtra un objeto WP_Query
, lo que significa que cualquier cosa que puedas hacer con query_posts()
puedes hacerlo mediante $query->set()
y $query->get()
. En particular podemos hacer uso del atributo meta_query
(ver Codex):
$meta_query = array(
array(
'key'=>'featured',
'value'=>'yes',
'compare'=>'!=',
),
);
$query->set('meta_query',$meta_query);
Pero... esto reemplaza la 'meta query' original (si tenía una). Así que a menos que quieras reemplazar completamente la meta query original, sugiero:
//Obtener la meta query original
$meta_query = $query->get('meta_query');
//Añadir nuestra meta query a las meta queries existentes
$meta_query[] = array(
'key'=>'featured',
'value'=>'yes',
'compare'=>'!=',
);
$query->set('meta_query',$meta_query);
De esta manera añadimos nuestra meta query junto con las meta queries existentes.
Puedes o no querer establecer la propiedad relation
de $meta_query
como AND
o OR
(para devolver posts que cumplan todas, o al menos una, de las meta queries).
* Nota: Este tipo de consulta devolverá posts con la meta clave 'featured', pero cuyo valor no sea yes
. No incluirá posts donde la meta clave 'featured' no exista. Podrás hacer esto en la versión 3.5.

¿Entonces no hay manera de verificar si un meta_key para un post existe o no / está vacío o no? Tendré que esperar hasta la 3.5 entonces. Gracias por tu respuesta.

Simplemente crearé un meta box con opciones Sí
y No
donde 'No' estará seleccionado por defecto. Cuando quiera destacar un post seleccionaré Sí
. Sin embargo, quiero que los últimos 5 posts se mantengan destacados y los demás aparezcan en la consulta principal. No quiero tener que volver y cambiar la selección cada vez, así que debo encontrar una manera de excluir solo los 5 posts más recientes. Veo muchas preguntas similares en stackexchange y debería haber una forma sencilla de manejar esos posts destacados. (una forma que no afecte el rendimiento general, no cree muchas consultas o requiera consultas SQL mixtas)

Por cierto, no estoy seguro de que sea buena idea crear un meta_key adicional con valor Sí
o No
para todos los posts. Sería genial poder excluir aquellos posts que simplemente no tengan la clave featured
.

Esta función dejó de funcionar en mi sitio después de actualizar a PHP 7, mostrando un error Uncaught Error: [] operator not supported for strings
ya que el meta_query
original estaba devolviendo null. Puedes solucionarlo recurriendo a un array vacío si no existe ninguno, reemplazando $meta_query = $query->get('meta_query');
por $meta_query = ( is_array( $query->get('meta_query') ) ) ? $query->get('meta_query') : [];
.

Quiero compartir mi solución temporal para publicaciones destacadas por si a alguien le resulta útil. No estoy usando el hook pre_get_posts
aquí, pero tampoco estoy usando query_posts
. El problema es que tengo que trabajar con la consulta principal y ejecutar una parte de la consulta SQL.
Sería genial si algún experto pudiera revisar el código y decirme si está bien y no causará problemas de rendimiento. También sería estupendo si alguien tiene un enfoque mejor y lo comparte con nosotros.
Crear consulta para publicaciones destacadas
<?php
$featured_query = new WP_query( array(
'meta_key' =>'featured',
'meta_value' =>'yes',
'posts_per_page' => 5,
'no_found_rows' => true
)
);
while ($featured_query->have_posts()) :
$featured_query->the_post();
//Contenido...
endwhile;
wp_reset_postdata();
?>
Crear la consulta principal, excluyendo las publicaciones que tienen el meta_key 'featured', limitar la exclusión a las 5 publicaciones más recientes y mostrar todas las demás.
<?php
$excludeposts = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'featured' AND meta_value != '' ORDER BY post_id DESC LIMIT 0, 5" );
$main_query = new WP_Query( array(
'post__not_in' => $excludeposts,
'paged' => $paged
)
);
while ($main_query->have_posts()) :
$main_query->the_post();
//Contenido...
endwhile;
?>

En respuesta a @Carlisle, si deseas excluir los 5 posts más recientes marcados como destacados, podrías hacer lo siguiente. Cambia posts_per_page por la cantidad que quieras excluir, y meta_query por cómo estás designando la categoría destacada.
function cmp_exclude_featured_posts($query) {
$exclude = array(); //Crea un array vacío para los IDs de posts a excluir
if ( $query->is_main_query() ) {
$featured = get_posts(array(
'post_type' => 'post',
'meta_query' => array(
array(
'key' => 'featured',
'value' => '1',
'compare' => '==',
),
),
'posts_per_page' => 2
));
foreach($featured as $hide) {
$exclude[] = $hide->ID;
}
$query->set('post__not_in', $exclude);
}
}
add_filter( 'pre_get_posts', 'cmp_exclude_featured_posts' );
