¿Por qué query_posts () no está marcado como obsoleto?

15

Hay dos query_posts()funciones técnicamente hablando. Uno query_posts()es en realidad WP_Query::query_posts()y el otro está en el espacio global.

Pidiendo de la cordura:

Si global query_posts()es ese "mal", ¿por qué no está en desuso?

O por qué no está marcado como _doing_it_wong.

prosti
fuente
2
¡Esa es una buena pregunta! Para otros que se encuentren con esto y no sepan por qué no deberían usar query_posts (), aquí y aquí hay algunos buenos Q & A de iniciación .
Tim Malone

Respuestas:

11

Pregunta esencial

Excavación Vamos en el trío: ::query_posts, ::get_postsy class WP_Querypara comprender ::query_postsmejor.

La piedra angular para obtener los datos en WordPress es la WP_Queryclase. Ambos métodos ::query_postsy ::get_postsusan esa clase.

Tenga en cuenta que la clase WP_Querytambién contiene los métodos con el mismo nombre: WP_Query::query_postsy WP_Query::get_posts, pero en realidad solo consideramos los métodos globales, así que no se confunda.

ingrese la descripción de la imagen aquí

Entendiendo el WP_Query

La clase llamada WP_Queryse introdujo en 2004. Todos los campos que tienen la marca ☂ (paraguas) estaban presentes en 2004. Los campos adicionales se agregaron más tarde.

Aquí está la WP_Queryestructura:

class WP_Query (as in WordPress v4.7) 
    public $query; 
    public $query_vars = array(); 
    public $tax_query;
    public $meta_query = false;
    public $date_query = false;
    public $queried_object; 
    public $queried_object_id; 
    public $request;
    public $posts; 
    public $post_count = 0; 
    public $current_post = -1; 
    public $in_the_loop = false;
    public $post; 
    public $comments;
    public $comment_count = 0;
    public $current_comment = -1;
    public $comment;
    public $found_posts = 0;
    public $max_num_pages = 0;
    public $max_num_comment_pages = 0;
    public $is_single = false; 
    public $is_preview = false; 
    public $is_page = false; 
    public $is_archive = false; 
    public $is_date = false; 
    public $is_year = false; 
    public $is_month = false; 
    public $is_day = false; 
    public $is_time = false; 
    public $is_author = false; 
    public $is_category = false; 
    public $is_tag = false;
    public $is_tax = false;
    public $is_search = false; 
    public $is_feed = false; 
    public $is_comment_feed = false;
    public $is_trackback = false; 
    public $is_home = false; 
    public $is_404 = false; 
    public $is_embed = false;
    public $is_paged = false;
    public $is_admin = false; 
    public $is_attachment = false;
    public $is_singular = false;
    public $is_robots = false;
    public $is_posts_page = false;
    public $is_post_type_archive = false;
    private $query_vars_hash = false;
    private $query_vars_changed = true;
    public $thumbnails_cached = false;
    private $stopwords;
    private $compat_fields = array('query_vars_hash', 'query_vars_changed');
    private $compat_methods = array('init_query_flags', 'parse_tax_query');
    private function init_query_flags()

WP_Query Es la navaja suiza.

Algunas cosas sobre WP_Query:

  • es algo que puedes controlar a través de argumentos que pasas
  • es codicioso por defecto
  • contiene la sustancia para el bucle
  • se guarda en el espacio global x2
  • Puede ser primario o secundario
  • utiliza clases auxiliares
  • tiene un práctico pre_get_postsgancho
  • incluso tiene soporte para bucles anidados
  • contiene la cadena de consulta SQL
  • contiene el número de resultados
  • contiene los resultados
  • contiene la lista de todos los argumentos de consulta posibles
  • tiene las banderas de la plantilla
  • ...

No puedo explicar todo esto, pero algunos de estos son complicados, así que proporcionemos consejos breves.

WP_Query es algo que puedes controlar a través de argumentos que pasas

The list of the arguments
---
 attachment
 attachment_id
 author
 author__in
 author__not_in
 author_name
 cache_results
 cat
 category__and
 category__in
 category__not_in
 category_name
 comments_per_page
 day
 embed
 error
 feed
 fields
 hour
 ignore_sticky_posts
 lazy_load_term_meta
 m
 menu_order
 meta_key
 meta_value
 minute
 monthnum
 name
 no_found_rows
 nopaging
 order
 p
 page_id
 paged
 pagename
 post__in
 post__not_in
 post_name__in
 post_parent
 post_parent__in
 post_parent__not_in
 post_type
 posts_per_page
 preview
 s
 second
 sentence
 static
 subpost
 subpost_id
 suppress_filters
 tag
 tag__and
 tag__in
 tag__not_in
 tag_id
 tag_slug__and
 tag_slug__in
 tb
 title
 update_post_meta_cache
 update_post_term_cache
 w
 year

Esta lista de WordPress versión 4.7 ciertamente cambiará en el futuro.

Este sería el ejemplo mínimo de creación del WP_Queryobjeto a partir de los argumentos:

// WP_Query arguments
$args = array ( /* arguments*/ );
// creating the WP_Query object
$query = new WP_Query( $args );
// print full list of arguments WP_Query can take
print ( $query->query_vars );

WP_Query es codicioso

Creado a partir de la idea, get all you canlos desarrolladores de WordPress decidieron obtener todos los datos posibles antes, ya que esto es bueno para el rendimiento . Esta es la razón por la cual, de manera predeterminada, cuando la consulta toma 10 publicaciones de la base de datos, también obtendrá los términos y los metadatos de estas publicaciones a través de consultas separadas. Los términos y metadatos se almacenarán en caché (se buscarán previamente).

Tenga en cuenta que el almacenamiento en caché es solo para toda la vida útil de la solicitud.

Puede desactivar el almacenamiento en caché si se establece update_post_meta_cachey update_post_term_cacheal falsemismo tiempo establecer los WP_Queryargumentos. Cuando el almacenamiento en caché está desactivado, los datos se solicitarán a la base de datos solo bajo demanda.

Para la mayoría de los blogs de WordPress, el almacenamiento en caché funciona bien, pero hay algunas ocasiones en las que puede deshabilitar el almacenamiento en caché.

WP_Query utiliza clases auxiliares

Si marcó los WP_Querycampos allí, tiene estos tres:

public $tax_query;
public $meta_query;
public $date_query;

Puedes imaginar agregar nuevos en el futuro.

ingrese la descripción de la imagen aquí

WP_Query contiene la sustancia para el bucle

En este código:

$query = new WP_Query( $args )
if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();

puede notar que WP_Querytiene la sustancia que puede iterar. Los métodos de ayuda también están ahí. Acabas de configurar el whilebucle.

Nota. fory los whilebucles son semánticamente equivalentes.

WP_Query Primaria y secundaria

En WordPress tienes una consulta primaria y cero o más consultas secundarias .

Es posible no tener la consulta primaria, pero esto está más allá del alcance de este artículo.

Consulta primaria conocida como consulta principal o consulta regular . La consulta secundaria también se denomina consulta personalizada .

WordPress usa la WP_Rewriteclase temprana para crear los argumentos de consulta basados ​​en la URL. En base a estos argumentos, almacena los dos objetos idénticos en el espacio global. Ambos contendrán la consulta principal.

global $wp_query   @since WordPress 1.5
global $wp_the_query @since WordPress 2.1

Cuando decimos consulta principal pensamos en estas variables. Otras consultas pueden llamarse secundarias o personalizadas.

Es completamente legal usar cualquiera global $wp_queryo $GLOBALS['wp_query'], pero usar la segunda notación es mucho más notable y ahorra escribir una línea adicional dentro del alcance de las funciones.

$GLOBALS['wp_query']y $GLOBALS['wp_the_query']son objetos separados. $GLOBALS['wp_the_query']debe permanecer congelado

WP_QueryTiene el práctico pre_get_postsgancho.

Este es el gancho de acción. Se aplicará a cualquier WP_Query instancia. Lo llamas así:

add_action( 'pre_get_posts', function($query){

 if ( is_category() && $query->is_main_query() ) {
    // set your improved arguments
    $query->set( ... );  
    ...  
 }

 return $query;  
});

Este enlace es excelente y puede alterar cualquier argumento de consulta.

Esto es lo que puedes leer :

Se activa después de crear el objeto variable de consulta, pero antes de que se ejecute la consulta real.

Este gancho es un administrador de argumentos pero no puede crear nuevos WP_Queryobjetos. Si tuvo una consulta primaria y una secundaria, pre_get_postsno puede crear la tercera. O si acaba de tener una primaria, no puede crear la secundaria.

Tenga en cuenta que en caso de que necesite modificar la consulta principal, solo puede usar el requestenlace.

WP_Query admite bucles anidados

Este escenario puede suceder si usa complementos y llama a funciones de complementos desde la plantilla.

Aquí está el ejemplo de muestra de WordPress tiene funciones de ayuda incluso para los bucles anidados:

global $id;
while ( have_posts() ) : the_post(); 

    // the custom $query
    $query = new WP_Query( array(   'posts_per_page' => 5   ) );    
    if ( $query->have_posts() ) {

        while ( $query->have_posts() ) : $query->the_post();            
            echo '<li>Custom ' . $id . '. ' . get_the_title() . '</li>';
        endwhile;       
    }   

    wp_reset_postdata();
    echo '<li>Main Query ' . $id . '. ' . get_the_title() . '</li>';

endwhile;

El resultado será así desde que instalé los datos de prueba de la unidad de tema :

Custom 100. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 1. Hello world!

Aunque solicité 5 publicaciones en la consulta $ personalizada, me devolverá seis, porque la publicación adhesiva seguirá. Si no hay wp_reset_postdataen el ejemplo anterior, la salida será así, ya $GLOBALS['post']que no será válida.

Custom 1001. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 13. Markup: Title With Special Characters

WP_Querytiene wp_reset_queryfunción

Esto es como un botón de reinicio. $GLOBALS['wp_the_query']debe estar congelado todo el tiempo, y los complementos o temas nunca deben alterarlo.

Esto es lo wp_reset_queryque debes hacer:

function wp_reset_query() {
    $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
    wp_reset_postdata();
}

Observaciones sobre get_posts

get_posts parece

File: /wp-includes/post.php
1661: function get_posts( $args = null ) {
1662:   $defaults = array(
1663:       'numberposts' => 5,
1664:       'category' => 0, 'orderby' => 'date',
1665:       'order' => 'DESC', 'include' => array(),
1666:       'exclude' => array(), 'meta_key' => '',
1667:       'meta_value' =>'', 'post_type' => 'post',
1668:       'suppress_filters' => true
1669:   );
... // do some argument parsing
1685:   $r['ignore_sticky_posts'] = true;
1686:   $r['no_found_rows'] = true;
1687: 
1688:   $get_posts = new WP_Query;
1689:   return $get_posts->query($r);

Los números de línea pueden cambiar en el futuro.

Es sólo una envoltura alrededor de WP_Queryque las declaraciones de los mensajes objeto de consulta.

El ignore_sticky_postsconjunto en verdadero significa que las publicaciones adhesivas pueden aparecer solo en una posición natural. No habrá publicaciones adhesivas en el frente. El otro no_found_rowsconjunto en verdadero significa que la API de la base de datos de WordPress no se usará SQL_CALC_FOUND_ROWSpara implementar la paginación, lo que reduce la carga en la base de datos para ejecutar el recuento de filas encontradas .

Esto es útil cuando no necesita paginación. Entendemos que ahora podemos imitar esta función con esta consulta:

$args = array ( 'ignore_sticky_posts' => true, 'no_found_rows' => true);
$query = new WP_Query( $args );
print( $query->request );

Aquí está la solicitud SQL correspondiente:

SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 10

Compare lo que tenemos ahora con la solicitud SQL anterior donde SQL_CALC_FOUND_ROWSexista.

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')  ORDER BY wp_posts.post_date DESC LIMIT 0, 10

La solicitud sin SQL_CALC_FOUND_ROWSserá más rápida.

Observaciones sobre query_posts

Consejo: Al principio en 2004 solo había global $wp_query. A partir de la versión 2.1 de WordPress $wp_the_queryvino. Consejo: $GLOBALS['wp_query']y $GLOBALS['wp_the_query']son objetos separados.

query_posts()Es WP_Queryenvoltorio. Devuelve la referencia al WP_Queryobjeto principal y, al mismo tiempo, establecerá el global $wp_query.

File: /wp-includes/query.php
function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

En PHP4 todo, incluidos los objetos, se pasaba por valor. query_postsfue así:

File: /wp-includes/query.php (WordPress 3.1)
function &query_posts($args) {
    unset($GLOBALS['wp_query']);
    $GLOBALS['wp_query'] =& new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

Tenga en cuenta que en un escenario típico con una consulta primaria y una secundaria tenemos estas tres variables:

$GLOBALS['wp_the_query'] 
$GLOBALS['wp_query'] // should be the copy of first one
$custom_query // secondary

Digamos que cada uno de estos tres toma 1M de memoria. El total sería 3M de memoria. Si lo usamos query_posts, $GLOBALS['wp_query']se desarmará y se volverá a crear.

PHP5 + debería ser inteligente vaciando el $GLOBALS['wp_query']objeto, al igual que en PHP4 lo hicimos con elunset($GLOBALS['wp_query']);

function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

Como resultado, query_postsconsume 2M de memoria en total, mientras que get_postsconsume 3M de memoria.

Tenga en cuenta query_postsque no estamos devolviendo el objeto real, sino una referencia al objeto.

Desde php.net : Una referencia de PHP es un alias, que permite que dos variables diferentes escriban en el mismo valor. A partir de PHP 5, una variable de objeto ya no contiene el objeto como valor. Solo contiene un identificador de objeto que permite a los accesores de objetos encontrar el objeto real. Cuando un objeto se envía por argumento, se devuelve o se asigna a otra variable, las diferentes variables no son alias: contienen una copia del identificador, que apunta al mismo objeto.

También en PHP5 + el operador de asignación (=) es inteligente. Utilizará una copia superficial y no una copia de objetos impresos. Cuando escribimos así, $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];solo se copiarán los datos, no todo el objeto, ya que comparten el mismo tipo de objeto.

Aquí hay un ejemplo

print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Resultará:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Intenta restablecer la consulta:

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Resultará:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Puede crear problemas incluso si usa WP_Query

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );   
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Por supuesto, la solución sería usar la wp_reset_queryfunción nuevamente.

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

Es por eso que creo que query_postspuede ser mejor desde el punto de vista de la memoria. Pero siempre debes hacer wp_reset_querytrucos.

prosti
fuente
10

Acabo de crear un nuevo boleto de trac, boleto # 36874 , para proponer la desaprobación de query_posts(). Si se aceptará o no, sigue siendo una buena pregunta.

El verdadero gran problema con query_posts()es que todavía es ampliamente utilizado por complementos y temas, a pesar de que ha habido muy buenos escritos sobre el tema de por qué NUNCA debes usarlo. Creo que la publicación más épica aquí en WPSE es la siguiente:

deprecation! == eliminación , por lo que la degradación query_posts()no detendrá su uso por parte de desarrolladores de baja calidad y personas en general que no conocen WordPress y que usan tutoriales de baja calidad como pautas. Así como alguna prueba, cuántas preguntas qué seguimos hasta aquí donde la gente utiliza caller_get_postsen WP_Query? Ha quedado en desuso durante muchos años.

Sin embargo, las funciones y argumentos en desuso pueden eliminarse en cualquier momento que los desarrolladores principales lo consideren apropiado, pero esto probablemente nunca sucederá, query_posts()ya que esto romperá millones de sitios. Entonces, sí, probablemente nunca veremos la eliminación total de query_posts(), lo que podría llevar al hecho de que lo más probable es que nunca sea desaprobado.

Sin embargo, este es un punto de partida, pero hay que recordar que despreciar algo en WordPress no detiene su uso.

ACTUALIZACIÓN 19 de mayo de 2016

El boleto que levanté ahora está cerrado y marcado como duplicado de un boleto de 4 años , que se cerró como wontfix y se volvió a abrir y aún permanece abierto y sin resolver.

Parece que los desarrolladores principales se aferran a este viejo y pequeño mal fiel. Todos los interesados, aquí está el boleto duplicado de 4 años

Pieter Goosen
fuente
¿Por qué cerraron el ticket core.trac.wordpress.org/ticket/36874 ? @PieterGoosen, ¿puede incluir el enlace a este hilo en su boleto core.trac.wordpress.org/ticket/36874 ya que esta pregunta se relaciona con el boleto 1: 1
prosti
@prosti Parece que se marcó como duplicado ya que este problema ya se ha mencionado ... Hace 4 años encontrado aquí .
Howdy_McGee
3

[algo despotricado]

Es la filosofía central permanente en este punto que nada es realmente desaprobado. El aviso de desaprobación, aunque es bueno tenerlo, solo se ignorará si la función no se descarta en algún momento. Hay muchas personas que no se desarrollan con WP_DEBUGy no notarán el aviso si no se produce una rotura real.

OTOH mano, esta función es como una gotodeclaración. Personalmente nunca (para una definición más pequeña que la esperada) uségoto pero puedo entender los argumentos que apuntan a alguna situación en la que no es malo por defecto. Lo mismo ocurre con query_posts, es una manera simple de configurar todos los globales necesarios para hacer un bucle simple, y puede ser útil en el contexto ajax o rest-api. Nunca lo usaría también en esos contextos, pero puedo ver que allí, es más una cuestión de estilo de codificación que una función que es malvada en sí misma.

Yendo un poco más profundo, el problema principal es que los globales deben establecerse en absoluto. Ese es el problema principal, no la única función que ayuda a configurarlos.

Mark Kaplun
fuente
Y para la comparación, es realmente query_postsmás lento que una consulta secundaria (léase: no la consulta principal).
prosti
@prosti, como solo establece y ejecuta un wp_query, ¿cuánto más lento puede ser? Seguro que hay algo de sobrecarga, pero probablemente estamos hablando de milisegundos aquí. Por supuesto, esto supone que lo usa en lugares donde WP no proporciona una consulta de forma predeterminada. En lugares donde lo hace, es malo, no el query_postssí mismo, sino la consulta inútil que se hizo cuando WP se estaba cargando
Mark Kaplun