web-dev-qa-db-fra.com

Utilisation de meta_query avec plusieurs clés et valeurs de comparaison

Je soupçonne que je pourrais demander la lune sur un bâton avec ceci, mais:

J'ai un filtre pour mon type de message personnalisé shows. Le filtre utilise les taxonomies standard attachées à shows, mais permet également à l'utilisateur de filtrer par lieu, qui est une méta-valeur pour chaque émission, ou par mois, une date de début étant associée à chaque émission.

Le problème que j'ai rencontré est que certaines émissions peuvent s'étendre sur plusieurs mois; alors, commencez la course en novembre 2014 et terminez en décembre. Pour cela, je stocke les méta-valeurs comme suit:

startdate   => 20141225
enddate     => 20141224

Idéalement, si quelqu'un filtre les émissions pour décembre 2014, je souhaite afficher les émissions qui commencent toutes les deux en décembre,ou/ se terminent avant la fin du mois de décembre. Le problème est que, parce que je vérifie également la venue méta_value avec relation => 'AND', je ne peux pas trouver un moyen de rechercher[pseudocode]WHERE venue == 'royal-playhouse' AND ((startdate BETWEEN array(20141201, 20141231)) OR (enddate BETWEEN array(20141201, 20141231))).

J'utilise un filtre pre_get_posts pour modifier les arguments $query passés, et il convient de noter qu'il existe quelques champs méta_value supplémentaires qui sont également utilisés avec le filtre (tels que le type d'affichage, l'accessibilité, etc.). Voici une version simplifiée de mon code (qui fonctionne actuellement parfaitement, mais vérifie uniquement par rapport à startdate):

function showDates_orderQuery($query) {
    if ($query->is_main_query() && !is_admin() && is_post_type_archive('showdates')) {

        $queryVenue = $query->query_vars['venue'];
        $queryMonth = $query->query_vars['month'];

        $metaQuery = array();

        //  Venue meta

        if ($queryVenue) {
            $metaQuery[] = array(
                'key'       => 'venue',
                'value'     => array($queryVenue),
                'compare'   => 'IN',
            );
        }

        //  Month meta

        if ($queryMonth) {

            $monthEpoch = strtotime($queryMonth);
            $formattedQueryMonth = date('Ymd', $monthEpoch);
            $formattedEndOfQueryMonth = date('Ymt', $monthEpoch);

            $metaQuery['relation'] = 'AND'; //  Set because we want to check for shows from a venue AND by the given month

            $metaQuery[] = array(
                'key'       => 'startdate',
                'value'     => array($formattedQueryMonth, $formattedEndOfQueryMonth),
                'compare'   => 'BETWEEN',
                'type'      => 'DATE'
            );
        }

        if ($metaQuery) {
            $query->set('meta_query', $metaQuery);
        }

        $query->set('meta_key', 'startdate');
        $query->set('orderby', 'meta_value');
        $query->set('order', 'ASC');
    }
}

add_action('pre_get_posts', 'showDates_orderQuery');

Toute idée sur la manière dont je pourrais y parvenir serait reçue avec gratitude.

1
indextwo

Parce que je suis un idiot complet, il semble que j’ai complètement oublié que je ai fondamentalement répondu à ma propre question il ya plus d’un an .

J'avais du mal à obtenir le comportement du filtre posts_join (j'ai généralement des problèmes avec INNER JOINs de toute façon) et j'ai donc plutôt consulté le filtre posts_where. Le principal problème avec cette méthode est que vous ne pouvez pas lui transmettre d’arguments autres que la condition $where existante; et dans ce cas, je voulais utiliser des variables non globales.

La solution consistait à utiliser une fonction Lambda anonyme en ligne pour le filtre posts_where:

if ($queryMonth) {

    $monthEpoch = strtotime($queryMonth);
    $formattedQueryMonth = date('Ymd', $monthEpoch);
    $formattedEndOfQueryMonth = date('Ymt', $monthEpoch);

    $monthOptions = array(
        'monthstart' => $formattedQueryMonth,
        'monthend' => $formattedEndOfQueryMonth,
    );

    $monthFilter = function ($where = '') use ($monthOptions) {
        global $wpdb;
        $where .= " AND (($wpdb->postmeta.meta_key = 'startdate' AND $wpdb->postmeta.meta_value >= '" . $monthOptions['monthstart'] . "') AND ($wpdb->postmeta.meta_key = 'startdate' AND $wpdb->postmeta.meta_value <= '" . $monthOptions['monthend'] . "')) ";
        $where .= " OR (($wpdb->postmeta.meta_key = 'enddate' AND $wpdb->postmeta.meta_value >= '" . $monthOptions['monthstart'] . "') AND ($wpdb->postmeta.meta_key = 'enddate' AND $wpdb->postmeta.meta_value <= '" . $monthOptions['monthend'] . "')) ";
        return $where;
    };

    add_filter('posts_where', $monthFilter);
}

Avec un peu de chance, cette méthode devrait en réalité devenir complètement superflue dans les prochaines semaines, grâce à la prochaine version de 4.1 -, grâce à @jmusal pour le heads up .

0
indextwo

En fonction de votre calendrier ou de votre volonté de vivre sur Edge, certaines améliorations majeures vont être introduites dans la version 4.1 qui, à mon avis, vous permettront de faire exactement ce que vous souhaitez faire. Si vous voulez vivre un peu sur Edge, vous pouvez développer avec la version bêta 4.1 et passer à la version au moment de son lancement (assurez-vous de déposer des rapports de bogues si vous les trouvez :))

Voici un lien vers le message make qui va un peu plus en détail: https://make.wordpress.org/core/2014/10/20/update-on-query-improvements-in-4-1/

1
jmusal

Vous ne pouvez pas trouver un moyen de le faire car il n'y en a pas. Il n'est pas possible de mélanger des ET/OU comme cela avec meta_query. Le système de requête est génial, mais pas inclusif. Vous devrez utiliser plusieurs requêtes ou filtrer le SQL vous-même.

0
Otto