web-dev-qa-db-fra.com

pre_get_posts ordre en ne fonctionnant pas

J'utilise le hook pre_get_posts pour modifier une requête avant son exécution.

Les messages en question sont des performances et sont donc basés sur la date et l'heure.

Je souhaite modifier la requête pour obtenir toutes les performances futures (c'est-à-dire que la date est supérieure à aujourd'hui ou la date est aujourd'hui et que l'heure est supérieure ou égale à l'heure actuelle). Cela fonctionne, mais lorsque j'essaie de les classer principalement par date ascendante et secondairement par heure ascendante, l'ordre n'est pas celui attendu.

Les articles sont classés par date correctement, mais seules les performances d’aujourd’hui le sont correctement. Les heures des performances des autres jours sont commandées de manière aléatoire.

Je pense que cela a quelque chose à voir avec le nommage des tableaux de méta_query en relation avec les paramètres orderby, mais je ne peux pas trouver comment résoudre ce problème.

Mon code est le suivant:

    //No limit on number to get
    $query->set('posts_per_page', -1);
    //Get future performances
    $query->set('meta_query', array(
        'relation'  => 'AND',   
        array(
            'relation'  => 'OR',
            'performance_date' => array(
                'key' => 'performance_date', 
                'value' => date('Ymd'),
                'compare' => '>',
                'type' => 'NUMERIC'
            ),
            array(
                'relation'  => 'AND',
                'performance_date' => array(
                    'key' => 'performance_date', 
                    'value' => date('Ymd'),
                    'compare' => '=',
                    'type' => 'NUMERIC'
                ),                      
                'performance_time' => array(
                    'key' => 'performance_time', 
                    'value' => date('H:i'),
                    'compare' => '>=',
                    'type' => 'TIME'
                )
            )               
        )
    ));
    //Order by closest performance first
    $query->set('orderby', array(
        'performance_date' => 'ASC',
        'performance_time' => 'ASC'
    )); 
1
Michael

Je pense que cela serait plus facile si vous aviez le champ personnalisé performance_datetime, car cela pourrait simplifier votre méta requête.

Voici une démo amusante à jouer:

Autant que je sache le problème, vous obtenez trois INNER JOINS sur la table wp_posts, à savoir wp_postmeta, mt1 et mt2. Mais la valeur du champ meta_key, pour ces tables jointes, n'est pas toujours performance_date ou performance_time. Donc, la commande par la méta-valeur, dans l'une de ces tables, ne sera pas précise pour toutes les publications renvoyées par WP_Query.

Une solution de contournement codée en dur, pour ce cas uniquement, pourrait être:

ORDER BY 
   CASE
        WHEN wp_postmeta.meta_key = 'performance_date' 
            THEN CAST( wp_postmeta.meta_value AS SIGNED )
        WHEN mt1.meta_key = 'performance_date' 
            THEN CAST( mt1.meta_value AS SIGNED )
        WHEN mt2.meta_key = 'performance_date' 
            THEN CAST( mt2.meta_value AS SIGNED )
        ELSE
            wp_postmeta.post_id
    END ASC,
    CASE
        WHEN wp_postmeta.meta_key = 'performance_time' 
            THEN CAST( wp_postmeta.meta_value AS TIME)
        WHEN mt1.meta_key = 'performance_time' 
            THEN CAST( mt1.meta_value AS TIME)
        WHEN mt2.meta_key = 'performance_time' 
            THEN CAST( mt2.meta_value AS TIME)
        ELSE
            wp_postmeta.post_id
    END ASC

Vous pouvez tester ceci avec:

$query->set('orderby', 'performance' );

où la commande personnalisée est prise en charge par le plug-in non testé suivant:

add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
    if( 'performance' !== $q->get( 'orderby' ) )
        return $orderby;

    global $wpdb;

    $order = ( 'ASC' === strtoupper( $q->get( 'order' ) ) ) ? 'ASC' : 'DESC';
    $orderby = "
       CASE
            WHEN {$wpdb->postmeta}.meta_key = 'performance_date' 
                THEN CAST( {$wpdb->postmeta}.meta_value AS SIGNED )
            WHEN mt1.meta_key = 'performance_date' 
                THEN CAST( mt1.meta_value AS SIGNED )
            WHEN mt2.meta_key = 'performance_date' 
                THEN CAST( mt2.meta_value AS SIGNED )
            ELSE
                {$wpdb->postmeta}.post_id
       END {$order},
       CASE
            WHEN {$wpdb->postmeta}.meta_key = 'performance_time' 
                THEN CAST( {$wpdb->postmeta}.meta_value AS TIME)
            WHEN mt1.meta_key = 'performance_time' 
                THEN CAST( mt1.meta_value AS TIME)
            WHEN mt2.meta_key = 'performance_time' 
                THEN CAST( mt2.meta_value AS TIME)
            ELSE
                {$wpdb->postmeta}.post_id
        END {$order}";

    return $orderby;
}, 10, 2 );

Notez que ceci est plutôt rigide, car lorsque vous modifiez votre requête, cela risque de ne pas fonctionner. Mais si tout va bien, vous pourrez l’adapter à vos besoins et même le rendre plus flexible.

2
birgire