web-dev-qa-db-fra.com

Comment créer ma propre méta_query imbriquée à l'aide de posts_where/posts_join?

Certains de mes messages (pas tous) ont un prix comme méta clé/valeur. Aujourd'hui, j'utilise l'action pre_get_posts afin que mes utilisateurs puissent rechercher des prix compris entre une certaine valeur.

C'est le code que j'utilise aujourd'hui et ça marche.

add_action('pre_get_posts', 'my_search_price');
function my_search_price( $query ) {
    if ($query->get('maxprice') != "" && $query->get('minprice') != "" && $query->is_main_query()) {

        $maxprice = intval($query->get('maxprice'));
        $minprice = intval($query->get('minprice'));

        $meta = array();
        $meta[] = 
        array (
            'key' => 'price',
            'value' => $maxprice,
            'compare' => '<=',
            'type' => 'numeric'
        );

        $meta[] = 
        array (
            'key' => 'price',
            'value' => $minprice,
            'compare' => '>=',
            'type' => 'numeric'
        );

        $meta[] = 
        array (
            'key' => 'price_updated',
            'value' => time()-(60*60*24*14),
            'compare' => '>',
            'type' => 'numeric'
        );

        $query->set('meta_query', $meta);
    }
}

Mon problème est que certains des articles ont maintenant un deuxième prix, également stocké en tant que méta clé/valeur. Le nom du nouveau prix est "price_used" et ce prix a sa propre clé/valeur mise à jour nommée "price_used_updated".

Je voudrais modifier la fonction my_search_price() afin qu’elle gère également le nouveau prix. Le résultat devrait être des messages où "price" OR "price_used" est compris entre minprice et maxprice. "price_updated"/"price_used_updated" doit être âgé de 14 jours maximum.

D'après ce que j'ai compris en lisant d'autres articles, meta_query ne peut pas être imbriqué. J'ai donc besoin de modifier le code SQL à la place en utilisant le filtre posts_join ou/et posts_where.

Quelqu'un peut-il s'il vous plaît me diriger dans la bonne direction à la solution s'il vous plaît? J'imagine que je ne suis pas le premier à avoir besoin d'une méta_query plus avancée. Mais je n'ai jamais modifié la requête SQL auparavant.

Ce que j'ai fait est la lecture de ces pages:

UPDATE: La requête de résultat devrait ressembler à ceci:

SELECT SQL_CALC_FOUND_ROWS wp_posts.*, mtGP2.meta_value, mtAM2.meta_value
FROM wp_posts

INNER JOIN wp_postmeta AS mtGP1 ON (wp_posts.ID = mtGP1.post_id)
INNER JOIN wp_postmeta AS mtGP2 ON (wp_posts.ID = mtGP2.post_id)

INNER JOIN wp_postmeta AS mtAM1 ON (wp_posts.ID = mtAM1.post_id)
INNER JOIN wp_postmeta AS mtAM2 ON (wp_posts.ID = mtAM2.post_id)

WHERE 1=1

AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'

AND
(
(mtGP1.meta_key = 'price' AND CAST(mtGP1.meta_value AS SIGNED) BETWEEN 1 AND 10
AND mtGP2.meta_key = 'price_updated' AND CAST(mtGP2.meta_value AS SIGNED) > NOW()-60*60*24*14)
OR
(mtAM1.meta_key = 'price_used' AND CAST(mtAM1.meta_value AS SIGNED) BETWEEN 1 AND 10
AND mtAM2.meta_key = 'price_used_updated' AND CAST(mtAM2.meta_value AS SIGNED) > NOW()-60*60*24*14)
)
GROUP BY wp_posts.ID

Mais cette requête a deux problèmes.

1) C'est très lent

2) J'ai toujours un problème, car je ne sais pas comment implémenter la requête dans wordpress. Je pense que je dois utiliser les filtres post_where et posts_join, mais je ne sais pas comment.

UPDATE 2. J'ai réécrit la requête pour qu'elle ne soit plus lente. Mais je ne sais toujours pas comment utiliser les filtres ou les actions (comme dans ma fonction originale my_search_price ()) pour implémenter la requête dans wordpress.

SELECT SQL_CALC_FOUND_ROWS *
  FROM
(
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta AS mtGP1 ON (wp_posts.ID = mtGP1.post_id)
INNER JOIN wp_postmeta AS mtGP2 ON (wp_posts.ID = mtGP2.post_id)
WHERE 1=1
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'
AND mtGP1.meta_key = 'price'
AND CAST(mtGP1.meta_value AS SIGNED) BETWEEN 1 AND 10
AND mtGP2.meta_key = 'app_updated'
AND CAST(mtGP2.meta_value AS SIGNED) > UNIX_TIMESTAMP()-60*60*24*14
GROUP BY wp_posts.ID

UNION

SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta AS mtGP1 ON (wp_posts.ID = mtGP1.post_id)
INNER JOIN wp_postmeta AS mtGP2 ON (wp_posts.ID = mtGP2.post_id)
WHERE 1=1
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'
AND mtGP1.meta_key = 'price_used'
AND CAST(mtGP1.meta_value AS SIGNED) BETWEEN 1 AND 10
AND mtGP2.meta_key = 'price_used_updated'
AND CAST(mtGP2.meta_value AS SIGNED) > UNIX_TIMESTAMP()-60*60*24*14
GROUP BY wp_posts.ID
) AS t
2
PappApp

Voici un aperçu de la manière dont vous voulez utiliser une UNION avec WP_Query.

add_filter(
  'posts_request',
  function ($clauses) {

    $clauses = str_replace('SQL_CALC_FOUND_ROWS','',$clauses,$scfr);

    $scfr = (0 < $scfr) : 'SQL_CALC_FOUND_ROWS' : '';

    $clause2 = $clauses; // manipulate this

    return "SELECT {$scfr} u.* FROM (({$clauses}) UNION ({$clause2})) as u";
  },
  1,
  2
);

Configurez vos arguments WP_Query pour générer la première moitié de UNION puis manipulez-le pour créer la requête entière. Quelque chose comme ça devrait être proche.

add_action('pre_get_posts', 'my_search_price');
function my_search_price( $query ) {
    if ($query->get('maxprice') != "" && $query->get('minprice') != "" && $query->is_main_query()) {

        $maxprice = intval($query->get('maxprice'));
        $minprice = intval($query->get('minprice'));

        $meta = array();

        $meta[] =
          array (
              'key' => 'price',
              'value' => array($maxprice,$minprice),
              'compare' => 'between',
          );

        $meta[] = 
          array (
              'key' => 'app_updated',
              'value' => time()-(60*60*24*14),
              'compare' => '>',
              'type' => 'numeric'
          );

        $query->set('meta_query', $meta);
    }
}

add_filter(
  'posts_request',
  function ($clauses) {

    $clauses = str_replace('SQL_CALC_FOUND_ROWS','',$clauses,$scfr);

    $scfr = (0 < $scfr) ? 'SQL_CALC_FOUND_ROWS' : '';

    $clause2 = str_replace('price','price_used',$clauses); // manipulate this
    $clause2 = str_replace('app_updated','price_used_updated',$clauses);

    return "SELECT {$scfr} u.* FROM (({$clauses}) UNION ({$clause2})) as u";
  },
  1,
  2
);

Vous devrez ajouter une logique au filtre post_requests afin qu’il ne s’exécute pas lorsque vous ne le souhaitez pas.

2
s_ha_dum