web-dev-qa-db-fra.com

pre_get_posts filtre meta_query sans conflit méta_query existant

J'ai une méta_key sur toutes les publications conçue pour vérifier le rôle d'utilisateur actuel et ne la renvoyer que si l'utilisateur a un rôle d'utilisateur acceptable. Le problème que je rencontre est que ma méta_query déclarée dans pre_get_posts entre en conflit avec des méta-requêtes existantes que j'ai configurées sur tout le site. Est-ce que je fais quelque chose de mal, n'est-ce pas possible ou existe-t-il une solution alternative à ma requête?

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

    // dont filter admin posts
    if(is_admin()) return $query;

    // get required globals
    global $current_user;

    // create our not in array
    $not_in = array (
        'public'    => array ('media', 'sponsor', 'super'),
        'media'     => array ('sponsor', 'super'),
        'sponsor'   => array ('super'),
        'super'     => array (),
    );

    // determine the users role
    $role = $current_user->roles;
    $role = array_key_exists(0, $role) ? $role[0] : 'public';
    if(current_user_can('edit_posts')) $role = 'super';

    // determine the users post visibility
    switch($role) {
        case 'media':
        case 'sponsor':
            $posts_visibility = $role;
            break;

        case 'super':
        case 'editor':
        case 'administrator':
            $posts_visibility = 'super';
            break;

        default:
            $posts_visibility = 'public';
            break;
    }

    // get existing meta query
    $meta_query = $query->get('meta_query');

    // add our new meta_query data
    $meta_query[] = array (
        'relation'  => 'OR',
        array (
            'key'       => 'restricted_visibility',
            'value'     => $not_in[$posts_visibility],
            'compare'   => 'NOT IN',
        ),
        array (
            'key'       => 'restricted_visibility',
            'compare'   => 'NOT EXISTS',
        ),
    );


    // update to our new meta query
    $query->set('meta_query', $meta_query);

    // return our query
    return $query;

});

Maintenant, je sais que la méta-requête fonctionne toute seule, mais elle semble entrer en conflit avec d'autres méta-requêtes ou ne pas fonctionner du tout lorsque vous essayez de combiner des requêtes existantes et nouvelles.

OK, donc après avoir appris que je ne peux pas mélanger les conditions ET/OU, j'ai changé pour utiliser le filtre posts_where. Le code est exactement le même que ci-dessus à droite jusqu'au point où j'ai défini la méta requête. Alors j'utilise ceci -

add_filter('posts_where', function ($where) {

    // dont filter admin posts
        if(is_admin()) return $where;

    // get required globals
    global $current_user;

    // create our not in array
    $not_in = array (
        'public'    => array ('media', 'sponsor', 'super'),
        'media'     => array ('sponsor', 'super'),
        'sponsor'   => array ('super'),
        'super'     => array (),
    );

    // determine the users role
    $role = $current_user->roles;
    $role = array_key_exists(0, $role) ? $role[0] : 'public';
    if(current_user_can('edit_posts')) $role = 'super';

    // determine the users post visibility
    switch($role) {
        case 'media':
        case 'sponsor':
            $posts_visibility = $role;
            break;

        case 'super':
        case 'editor':
        case 'administrator':
            $posts_visibility = 'super';
            break;

        default:
            $posts_visibility = 'public';
            break;
    }

    if(count($not_in[$posts_visibility]) > 0) {
        // join postmeta so we can query it
        $this->filter('posts_join', function ($join) {

            // get our global
            global $wpdb;

            // create our join and return it
            $join .= sprintf(' LEFT JOIN %1$s ON %2$s.ID = %1$s.post_id ', $wpdb->postmeta, $wpdb->posts);

            return $join;
        });

        // build our query
        $where .= sprintf(
            ' AND (( NOT EXISTS (SELECT * FROM %1$s WHERE (%1$s.post_id = %2$s.ID) AND %1$s.meta_key = "restricted_visibility")' .
            ' OR ( %1$s.meta_key = "restricted_visibility" AND %1$s.meta_value NOT IN (%3$s) ))) GROUP BY %2$s.ID',
            $wpdb->postmeta,
            $wpdb->posts,
            '\'' . implode('\', \'', $not_in[$posts_visibility]) . '\''
        );
    }

    // return our query
    return $where;
});

Le problème que je rencontre avec l'extrait ci-dessus est qu'il semble causer beaucoup de problèmes inattendus. Les requêtes prennent beaucoup de temps et de nombreux autres problèmes inattendus. J'ai dû rejoindre la table postmeta pour pouvoir l'interroger.

J'attends vos pensées avec impatience, Chris.

1
Chris

D'ACCORD,

Après beaucoup d'essais et d'erreurs et de questions autour de moi, j'ai finalement réussi à trouver une solution efficace. J'ai pensé le poster ici pour ceux qui essaient de résoudre un problème similaire à l'avenir.

Filtre 1: posts_join crée une jointure gauche sur toutes les requêtes pour lier la table postmeta. Il est important de noter que j'ai créé la table postmeta avec un alias de cpm1 car il s'agit de la liaison qui fait que tout fonctionne plus tard. Sinon, notre requête personnalisée sera toujours en conflit avec d'autres méta-requêtes.

// create our posts join filter
add_filter('posts_join', function ($join) {

    // get our global
    global $wpdb;

    // create our join complete with alias and return it
    return $join . sprintf(" LEFT JOIN %1\$s AS cpm1 ON (%2\$s.ID = cpm1.post_id AND cpm1.meta_key = 'META_KEY_HERE') ", $wpdb->postmeta, $wpdb->posts);

});

Maintenant que nous avons la jointure, lors de la recherche de publications, nous pouvons ajouter la partie personnalisée de notre requête. Notez que la requête fait référence à notre table jointe à gauche (cpm1) plutôt que de référencer directement la table postmeta.

// create our posts_where filter to make use of our join and exclude certain posts
add_filter('posts_where', function ($where) {

    // dont filter admin posts
    if(is_admin()) return $where;

    // get required globals
    global
        $current_user;

    // create our not in array
    $not_in = array (
        'public'    => array ('media', 'sponsor', 'super'),
        'media'     => array ('sponsor', 'super'),
        'sponsor'   => array ('super'),
        'super'     => array (),
    );

    // determine the users role
    $role = $current_user->roles;
    $role = array_key_exists(0, $role) ? $role[0] : 'public';
    if(current_user_can('edit_posts')) $role = 'super';


    // determine the users post visibility
    switch($role) {
        case 'media':
        case 'sponsor':
            $posts_visibility = $role;
            break;

        case 'super':
        case 'editor':
        case 'administrator':
            $posts_visibility = 'super';
            break;

        default:
            $posts_visibility = 'public';
            break;
    }

    // only apply our custom query if we have legitimate conditions to be met
    if(count($not_in[$posts_visibility]) > 0) {

        // build our query
        $where .= sprintf(
            " AND (( cpm1.meta_key = 'META_KEY_HERE' AND cpm1.meta_value NOT IN (%1\$s)) OR cpm1.meta_id IS NULL ) ",
            "'" . implode("', '", $not_in[$posts_visibility]) . "'"
        );
    }

    // return our query
    return $where;

});
0
Chris