web-dev-qa-db-fra.com

Obtenir la position d'un article dans une catégorie

J'ai un identifiant de poste x, un identifiant de catégorie y. Je veux récupérer la position à laquelle le message apparaîtra dans la catégorie.

  • Il est confirmé que le poste appartient à la catégorie
  • La post-commande sera basée sur une valeur numérique définie dans post-méta, la clé méta 'facebook_shares'
  • Catégorie ont plus que 1k autres messages.

Je recherche une meilleure solution qui consomme moins d’utilisation des requêtes de base de données et une requête unique sera la meilleure.

Merci d'avance.

3
Shazzad

Si je comprends bien, vous devez récupérer tous les messages de la catégorie 'y' ainsi que la méta-clé 'facebook_shares'. Après cela, vous devez connaître la position d'un message spécifique.

Ma proposition est:

  1. Créez une classe qui étend WP_Query et filtrez les résultats pour la catégorie et le champ méta requis. Cette classe doit également filtrer la requête SQL pour définir la méta-valeur en tant que propriété des objets post renvoyés par WP_Query::get_posts.
  2. un Spl ArrayIterator Spl pour parcourir tous les messages et mettre chaque message dans un
  3. SplHeap qui implémente la méthode compare en examinant les propriétés méta des objets post. SplHeap a inséré la propriété des éléments de commande automatique passés.
  4. une troisième classe qui utilise les 2 précédentes par composition, pour obtenir l'ordre de post recherché.

Et une seule requête de base de données est effectuée.

Codons.

Commencez par écrire notre implémentation SplHeap:

class SharesHeap extends SplHeap {  
  public function compare( $post1, $post2 ) {
    $key = FB_Shares_Query::$key;
    if ( ! $post1 instanceof WP_Post || ! isset( $post1->$key ) ) return 0;
    if ( ! $post2 instanceof WP_Post || ! isset( $post2->$key ) ) return 0;
    return (int)$post2->$key - (int) $post1->$key;
  }
}

Maintenant, la classe qui étend WP_Query pour obtenir les posts. Les constructeurs de cette classe acceptent un terme de catégorie pouvant être transmis en tant qu'identificateur de catégorie, un slug de catégorie ou même un objet de catégorie. Par défaut, l'argument 'post_type' est défini sur 'post' , mais vous pouvez le remplacer en transmettant un deuxième argument au constructeur.

class FB_Shares_Query extends WP_Query {

  public static $key = 'facebook_shares'; // change the metakey here, if needed
  protected static $args = array( 'nopaging' => TRUE ); // get all posts

  public function __construct( $cat = NULL, $post_type = 'post' ) {
    if ( is_object($cat) && isset($cat->term_id) ) $cat = $cat->term_id;
    $cat_field = is_numeric($cat) ? 'cat' : 'category_name';
    self::$args['post_type'] = ! empty( $post_type ) ? $post_type : 'post';
    self::$args[$cat_field] = $cat;
    parent::__construct( self::$args );
  }

  public function get_posts() {
    add_filter('posts_clauses', array(__CLASS__, 'my_filters') );
    $results = parent::get_posts();
    remove_filter('posts_clauses', array(__CLASS__, 'my_filters') );
    return $results;
  }

  public static function my_filters( $pieces ) {
    $key = self::$key;
    $meta = $GLOBALS['wpdb']->postmeta;
    $posts = $GLOBALS['wpdb']->posts;
    $pieces['fields'] .= ", {$meta}.meta_value as {$key}";
    $pieces['join'] .= "INNER JOIN {$meta} ON ({$posts}.ID = {$meta}.post_id)";
    $where = " AND ( {$meta}.meta_key = '{$key}' AND ";
    $where .= " CAST( {$meta}.meta_value AS SIGNED ) > 0 )";
    $pieces['where'] .= $where;
    return $pieces;
  }
}

Puis la classe qui "colle" précédente:

class FB_Shares_Order {

  protected $heap;
  protected $posts;

  public function __construct( SplHeap $heap )  {
    $this->heap =  $heap;
  }

  public function getOrder( WP_Post $post = NULL ) {
    if ( ! $this->posts instanceof ArrayIterator ) return -1;
    if ( ! isset( $post->ID ) ) return FALSE; 
    while( $this->posts->valid() ) {
      $this->heap->insert( $this->posts->current() );
      $this->posts->next();
    }
    $position = 0;
    while( $this->heap->valid() ) {
      $current = $this->heap->extract();
      if ( $current->ID == $post->ID ) return $position + 1;
      $position++;
    }
    return FALSE;
  }

  public function getPosts( WP_Query $query ) {
    if ( $query->post_count > 0) $this->posts = new ArrayIterator( $query->posts );
  }
}

Enfin, une fonction qui fonctionne comme façade pour les 3 classes. Il accepte un post (id ou object) et une catégorie (id, slug ou object):

function getFB_Shares_Order( $post, $cat ) {
  if ( is_object($post) && isset( $post->ID ) ) $post = $post->ID;
  if ( ! is_numeric($post) || ! (int) $post > 0 ) return FALSE;
  if ( ! has_category($cat, $post) ) return FALSE;
  $order = new FB_Shares_Order( new SharesHeap );
  $order->getPosts( new FB_Shares_Query( $cat ) );
  return $order->getOrder( get_post( $post ) );
}

Exemple d'utilisation

global $post;
$position = getFB_Shares_Order( $post, 'uncategorized' );

Valeurs renvoyées:

Si la requête renvoie des publications et que la publication donnée est l'une d'entre elles, la fonction renvoie l'ordre de publication dans la collection de publications en regardant la clé méta 'facebook_shares'.

PAR EXEMPLE. la requête retourne 3 posts, avec les valeurs de 'facebook_shares' respectivement: 10, 15, 30 et vous passez à fonctionner l'identifiant de post qui a 'facebook_shares' = 15, puis la fonction return 2 , car le poste est le deuxième en ordre.

Si la requête ne renvoie aucun message, la fonction renvoie - 1 .

Si la requête renvoie des publications, mais que les publications recherchées ne figurent pas parmi elles, la fonction renvoie false .

Mises en garde

Certains problèmes sur WP_Query de base utilisent meta_query sans valeurs spécifiques ('compare' = EXIST ou NOT EXIST), spécialement si la requête contient également une requête fiscale. Pour les éviter, dans la classe qui étend la requête WP, utilisez des filtres sur la requête SQL et la clause where est définie "manuellement". Toutefois, s'il existe du code externe (plug-in, thèmes) ajoutant des filtres personnalisés à la requête (à l'aide de posts_where, de posts_join et de crochets de filtre similaires), ce code peut rompre le comportement de l'appel.

Le code est très rapidement testé et semble fonctionner, cependant .. aucune garantie.

6
gmazzap