web-dev-qa-db-fra.com

Ordre par plusieurs champs méta

J'ai deux champs personnalisés:

  • meta_key1 est booléen (0 ou 1).
  • meta_key2 est une valeur de date - 20150623 par exemple.

Tous les champs meta_key2 n'ont pas de valeur explicite. On dirait qu'ils sont traités comme une chaîne vide ''.

J'aimerais trouver toutes les publications qui ont une valeur meta_key2 (date) supérieure à celle d'aujourd'hui ou une valeur meta_key1 qui est true.

C'est l'ordre que je voudrais qu'ils affichent.

  1. key2.meta_value >= today ASC
  2. key1.meta_value = TRUE ordered by date ASC (ceux avec les dates données en premier).

Une partie du problème réside dans le fait que les champs avec un paramètre de date vide semblent être traités en tant que 0 et arrivent donc en premier dans l'ordre ASC. J'ai essayé d'utiliser COALESCE pour expliquer cela et j'ai eu du succès, mais je suis suspendu à une partie. Je ne parviens pas à afficher les articles key1.meta_value = FALSE et key2.meta_value is >= today dans l'ordre ASC.

Voici ma requête:

$meta_key1 = 'prog_ongoing';
$meta_key2 = 'prog_date_start';
$start_date = date('Ymd');

$postids = $wpdb->get_col( $wpdb->prepare( 
"
SELECT      DISTINCT key1.post_id
FROM        $wpdb->postmeta key1
INNER JOIN  $wpdb->postmeta key2
            ON key2.post_id = key1.post_id
            AND key2.meta_key = %s
WHERE       key1.meta_key = %s
            AND key1.meta_value is TRUE
            OR key2.meta_value >= %d
ORDER BY    COALESCE(NULLIF(key1.meta_value, 0), 0) DESC, COALESCE(NULLIF(key2.meta_value, ''), $start_date) ASC, key2.meta_value ASC 
",
$meta_key2,
$meta_key1,
$start_date
) );

Je ne suis pas sûr que ma déclaration COALESCE sur key1.meta_value fasse quoi que ce soit. Merci pour toute idée à ce sujet.

2
eleclair

Un problème avec la requête est que la jointure automatique avec la WHERE ambiguë vous donne un jeu de données croisé (masqué par la DISTINCT), il serait donc plus simple d'utiliser wp_post comme base pour attacher les jointures qui correspondent exactement aux clés, par exemple

    SELECT      p.ID, key1.meta_value as prog_ongoing, key2.meta_value as prog_date_start
    FROM        $wpdb->posts p
    INNER JOIN  $wpdb->postmeta key1
                ON key1.post_id = p.ID
                AND key1.meta_key = %s
    INNER JOIN  $wpdb->postmeta key2
                ON key2.post_id = p.ID
                AND key2.meta_key = %s

ce qui donne un jeu de données linéaire. Ensuite, vous pouvez ajouter (ou non) la clause WHERE pour restreindre les données:

    WHERE       key1.meta_value IS TRUE OR key2.meta_value >= %d

et pour le ORDER BY, utilisez un tri de champs uniques dans l'instruction CASE:

    ORDER BY    CASE
                    WHEN key2.meta_value >= %d THEN CONCAT('A', key2.meta_value)
                    WHEN key1.meta_value AND key2.meta_value THEN CONCAT('B', key2.meta_value)
                    WHEN key1.meta_value THEN 'C'
                    ELSE 'D'
                END ASC

ou quelque chose de similaire, ce qui précède a besoin de la variable prepare pour être:

    $meta_key1,
    $meta_key2,
    $start_date, $start_date

Vous pouvez utiliser le filtre posts_orderby pour faire quelque chose de similaire avec WP_Query (bien qu'il utilise une méthode qui produit des jeux de données croisés, obligeant celui-ci à utiliser un GROUP BY, ce qui peut compliquer les choses). Par exemple

$args = array(
    'posts_per_page' => -1,
    'post_type' => 'cpt_program',
    'meta_query' => array(
        'ongoing' => array(
            'key' => 'prog_ongoing',
        ),
        'start_date' => array(
            'key' => 'prog_date_start',
        )
    ),
);

add_filter( 'posts_orderby', $func = function ( $orderby, $query ) {
    $start_date = date('Ymd');
    global $wpdb;
    $orderby = $wpdb->prepare(
        "
        CASE
            WHEN mt1.meta_value >= %d THEN CONCAT('A', mt1.meta_value)
            WHEN {$wpdb->postmeta}.meta_value AND mt1.meta_value THEN CONCAT('B', mt1.meta_value)
            WHEN {$wpdb->postmeta}.meta_value THEN 'C'
            ELSE 'D'
        END ASC
        "
        , $start_date
    );
    return $orderby;
}, 10, 2 );
$query = new WP_Query( $args );
remove_filter( 'posts_orderby', $func, 10, 2 );
2
bonger

Premier passage pour une solution. Il utilise le nouveau méta-tri introduit dans la version 4.2:

 <?php
    $args = array(
        'posts_per_page' => -1,
        'meta_query' => array(
            'relation' => 'OR',
            'ongoing' => array(
                'key'     => 'prog_ongoing',
                'value'   => 1
            ),
            'start_date' => array(
                array(
                    'key' => 'prog_date_start',
                    'value'   => date('Ymd'),
                    'type'    => 'numeric',
                    'compare' => '>='
                )
            )
        ),
        'orderby' => 'start_date ongoing',
        'order'   => 'ASC',
    );

    $programs = new WP_Query($args);
    ?>
    <?php while($programs->have_posts()): $programs->the_post(); ?>
        <h1><?php the_title(); ?></h1>
    <?php endwhile; ?>

Je pense que j'ai bien compris la logique, mais laissez-moi savoir si autrement

MODIFIER

Vous pouvez vérifier la nouvelle syntaxe pour trier plusieurs champs personnalisés dans Wordpress 4.2 de la manière suivante:

3
Manny Fleurmond

Pourquoi ne pas utiliser WP_Query()? Beaucoup plus simple de cette façon:

    <?php
    $args = array(
        'posts_per_page' => -1,
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'prog_ongoing',
                'value'   => 1
            ),
            array(
                'relation' => 'AND',
                array(
                    'key' => 'prog_date_start',
                    'value'   => date('Ymd'),
                    'type'    => 'numeric',
                    'compare' => '<='
                ),
                array(
                   'key' => 'prog_date_start',
                    'value'   => 1,
                    'type'    => 'numeric',
                    'compare' => '>'
                )
            )
        ),
        'orderby' => 'meta_value_num date',
        'order'   => 'ASC',
        'meta_key' => 'prog_date_start'
    );

    $programs = new WP_Query($args);
    ?>
    <?php while($programs->have_posts()): $programs->the_post(); ?>
        <h1><?php the_title(); ?></h1>
    <?php endwhile; ?>
0
passatgt