Ordenar por valor meta incluyendo entradas que no lo tienen

10 jun 2013, 01:34:49
Vistas: 41.4K
Votos: 53

He estado modificando la búsqueda integrada de WP usando el filtro pre_get_posts, permitiendo al usuario ordenar las entradas (incluyendo varios tipos de post personalizados) por diferentes campos.

El problema que tengo es que cuando le digo a WP que ordene por un valor meta, excluirá todas las entradas que no tienen ese valor meta establecido. Esto hace que el número de resultados cambie si cambias la ordenación de, por ejemplo, "Precio" a "Fecha" porque las "Entradas" no tienen "Precio" establecido pero los "Artículos" sí.

Esto no es lo que quiero, así que me gustaría saber si hay alguna manera de incluir TODAS las entradas - incluso aquellas que carecen del valor meta por el que estoy ordenando - y poner las que no tienen el valor al final.

Sé cómo ordenar por más de un campo pero eso no ayuda.

Gracias

Parece que no soy el único con esta pregunta: ¿Forma de incluir posts tanto con como sin cierta meta_key en los argumentos para wp_query? pero no hay solución allí.

Actualización

He probado la respuesta pero no estoy seguro si la entendí correctamente, esto es lo que tengo ahora:

<?php
function my_stuff ($qry) {
    $qry->set('meta_query', array(array(
        'key' => 'item_price', 
        'value' => '', 
        'compare' => 'NOT EXISTS'
    )));

    $qry->set('orderby', 'meta_value date'); # La ordenación funciona tanto con meta_value como con meta_value_num - He probado ambos
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

El valor meta es un número (se usa para almacenar un precio como sugiere el nombre)

Actualización 2

He comentado la parte de ordenación y ahora solo tengo esto:

<?php
$qry->set('meta_query', array(array(
    'key' => 'item_price', 
    'value' => '', 
    'compare' => 'NOT EXISTS'
)));

Con este código la consulta parece devolver todas las entradas que no tienen la clave item_price y ninguna de las entradas que la tienen. Es decir, el problema ahora está invertido.

Si agrego el código de ordenación también, obtengo 0 resultados.

Edición: ...tres años después... :P Tuve este problema de nuevo. Probé todas las respuestas dadas y ninguna funciona. No estoy seguro por qué algunas personas parecen pensar que funcionan pero al menos para mí no funcionan.

La solución que terminé usando es utilizar el filtro save_post - asegurándome de que todas las entradas tengan el campo personalizado por el que deseo ordenar. Es un poco molesto tener que hacerlo, pero mientras lo hagas temprano probablemente no tendrás problemas.

En este caso estaba construyendo un "contador de vistas" en las entradas y quería que los usuarios pudieran ordenar por las entradas más leídas. Nuevamente, las entradas que nunca han sido vistas (supongo que es bastante improbable, pero aun así) desaparecían al ordenar por el contador de vistas. Agregué este fragmento de código para asegurarme de que todas las entradas tengan un contador de vistas:

add_action('save_post', function ($postId) {
    add_post_meta($postId, '_sleek_view_count', 0, true);
});
11
Comentarios

Por favor muéstranos tu código. Facilita la respuesta.

kaiser kaiser
10 jun 2013 02:24:14

Primero: meta_query y tax_query son siempre un array( array() ) ya que combinan múltiples arrays. Segundo - como mencioné en mi respuesta - necesitas usar meta_value_num para números. También podría ser necesario definir explícitamente el meta_value_num (consulta la entrada de la página Codex de WP_Query). Por último, no tiene sentido ordenar en dirección ASC y DESC al mismo tiempo. Eso no es posible. El delimitador de espacio solo funciona para orderby y no puedes indicar que ordene el primero ASC y el segundo DESC. Para eso está el filtro posts_clauses.

kaiser kaiser
10 jun 2013 03:42:38

Y asegúrate de que tus entradas meta_value_num sean números reales. He visto demasiadas veces que alguien afirma que es un número, pero en realidad lo guarda como cadena en la base de datos.

kaiser kaiser
10 jun 2013 03:43:23

Gracias por tu ayuda, probaré esto y te responderé. La razón de ASC DESC es para que ordene por el meta_value en ASC y por la date en DESC, por lo que puedo ver funciona.

powerbuoy powerbuoy
10 jun 2013 04:06:12

Además, el ordenamiento en sí funciona bien. El único problema es que las publicaciones (o tipos de publicación personalizados) sin el valor meta no se muestran. (En relación con meta_value_num)

powerbuoy powerbuoy
10 jun 2013 04:29:58

Para aclarar, ¿quieres incluir tanto publicaciones con un cierto metavalor como publicaciones que no tengan ese metavalor establecido?

Howdy_McGee Howdy_McGee
15 abr 2014 21:20:52

@Howdy_McGee eso es correcto. Algunos de mis tipos de posts personalizados TIENEN este valor establecido. Otros no. Y los tipos de posts integrados (como POST y PAGE) no lo tienen. Así que cada vez que intento ordenar por ese campo personalizado, solo aparecen los posts CON el campo personalizado.

powerbuoy powerbuoy
15 abr 2014 21:31:23

He visto preguntas similares antes, @G.M. tiene una buena respuesta, pero debes mostrar todos los resultados en la misma página, así que si eso no funciona para ti, entonces o muestras solo posts con el meta_key o posts sin el meta_key, no ambos. Aquí está el post al que me refiero.

Howdy_McGee Howdy_McGee
15 abr 2014 21:35:10

Oh, vale, gracias. Esto era para un proyecto que ya terminé hace tiempo, pero lo tendré en cuenta para la próxima vez. Aunque, preferiría seguir usando el "bucle normal de WP" con la paginación habitual...

powerbuoy powerbuoy
15 abr 2014 23:38:05

He abierto un ticket en Trac para posiblemente añadir una solución a las clases de consulta principales que haría posible este tipo de cosas: https://core.trac.wordpress.org/ticket/42907

J.D. J.D.
15 dic 2017 00:11:13

@powerbuoy por favor revisa mi respuesta para encontrar una solución.

Paul Paul
23 feb 2019 18:36:01
Mostrar los 6 comentarios restantes
Todas las respuestas a la pregunta 17
9
25

Sencillo y fácil, probado en 2018 y actualmente en uso en producción.

Actualización 2022: Modificado para que la consulta NOT_EXISTS venga antes de la consulta EXISTS, y se aclara el efecto que tienen múltiples claves en la cláusula orderby.

$query->set( 'meta_query', array(
    'relation' => 'OR',
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'NOT EXISTS'
    ),
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'EXISTS'
    ),
) );
$query->set( 'orderby', 'meta_value title' ); 

Esto verifica todos los elementos con y sin la meta clave, sin especificar un valor. La consulta meta proporciona la clave para el orderby de manera confiable. Ha sido probado. La cláusula orderby con meta_value/meta_value_num usará la última clave en la cadena.

Ejemplo práctico

/**
 * Modifica la consulta antes de recuperar los posts. Establece los 
 * parámetros `meta_query` y `orderby` cuando no se ha establecido 
 * ningún parámetro `orderby` (ordenación por defecto).
 * 
 * @param   WP_Query  $query  El objeto completo `WP_Query`.
 * @return  void
 */
function example_post_ordering( $query ) {
    
    // Si no está en wp-admin, 
    // y la consulta es la consulta principal, 
    // y la consulta no es una consulta singular, 
    // y la consulta no tiene un parámetro orderby establecido...
    // Nota: verificar tipos de post, etc. aquí según sea necesario.
    if ( ! is_admin() 
    && $query->is_main_query() 
    && ! $query->is_singular() 
    && empty( $query->get( 'orderby' ) ) ) {
        
        // Establecer solo `meta_key` no es suficiente, ya que esto 
        // ignorará los posts que aún no tengan, o nunca tendrán un 
        // valor para la clave especificada. Esta meta consulta 
        // registrará la `meta_key` para ordenar, pero no ignorará 
        // aquellos posts sin un valor para esta clave.
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'NOT EXISTS'
            ),
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'EXISTS'
            ),
        ) );

        // Ordenar por el meta valor, luego por el título si múltiples 
        // posts comparten el mismo valor para la meta clave proporcionada.
        // Usar `meta_value_num` si los meta valores son numéricos.
        $query->set( 'orderby', 'meta_value title' );
    }

}

add_action( 'pre_get_posts', 'example_post_ordering', 10 );

Esto ordenará los posts por custom_meta_key por defecto, y no ignorará los posts sin un valor para esa clave.

18 abr 2018 18:23:10
Comentarios

Solo con leer el código, parece que lo único que hace es obtener posts que tienen custom_meta_key y posts que no tienen custom_meta_key. Siéntete libre de incluir un ejemplo práctico funcional con ordenamiento.

powerbuoy powerbuoy
18 abr 2018 20:33:10

Tienes razón, eso es todo lo que está haciendo, pero la línea de abajo es la responsable de ordenar por meta_value (de la meta key que se está consultando). $query->set( 'orderby', 'meta_value title' ); (Ordenar por meta value, luego por título cuando múltiples posts tienen el mismo valor para la meta key). Esto debería hacerse en el hook pre_get_posts, usando la variable $query que se pasa. Ten en cuenta que la pregunta era cómo ordenar por meta value, sin ignorar los posts que no tienen un valor para esa meta key.

noahmason noahmason
18 abr 2018 21:18:00

@powerbuoy Ver ejemplo práctico actualizado

noahmason noahmason
18 abr 2018 21:55:47

De acuerdo, lo intentaré la próxima vez que me enfrente a este problema.

powerbuoy powerbuoy
27 abr 2018 21:13:34

Funcionó para mí en una llamada personalizada get_posts() para colocar las publicaciones con meta _featured en la parte superior, y luego ordenar por fecha después de eso. ¡Gracias!

Nate Beaty Nate Beaty
9 nov 2018 20:03:22

Esto solo funcionó para mí cuando invertí el orden de NOT EXISTS y EXISTS. Funcionaba, pero el orderby se ignoraba.

mikemike mikemike
3 ene 2021 18:48:42

@mikemike gracias por esa información, haciendo estos cambios funcionó para mí

bigdaveygeorge bigdaveygeorge
11 ene 2021 19:45:54

@mikemike copiado - acabo de hacer esta actualización mencionada.

noahmason noahmason
9 jul 2022 20:08:50

Justo estaba enfrentando este problema y esto funcionó perfectamente,

AuRise AuRise
5 jul 2024 17:32:37
Mostrar los 4 comentarios restantes
4
12

Este método devolverá todas las publicaciones, incluyendo aquellas con y sin el meta_key solicitado, pero hará cosas extrañas al ordenar.

add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
    $qry->set(
        'meta_query',
        array(
            'relation' => 'OR', # Las coincidencias con este meta_query se añadirán a las que coincidan con la consulta 'meta_key'
            array(
                'key' => 'item_price', 
                'value' => 'bug #23268', 
                'compare' => 'NOT EXISTS'
            )
        )
    );

    $qry->set('orderby', 'meta_value date'); # El ordenamiento funciona con meta_value y meta_value_num - probé ambos
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Descubrí esto experimentando con todas las diferentes respuestas a esta pregunta y analizando el SQL generado mediante prueba y error. Parece que configurar array('meta_query' => array('relation' => 'OR')) genera un LEFT JOIN apropiado en lugar de un INNER JOIN, que es necesario para incluir publicaciones que carecen de los metadatos. Especificar NOT EXISTS evita que la cláusula WHERE filtre las publicaciones que no tienen el campo meta. Para esta WP_Query en particular, el SQL generado es (se añadieron indentación/saltos de línea):

SELECT SQL_CALC_FOUND_ROWS
    wp_posts.ID
    FROM wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
    LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'item_price')
    WHERE 1=1
    AND ( wp_term_relationships.term_taxonomy_id IN (2) )
    AND wp_posts.post_type = 'post'
    AND (wp_posts.post_status = 'publish'
        OR wp_posts.post_status = 'private')
    AND (wp_postmeta.meta_key = 'item_price'
        -- ¡Mira aquí! Le damos permiso a SQL para elegir una fila
        -- aleatoria de wp_postmeta cuando esta publicación en particular
        -- carece de 'item_price':
        OR  mt1.post_id IS NULL )
    GROUP BY wp_posts.ID
    ORDER BY wp_postmeta.meta_value,wp_posts.post_date DESC
    LIMIT 0, 10

El resultado es un listado de todas las publicaciones con el meta_value de item_price y aquellas que carecen de item_price. Todas las publicaciones con item_price se ordenarán correctamente entre sí, pero las publicaciones sin item_price usarán algún otro valor meta aleatorio (por ejemplo, _edit_last que parece ser 1 con frecuencia en mi base de datos u otro metadato interno de WordPress completamente arbitrario) para su wp_postmeta.meta_value en la cláusula ORDER BY. Así que, aunque este método está cerca y puede parecer funcionar para ciertos datos, está roto. Todo lo que puedo decir es que, si los valores de tu item_price no entran en conflicto con los campos meta aleatorios que MySQL elige para las publicaciones sin item_price, esto podría funcionarte. Si solo necesitas garantizar que tus publicaciones con item_price estén ordenadas correctamente entre sí sin importar el orden del resto, puede ser aceptable. Pero creo que esto es solo una limitación en WordPress. Por favor corríjanme, espero estar equivocado y que haya una manera de solucionarlo ;-).

Parece que para el INNER JOIN wp_postmeta, MySQL está eligiendo una fila aleatoria entre las múltiples filas de postmeta asociadas con la publicación cuando falta el meta_key en dicha publicación. Desde una perspectiva SQL, necesitamos averiguar cómo decirle a WordPress que genere ORDER BY mt1.meta_value. Esta columna es propiamente NULL cuando falta nuestro meta_key solicitado, a diferencia de wp_postmeta.meta_value. Si pudiéramos hacer eso, SQL ordenaría estos valores NULL (entradas faltantes) antes que cualquier otro valor, dándonos un orden bien definido: primero todas las publicaciones sin el campo postmeta específico, luego las que sí lo tienen. Pero ese es el problema: 'orderby' => 'meta_value' solo puede referirse a 'meta_key' => 'item_price' y el wp_postmeta sin alias siempre es un INNER JOIN en lugar de un LEFT JOIN, lo que significa que wp_postmeta.meta_value y wp_postmeta.meta_key nunca pueden ser NULL.

Así que supongo que debo decir que esto no es posible con el WP_Query integrado de WordPress tal como está documentado actualmente (en WordPress 3.9.1). Molesto. Así que si realmente necesitas que esto funcione correctamente, probablemente necesites engancharte en otra parte de WordPress y modificar el SQL generado directamente.

31 jul 2014 08:32:41
Comentarios

¡Se ve muy prometedor! Lo probaré la próxima vez que tenga este problema. Me gustaría darte la respuesta ahora mismo, pero preferiría confirmar que funciona para mí primero.

powerbuoy powerbuoy
5 ago 2014 19:09:06

Esto evitó que se mostrara cualquier cosa en mi caso. No apareció nada después de implementar esto.

Jake Jake
26 oct 2016 14:32:47

@Jake Sí, lo mismo aquí. Tuve este problema nuevamente hoy e intenté esto. Devuelve 0 resultados.

powerbuoy powerbuoy
7 jun 2017 12:00:52

¿Qué versión de WordPress están usando ustedes? Creo que esta publicación describe cómo usar una API interna y no documentada que no es compatible con WordPress, por lo que probablemente solo funcione si están en WordPress-3.9.1 o no muchas versiones más allá de esa.

binki binki
7 jun 2017 17:40:40
0

El problema que todos están teniendo aquí tiene que ver con el orden de las consultas meta. Para ordenar correctamente, necesitarás colocar la consulta "NOT EXISTS" antes de la consulta "EXISTS".

La razón de esto es porque WordPress utiliza el meta_value del último "LEFT JOIN" en la cláusula "ORDER BY".

Por ejemplo:

$pageQuery = new WP_Query([
    'meta_query' => [
        'relation' => 'OR',
        ['key' => 'item_price', 'compare' => 'NOT EXISTS'], // ¡esto va primero!
        ['key' => 'item_price', 'compare' => 'EXISTS'],
    ],
    'order' => 'DESC',
    'orderby' => 'meta_value_num',
    'post_status' => 'publish',
    'post_type' => 'page',
    'posts_per_page' => 10,
]);
23 feb 2019 18:33:29
3

Existen dos posibles soluciones para esto:

1. Todos los posts tienen meta

La mejor solución que he encontrado aquí es asignar un precio de 0 a los demás posts/productos. Puedes hacer esto manualmente, o recorrer todos los posts y si el precio está vacío entonces actualizarlo.

Para hacer esto manejable en el futuro puedes usar el hook save_post y asignarles un valor cuando se añadan por primera vez (solo si está vacío).

2. Múltiples consultas

Podrías ejecutar la primera consulta como lo estás haciendo y almacenar los IDs de los posts devueltos. Luego podrías ejecutar otra consulta para todos los posts ordenados por fecha, excluyendo los IDs obtenidos en la primera consulta.

Puedes entonces mostrar los dos resultados por separado en el orden deseado.

20 abr 2014 17:01:04
Comentarios

Tres años después y tuve el mismo problema de nuevo :P Tuve que usar el método save_post (he actualizado mi pregunta con el código que usé).

powerbuoy powerbuoy
7 jun 2017 12:11:42

Esto es posible sin necesidad de usar hooks o ejecutar múltiples consultas. Mira mi respuesta.

Jacob Raccuia Jacob Raccuia
11 jul 2020 07:51:50

Deberías haber mostrado ejemplos de código.

Steve Moretz Steve Moretz
6 ago 2021 12:50:35
0

También me encontré con un problema similar y la siguiente solución me ayudó:

$args = array(
'post_type' => 'kosh_products',
'posts_per_page' => -1,
'meta_query' => array(
    'relation' => 'OR',
    'category_sort_order' => array(
        'key' => '_sort_order',
        'compare' => 'EXISTS'
    ),
    'category_sort_order_not_exists' => array(
        'key' => '_sort_order',
        'compare' => 'NOT EXISTS'
    ), 
),
'orderby' => array( 
    'category_sort_order' => 'ASC',
    'date' => 'ASC'
));
$query = new WP_Query( $args );

Encontré una descripción en WordPress Codex con el título "'orderby' con múltiples 'meta_key'": https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters Parámetros de ordenación en WP_Query

25 may 2019 22:24:40
1

Creo que tengo una solución.

Puedes usar dos meta_keys, una que todos los posts tengan (como "_thumbnail_id"), y la meta_key que deseas usar como filtro.

Así que tus argumentos serían:

$qry->set(
    'meta_query',
    array(
        'relation' => 'OR',
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        ),
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        )
    )
);

$qry->set('orderby', 'meta_value date'); # El ordenamiento funciona con meta_value así como con meta_value_num - he probado ambos
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
22 ago 2013 21:06:01
Comentarios

El problema aquí es la comparación de cadena vacía, elimínala y funcionará 'value' => '', también la segunda comparación debería ser NOT EXISTS y la última instrucción de configuración no es necesaria

nodws nodws
24 abr 2018 18:19:50
0

Yo mismo tuve este problema con valores meta numéricos y me di cuenta de que el orden de la consulta también es importante. Para mí, la consulta NOT EXISTS debe ser la primera.

Ejemplo:

$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_query', [
    'relation' => 'OR',
    [ 'key' => 'your_meta_name', 'compare' => 'NOT EXISTS' ],
    [
        'key' => 'your_meta_name',
        'compare' => 'EXISTS',
    ],
] );

También es importante, para obtener la dirección correcta con valores numéricos, que el ’orderby’ general esté configurado como ’meta_value_num’. De lo contrario, obtendrás resultados extraños con valores numéricos, por ejemplo:

1, 2, 20, 21, 3, 4, 5 …

En lugar de:

1, 2, 3, 4, 5 … 20, 21

12 ene 2019 03:16:08
0

La consulta meta OR que combina NOT EXISTS y EXISTS funciona pero causó una consulta lenta.

Esta solución funciona mucho más rápido.

La clave principal estuvo en la cláusula SQL order by: order by YOUR_FIELD is NULL, YOUR_FIELD

add_filter('posts_orderby', function (string $orderby, WP_Query $query) {
    if ('MY_CUSTOM_SORT' === $query->get('orderby')) {
        $orderby = "mta.meta_value is NULL, mta.meta_value ='', mta.meta_value ASC";
    }
    return $orderby;
}, 10, 2);

add_filter('posts_join', function (string $join, WP_Query $query) {
    if ('MY_CUSTOM_SORT' === $query->get('orderby')) {
        $join .= " LEFT JOIN wp_postmeta AS mta ON ( wp_posts.ID = mta.post_id AND mta.meta_key = 'SOME_META_KEY')";
    }
    return $join;
}, 10, 2);

Y en los argumentos de tu WP_Query:

$args['orderby'] = 'MY_CUSTOM_SORT';
19 oct 2022 01:26:42
0

Esta es una pregunta muy antigua pero ninguna de las respuestas era lo suficientemente robusta para que yo la usara y estuviera satisfecho con los resultados. La solución a continuación todavía utiliza WP_Query, pero manipula las declaraciones join y order by.

Hace uso de una función de MySQL llamada ORDER BY IF, que permite ejecutar lógica durante tu declaración ORDER BY. Esto es importante porque las otras soluciones aquí simplemente agrupan los resultados sin valor, ya sea al principio o al final de la lista, lo cual es inútil cuando intentas ordenar por 2 claves.

En el siguiente ejemplo tengo un CPT de 'businesses' (negocios). Quiero que todos se ordenen por título, pero aquellos con is_premium deben aparecer primero. He comentado el código para que puedas ver lo que está pasando en cada parte.

<?php
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$args = [
    'post_type' => 'business',
    'posts_per_page' => 12,
    'meta_query' => [
        'relation' => 'OR',
        [
            'key' => 'is_premium', 
            'compare' => 'NOT EXISTS' // NOT EXISTS primero es importante
        ],
        [
            'key' => 'is_premium', 
            'compare' => 'EXISTS'
        ],
    ]
    ,
    'orderby' => [
        'meta_value' => 'DESC', // Este es el campo is_premium
        'title' => 'ASC',
    ],
    'paged' => $paged
];

// Añadimos nuestro propio join a la consulta
function custom_join($join) {
    global $wpdb;

    if( ! is_admin() ) {
        $join .= $wpdb->prepare(
        ' LEFT JOIN ' . $wpdb->postmeta . ' cpm ON cpm.post_id = ' . $wpdb->posts . '.ID AND cpm.meta_key = %s'
        , 'is_premium' );
    }

    return $join;
}
add_filter('posts_join','custom_join');

// Añadimos nuestro propio order by a la consulta
function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        // Aquí ordenamos por meta_value SOLO si es 1, de lo contrario lo ignoramos para esa declaración de orden, lo que  
        // significa que se ordenará por la siguiente declaración (título), igual que los otros resultados
        $orderby_statement = "IF(cpm.meta_value = 1, 1, 0) DESC, wp_posts.post_title ASC ";
    }

    return $orderby_statement;
}
add_filter('posts_orderby','custom_orderby', 10, 2 ); 

// Consulta
$business = new WP_Query( $args );

// Eliminamos los filtros
remove_filter('posts_orderby','custom_orderby');
remove_filter('posts_join','custom_join');
3 ene 2021 19:41:17
0

Si es apropiado, puedes agregar un valor meta predeterminado cada vez que se guarda o actualiza una publicación, si el valor meta no existe.

function addDefaultMetaValue($post_id) {
    add_post_meta($post_id, 'item_price', 0, true);
}
add_action('save_post', 'addDefaultMetaValue');

Si estás utilizando un tipo de publicación personalizado, reemplaza add_action('save_post', 'addDefaultMetaValue'); por add_action('save_post_{post_type}', 'addDefaultMetaValue'); por ejemplo: add_action('save_post_product', 'addDefaultMetaValue');

20 ago 2015 19:17:40
6

Existe un posible valor orderby de meta_value para eso.

$query = new WP_Query( array ( 
    'meta_key'   => 'your_keys_name',
    'orderby'    => 'meta_value',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'your_meta_key',
         'value'   => '',
         'compare' => 'NOT EXISTS',
         // 'type'    => 'CHAR',
    ) )
) );

Si tienes valores numéricos, simplemente usa meta_value_num en su lugar.

Aviso: Esto no ha sido probado, pero debería funcionar. El punto es que necesitas especificar tus valores meta_key y key. De lo contrario, no puedes comparar contra valores no existentes, lo que debería permitir consultar ambos tipos de posts. Es un poco hackeado, pero mientras funcione...

10 jun 2013 02:38:53
Comentarios

Gracias por tu respuesta, por favor revisa mi pregunta actualizada, no estoy seguro de haber entendido correctamente.

powerbuoy powerbuoy
10 jun 2013 03:05:54

Aún no he logrado que esto funcione, así que si tienes una solución me encantaría saber qué estoy haciendo mal. Además, he establecido una recompensa en SO si quieres reclamarla: http://stackoverflow.com/questions/17016770/wordpress-order-by-meta-value-if-it-exists-else-date/17183618

powerbuoy powerbuoy
20 jun 2013 05:45:54

Dos cosas. 'your_keys_name' y 'your_meta_key' deberían ser la misma cadena en lugar de distintas, de lo contrario parece que has malinterpretado la pregunta. En segundo lugar, probé esto en mi configuración local y excluye cualquier publicación donde existe la clave (a través del meta_query) y excluye cualquier publicación donde falta la clave (a través de meta_key), lo que resulta en que no se muestren publicaciones. Sin embargo, esta respuesta es un paso hacia algo que funciona al menos ;-).

binki binki
31 jul 2014 07:16:07

Oh, curiosamente, esta respuesta sí funciona si solo agregas 'relation' => 'OR' al meta_query. Cosas locas o_o.

binki binki
31 jul 2014 07:18:19

@binki Simplemente envía una [edición] a mi pregunta y cambia las partes que creas que deberían modificarse. Este es un sitio impulsado por la comunidad :)

kaiser kaiser
31 jul 2014 12:58:04

@kaiser, seguí investigando, incluso haciendo pruebas, y terminé con un pequeño monólogo. Supongo que mi nueva respuesta es el lugar adecuado para guardar mis divagaciones ;-).

binki binki
31 jul 2014 16:24:43
Mostrar los 1 comentarios restantes
0

Esta solución funcionó para mí:

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

    if( ! is_admin() ) {

        $query->set( 'orderby', 'meta_value_num' );
        $query->set( 'order', 'ASC' );
        $query->set( 'meta_query', [
            'relation' => 'OR',
            [ 
                'key' => 'ce_orden', 
                'compare' => 'NOT EXISTS' ],
            [
                'key' => 'ce_orden',
                'compare' => 'EXISTS',
            ],
        ] );

        return $query;

    }

}

Sin embargo, esta solución primero muestra los registros con meta_value nulo. Esta otra solución muestra el orden ASC y los nulos al final:

function custom_join($join) {
    global $wpdb;

    if( ! is_admin() ) {
        $join .= $wpdb->prepare(
        ' LEFT JOIN ' . $wpdb->postmeta . ' cpm ON cpm.post_id = ' . $wpdb->posts . '.ID AND cpm.meta_key = %s'
        , 'ce_orden' );
    }

    return $join;
}

add_filter('posts_join','custom_join');

function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        $orderby_statement = "CAST( COALESCE(cpm.meta_value,99999) as SIGNED INTEGER) ASC";
    }

    return $orderby_statement;
}

add_filter('posts_orderby','custom_orderby', 10, 2 ); 
28 oct 2019 20:03:50
0

Pude resolver esto usando una sola consulta con los argumentos adecuados. WP 4.1+

$args = array( 
    'posts_per_page' => -1, 
    'post_status' => 'publish',
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'key' => 'custom_sort',
            'compare' => 'EXISTS'
        ),
        array(
            'key' => 'custom_sort',
            'compare' => 'NOT EXISTS'
        )
    ),
    'orderby' => 'meta_value_num title',
    'order' => 'ASC',
));

$query = new WP_Query($args);

Ver mi respuesta completa aquí: https://wordpress.stackexchange.com/a/370841/15209

11 jul 2020 07:51:26
3

Terminé solucionando esto con un pequeño truco (en mi opinión), pero cumplió su propósito en mi caso particular.

Puedes engancharte a los filtros posts_join_paged y posts_orderby para modificar las cadenas de unión y ordenamiento. Esto te permitirá ordenar por lo que quieras siempre que primero lo unas, en lugar de que WP_Query asuma que el campo debe existir para esa publicación en particular. Luego puedes eliminar los argumentos meta_key, orderby y order de tu WP_Query.

A continuación hay un ejemplo. Al inicio de cada función tuve que agregar condiciones de escape para ciertos casos, ya que esto se aplicará a todo lo que use WP_Query. Es posible que necesites modificarlo para adaptarlo a tus necesidades específicas.

La documentación sobre estos dos filtros es lamentablemente escasa, así que... ¡buena suerte! :)

add_filter('posts_join_paged', 'edit_join', 999, 2);
add_filter('posts_orderby', 'edit_orderby', 999, 2);

/**
 * Editar JOIN
 *
 * @param string $join_paged_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_join($join_paged_statement, $wp_query)
{
    global $wpdb;
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $join_paged_statement;
    }

    $join_to_add = "
        LEFT JOIN {$wpdb->prefix}postmeta AS my_custom_meta_key
            ON ({$wpdb->prefix}posts.ID = my_custom_meta_key.post_id
                AND my_custom_meta_key.meta_key = 'my_custom_meta_key')
    ";

    // Solo agregar si no está ya incluido
    if (strpos($join_paged_statement, $join_to_add) === false) {
        $join_paged_statement = $join_paged_statement . $join_to_add;
    }

    return $join_paged_statement;
}

/** 
 * Editar ORDER BY
 *
 * @param string $orderby_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_orderby($orderby_statement, $wp_query)
{
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $orderby_statement;
    }

    $orderby_statement = "my_custom_meta_key.meta_value DESC";

    return $orderby_statement;
}
15 abr 2014 21:17:20
Comentarios

El código funciona. Pero el meta_value se maneja como una cadena. Así que 6 tiene una calificación más alta que 50. ¿Es posible hacer modificaciones para tratarlos como números?

Drivingralle Drivingralle
24 ene 2017 19:59:03

@Drivingralle cast(my_custom_meta_key.meta_value as unsigned) DESC debería funcionar...

tfrommen tfrommen
2 feb 2017 23:34:21

Gracias @tfrommen. $orderby_statement = "cast(my_custom_meta_key.meta_value as unsigned) DESC"; funciona perfectamente.

Drivingralle Drivingralle
6 feb 2017 15:22:34
0

Terminé utilizando el filtro 'get_meta_sql' que proporciona WordPress

add_filter( 'get_meta_sql', 'adjust_the_meta_sql' ), 10, 2 );

La función se ve así

function adjust_the_meta_sql( $sql, $queries ) {
            if ( 'my_post_type' === filter_input( INPUT_GET, 'post_type' )
                && '_my_key' === $queries[0]['key'] ) {
                $sql['join']  = preg_replace( '/INNER JOIN wp_postmeta ON \(([^)]+)\)/', 'LEFT JOIN wp_postmeta ON ($1 ' . $sql['where'] . ')', $sql['join'] );
                $sql['where'] = '';
            }
            return $sql;
        }

Resuelve dos problemas con el SQL generado por WordPress.

  1. Convierte el INNER JOIN en un LEFT JOIN
  2. Mueve la cláusula WHERE a la cláusula ON.

Tu consulta particular puede ser diferente, así que asegúrate de probar y ajustar según sea necesario.

18 may 2022 10:01:29
1

Creo que lo que @kaiser estaba intentando hacer era decirle a la consulta que devuelva todos los posts que tengan esa meta clave aplicando una especie de condición where ficticia para no filtrar ninguno de esos posts. Entonces, si sabes que todos los valores que pueden tomar tus campos personalizados son x,y,z, podrías decir "WHERE meta_key IN(x,y,z)" pero la idea es que puedes evitar ese problema por completo diciendo != (''):

$query = new WP_Query( array ( 
    'orderby'    => 'meta_value_num',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'item_price',
         'value'   => '',
         'compare' => '!=',
    ) )
) );

Tampoco lo he probado pero parece que vale la pena intentarlo :-).

23 ago 2013 01:58:26
Comentarios

No puedo probar esto en este momento, pero estoy bastante seguro de que solo devolverá publicaciones donde item_price esté configurado y no sea ''.

powerbuoy powerbuoy
23 ago 2013 14:13:11
0

Amigos, gracias por todas las respuestas válidas pero, actualmente (2022), el anidamiento es la clave:

$query->set( 'orderby', array(
     'meta_value_num' => 'DESC',
     'title' => 'ASC' )
); // prioridades de orden

$query->set( 'meta_query', array(
     'featured' => array(
          'relation' => 'OR',
          array(
               'key' => '_featured',
               'value' => '1', // mostrar primero los posts con valor = 1
          ),
          array( // estas reglas anidadas tienen menos prioridad para el orden, pero nos aseguran mostrar todos los demás posts
               'relation' => 'OR',
               array(
                    'key' => '_featured',
                    'compare' => 'NOT EXISTS', // se explica por sí mismo
               ),
               array(
                    'key' => '_featured',
                    'value' => '0', // el caso donde el valor existe pero necesita ser considerado como no destacado
               )
          )
     )
));

Caso extra: No he probado esto, pero si el meta no es un valor 0/1, la tercera regla debería ser:

array(
      'key' => '_featured',
      'value' => '[valor de prioridad]',
      'compare' => '!='
)
5 dic 2022 21:53:28