web-dev-qa-db-fra.com

"Problèmes" lors du chargement de nombreuses images sur une seule page (modèle personnalisé)

J'ai une page qui charge beaucoup d'éléments en vedette dans une grille. Il y a jusqu'à 56 images en cours de chargement, et il y a un peu de retard. Voici le code tel qu'il fonctionne maintenant:

if( count( $postslist ) > 0 ) {
  foreach ($postslist as $post) : setup_postdata($post);
  ?> 
  <div class='oneCell'>
    <?php 
    $image = get_the_post_thumbnail($this_post->ID, 'full size'); 
    $imageSrc = substr($image, strpos($image, "src") + 5);
    $imageSrc = substr($imageSrc, 0, strPos($imageSrc, "\""));
    $finalImage = "<img class='lazy' src='/images/grey.png' data-original='";
    $finalImage .= $imageSrc . "' />";
    $lastImage = "<a href='";
    $lastImage .= catch_that_image();
    $lastImage .= "'>";
    $lastImage .= $finalImage;
    $lastImage .= "</a>";
    echo $lastImage;
    ?>
  </div>
  <?php 
  $currentCount = $currentCount + 1;
  endforeach;
}

Je comprends qu'il y a des problèmes avec le code, frapper la base de données une fois pour chaque image d'une boucle, ce qui cause la lenteur.

Quelqu'un peut-il m'aider avec une solution pour accélérer le chargement? Ma pensée est de tout prendre de la base de données en une fois et de l'afficher à partir de là, mais je n'ai pas encore trouvé comment gérer cela.

Merci!

Modifier:

Désolé, j'aurais dû l'expliquer un peu plus:

Get_the_post_thumbnail demande une image en taille réelle, car elle est ce dont elle a besoin (189x189). L'image sélectionnée dans l'article est cette image et sera toujours 189x189.

catch_that_image () ne saisit que la première (et la seule) image du corps du message à afficher lorsque l'utilisateur clique sur l'image sélectionnée (miniature) à l'aide d'une bibliothèque js (magnific-popup.js).

Edit 2:

Il semble que mon problème concerne la base de données. Les appels sont trop lents pour en faire un à la fois (car ils sont nombreux). Y a-t-il un moyen de tout récupérer en un seul appel, puis de le traiter à partir de là?

1
SlackerCoder

La réponse est un chargement rapide ou en cache ou les deux.

Eager loading

Jetez un coup d'œil à ce pseudo-code:

$ids = get_ids_from_a_db_table();
foreach ( $ids as $id ) {
  $row = get_row_from_foreign_table_using( $id );
  echo "Row title for the ID: $id is $row->title";
}

Si le nombre de $ids est n, ce code simple exécute des requêtes n+1, les premières à charger les identifiants, puis un pour chaque identifiant.

Votre code est encore pire, il lance 2 requêtes pour chaque identifiant, une pour obtenir un objet de pièce jointe, une pour obtenir une URL de pièce jointe (c'est WordPress, pas vous, vraiment)

Donc, vous lancez 2n + 1 requêtes ...

Le chargement rapide, appelé requête DB, est la façon dont vous résolvez le problème des requêtes n + 1 (ou 2n + 1) en interrogeant uniquement une db demander toutes les données dont vous avez besoin (normalement, utilisez JOIN + WHERE)

$data = get_data_from_joined_db_tables();
foreach ( $data as $row ) {
  echo "Row title for the ID: $row->id is $row->title";
}

Comment faire dans WordPress? Vous avez besoin de deux choses:

  • ajouter les clauses de jointure appropriées et where pour fusionner la table meta
  • filtrer les champs retournés par WP_Query pour inclure les champs de la table méta

Les deux peuvent être faits en utilisant des filtres de requête, ici une classe qui étend WP_Query et qui fait ça:

class Thumb_Query extends WP_Query {

  protected static $args = array( 'nopaging' => TRUE );

  public function __construct( $args = '' ) {
    if ( empty( $args ) ) $args = self::$args; // defaults
    parent::__construct( $args );
  }

  public function get_posts() {
    add_filter('posts_clauses', array( __CLASS__, 'thumb_filters') );
    $results = parent::get_posts();
    remove_filter('posts_clauses', array( __CLASS__, 'thumb_filters') );
    return $this->parse_images( $results );
  }

  public static function thumb_filters( $pieces ) {
    $meta = $GLOBALS['wpdb']->postmeta;
    $posts = $GLOBALS['wpdb']->posts;
    $pieces['fields'] .= ", thumbs.meta_value as thumb_id";
    $pieces['fields'] .= ", imgs.post_title as thumb_title";
    $pieces['fields'] .= ", imgdata.meta_value as thumb_file";
    $pieces['join'] .= "INNER JOIN {$meta} thumbs ON ({$posts}.ID = thumbs.post_id)";
    $pieces['join'] .= " INNER JOIN {$posts} imgs ON (thumbs.meta_value = imgs.ID)";
    $pieces['join'] .= " INNER JOIN {$meta} imgdata ON (imgs.ID = imgdata.post_id)";
    $where = " AND ( thumbs.meta_key = '_thumbnail_id' AND ";
    $where .= " CAST( thumbs.meta_value AS SIGNED ) > 0 )";
    $where .= " AND ( imgdata.meta_key = '_wp_attached_file' )";
    $pieces['where'] .= $where;
    $pieces['groupby'] = " {$posts}.ID";
    return $pieces;
  }

  protected function parse_images( $rows ) {
    $exts = array('jpg', 'jpeg', 'gif', 'png');
    foreach ( $rows as $i => $row ) {
       $urls = wp_extract_urls( $row->post_content );
       $img = FALSE;
       while ( ! $img && ! empty($urls) ) {
         $url = array_shift($urls);
         $ext = strtolower ( pathinfo( $url, PATHINFO_EXTENSION ) );
         if ( in_array( $ext, $exts ) ) $img = $url;
       }
       $rows[$i]->thumb_link = $img ? $img : '#';
    }
  }
}

Cette classe étend WP_Query, accepte donc les mêmes choses que son parent, mais ajoute des filtres pour changer l’id de post de chargement de colonne, le nom de post de colonne et le chemin du fichier de vignette.

Ainsi, si votre requête renvoie 50 publications, pour afficher des vignettes, vous exécutez 101 requêtes (2n + 1), votre classe n'exécute qu'une seule requête.

De plus, avant d'afficher les résultats, la classe analyse toutes les lignes, extrait la première URL de contenu de publication et supprime le filtre ajouté.

Comment utiliser

Utilisez les arguments standard WP_Query:

$args = array(
  'post_type' => 'portfolio',
  'category_name' => 'featured',
  'posts_per_page' => 50
);

Mais utilisez Thumb_Query au lieu de WP_Query

$portfolio = new Thumb_Query( $args );

Puis boucle et sortie:

if ( $portfolio->have_posts() ) {
  $u = wp_upload_dir();
  global $post;
  foreach( $portfolio->posts as $post ) { 
    setup_postdata( $post );
    $f = '<div class="oneCell"><a href="%s" title="%s">';
    $f .= '<img class="lazy" alt="%s" width="189" src="%s" data-original="%s"/>';
    $f .= '</a></div>';
    $title = esc_attr( get_the_title() );
    printf( $f,
      esc_url( $post->thumb_link ),
      $title, $title,
      esc_url( get_template_directory_uri() . '/images/grey.png'  ),
      esc_url( trailingslashit($u['baseurl']) . $post->thumb_file )
    );
  }
  wp_reset_postdata();
}

Dans la boucle, vous avez accès à toutes les propriétés de publication standard, mais chaque publication possède 4 propriétés supplémentaires:

  • $post->thumb_id l'identifiant de pièce jointe miniature
  • $post->thumb_title le titre de la pièce jointe miniature
  • $post->thumb_file le fichier de vignette relatif au dossier uploads, quelque chose comme '/2014/04/myfile.jpg'
  • $post->thumb_link l'URL complète de la première image du contenu de la publication ou '#' si aucune image n'a été trouvée
3
gmazzap

Il est assez difficile de comprendre ce que vous avez fait. L’une de mes préoccupations est d’utiliser get_the_post_thumbnail() et catch_that_image() ensemble dans le même code que vous avez utilisé. Je connais la fonction catch_that_image() et je l’ai déjà utilisée. C'est une fonction agréable à savoir qui est généralement utilisée par les codeurs pour récupérer la première image et l'afficher dans l'extrait. Cependant, il existe de meilleures façons d’obtenir le même résultat sans catch_that_image(). Je pense vraiment que cela ralentit votre site car il s’agit d’une fonction extérieure qui fonctionne séparément de la boucle, en particulier si vous devez récupérer 56 images. catch_that_image() doit être exécuté à chaque publication pour récupérer l'image. Elle est donc exécutée 56 fois. Corrigez-moi si je me trompe ici.

Je ne sais pas à quoi ressemble votre boucle, mais cela semble être un peu la pagaille. Je pense que la meilleure façon d'aller ici est d'utiliser WP_Query en conjonction avec get_posts pour construire votre boucle.

Dans WP_Query, il vous suffira de passer un argument pour que cela fonctionne, posts_per_page. Vous devrez définir ceci sur 1, sinon vous obtiendrez la même image plusieurs fois.

Suivant consiste à exécuter la boucle et à filtrer toutes les images attachées à une publication en utilisant post_type et post_mime_type. Wordpress enregistre toutes les pièces jointes sous forme de post type . Toutes les pièces jointes ne sont pas des images, il est donc nécessaire de spécifier que seules les pièces jointes qui sont des images doivent être renvoyées. Ceci est spécifié par post_mime_type .

wp_get_attachment_image sera utilisé pour renvoyer ces images à la taille spécifiée. Dans votre code, ce sera l’image en taille réelle.

Tout cela est fait et épousseté, la boucle complète ressemblera à quelque chose comme ça

<?php 
    $new_query = new WP_Query(array(
        'posts_per_page'   => 1,
    ));

        while ($new_query->have_posts()) : $new_query->the_post();

            $args = array (
                'post_type' => 'attachment',
                'numberposts' => 56,
                'status' => 'publish',
                'post_mime_type' => 'image',
                'parent' => $post->ID
            ); 

            $attachments = get_posts($args);

            if ( $attachments ) {
                foreach ( $attachments as $attachment ) {
                    echo '<div class="oneCell">';
                    echo '<a href="' . get_permalink( $post->ID ) . '">';
                    echo wp_get_attachment_image( $attachment->ID, 'full' );
                    echo '</a>';
                    echo '</div>';
                }
            };

        endwhile;

wp_reset_postdata();

?>   

Vous avez juste besoin de changer les arguments pour répondre à vos besoins exacts, mais j'espère que c'est une plate-forme de base dont vous avez besoin et sur laquelle vous pouvez travailler et que cela aide beaucoup avec la rapidité. Prendre plaisir.

Modifier N'oubliez pas de réinitialiser la requête à l'aide de wp_reset_postdata();

1
Pieter Goosen