Contenu principal
classe php w3p_image_from_object

WordPress : une classe php de galerie économe en requêtes

WordPress fournit un moyen de créer facilement des galeries de photos assez facilement, cependant nous avons parfois besoin d’aller plus loin en créant nos propres scripts de galerie. Aujourd’hui je vous propose donc une classe php qui vous aidera à construire votre galerie, et qui a le bon ton d’être très peu gourmande.

Si vous avez déjà tenté l’expérience, vous connaissez déjà certaines fonctions comme wp_get_attachment_image_src() ou wp_get_attachment_image(), très gourmandes en requêtes. Et pour cause, elles sont prévues pour une photo, pas pour utilisation « de groupe ».

Première idée sans optimisation

L’idée de base pour créer une galerie est de partir d’un ensemble d’IDs puis de boucler afin de créer chaque image, et nous garderons ce principe jusqu’à la fin.
La première idée serait donc de faire quelque chose comme ça : (version simplifiée)

01020304050607080910

$img_ids = array( 22, 45, 7, 31, 139);	// IDs des photos
echo '<div class="gallery">';
foreach ($img_ids as $img_id) {
	$big_src = reset( wp_get_attachment_image_src( $img_id, 'large' ));	// Source de l'image grand format pour une lightbox
	$img_title = esc_attr( trim( strip_tags( get_the_title($img_id) ) ) );	// Attribut title du lien
	echo '<a href="'.$big_src.'" class="gallery-item">';
	echo wp_get_attachment_image($img_id, 'gallery-thumb');
	echo '</a>';
}
echo '</div>';

L’idée n’est pas mauvaise en soit, mais comme je le disais, ces fonctions ne sont pas prévues pour cette utilisation, et le résultat est sans appel :
Page sans galerie : 46 requêtes,
Page comportant une galerie avec 15 images : 91 requêtes !!!
Oui vous avez bien lu, 91 requêtes, soit 3 requêtes par image oO

En fait le problème est simple quand on étudie ces fonctions. On leur fournit un ID, et la première chose qu’elles font c’est un get_post(), et parfois des get_post_meta() (pour avoir le alt de l’image par exemple). Même si l’objet créé par get_post() est disponible jusqu’à la restitution du code html, ce n’est pas une solution viable pour ce genre d’utilisation.

L’idée finale, la bonne

L’idée serait donc d’aller chercher ces attachments en une seule requête au début et d’éviter tous ces get_post() par la suite. Pour ça on est obligé de mettre les doigts dans le cambouis. C’est ce que j’ai fait en créant cette classe php. En somme, elle contient toutes les fonctions de WordPress qui servent à l’élaboration des images et qui nécessitent un get_post().
En voici la liste :
– image_downsize()
– get_image_tag()
– image_get_intermediate_size()
– wp_get_attachment_image_src()
– wp_get_attachment_image()
– wp_get_attachment_link()
– get_attached_file()
– wp_get_attachment_metadata()
– wp_get_attachment_url()
– wp_get_attachment_thumb_file()
– wp_get_attachment_thumb_url()
– wp_attachment_is_image()
– get_attachment_link()
Je les ai prises de 3 fichiers de WordPress et les ai incluses dans la classe. La différence sera qu’au lieu de leur fournir un ID d’attachment en argument, on leur fournira un objet.
Mon premier essai a été plutôt concluant puisqu’au lieu de 45 requêtes pour 15 images, je suis tombé à 15 + 1 pour la principale.
Ce n’était pas encore assez à mon goût, je suis donc allé plus loin. Dans ces fonctions on croise parfois des get_post_meta(), ce sont eux qui nous bloquent au final.
Idée suivante : inclure le contenu de ces metas dans la requête initiale grâce à des jointures pour éviter les get_post_meta() ultérieurement. Les fonctions vont donc d’abord vérifier si ces informations ne sont pas contenues dans l’objet avant d’aller chercher la meta.
Résultat : 0 requête par image + 1 requête principale (o/).

Utilisation

Exemple 1, requête principale classique.

01020304050607080910111213141516171819

include_once('class-w3p-image-from-object.php');	// Ma classe
$img_from_obj = new w3p_image_from_object();

global $wpdb;
$img_ids = array( 22, 45, 7, 31, 139);	// IDs des photos
$imgs = $wpdb->get_results("SELECT * FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%' AND (ID = ".implode(" OR ID = ", $img_ids).")", OBJECT_K);

echo '<div class="gallery">';
foreach ($img_ids as $img_id) {
	if ( !isset($imgs[$img_id]) )
		continue;
	$img = $imgs[$img_id];
	$big_src = reset( $img_from_obj->wp_get_attachment_image_src( $img, 'large' ));	// Source de l'image grand format pour une lightbox
	$img_title = esc_attr( trim( strip_tags( $img->post_title ) ) );		// Attribut title du lien
	echo '<a href="'.$big_src.'" class="gallery-item">';
	echo $img_from_obj->wp_get_attachment_image($img, 'gallery-thumb');
	echo '</a>';
}
echo '</div>';

Ici il s’agit donc d’une requête classique, nous n’avons pas inclus les metas dedans, il y aura donc 1 requête supplémentaire par image.
Détail important : le foreach boucle sur le tableau des IDs pour une bonne raison : si dans le tableau des IDs il y a des images en double, ces doublons n’apparaitront pas dans le résultat $imgs, il y aura donc des images en moins. D’où l’utilisation de OBJECT_K (qui met l’ID de l’attachment en clé de chaque valeur du tableau) et ce bouclage sur les IDs. La seconde raison c’est pour l’ordre des images, ainsi on garde l’ordre du tableau des IDs. Mais bon, ça c’est votre sauce ;)

Exemple 2, avec un helper.
Afin de faciliter la création de la requête initiale qui inclue les metas, et de garder l’ordre du tableau des IDs, j’ai ajouté un petit helper à la classe : (roulements de tambour)… get_results() (la foule en délire).

010203040506070809101112131415

include_once('class-w3p-image-from-object.php');	// Ma classe
$img_from_obj = new w3p_image_from_object();

$img_ids = array( 22, 45, 7, 31, 139);	// IDs des photos
$imgs = $img_from_obj->get_results($img_ids);

echo '<div class="gallery">';
foreach($imgs as $img) {
	$big_src = reset( $img_from_obj->wp_get_attachment_image_src( $img, 'large' ));	// Source de l'image grand format pour une lightbox
	$img_title = trim( strip_tags( $img->post_title ) );				// Attribut title du lien
	echo '<a href="'.$big_src.'" class="gallery-item">';
	echo $img_from_obj->wp_get_attachment_image($img, 'gallery-thumb');
	echo '</a>';
}
echo '</div>';

Trois arguments pour get_results( $ids, $output_type, $order ) :
– $ids (array) : tableau contenant les IDs des images,
– $output_type (constant) : OBJECT ou OBJECT_K seulement,
– $order (bool) : ordonne les résultats selon l’ordre du tableau d’IDs (et ajoute les doublons). $output_type doit être OBJECT_K.

En utilisant ce helper donc, la galerie ne génèrera qu’une seule requête au total.

Limitations

– Si le lien sur les images mène au post au lieu du fichier, il vous en coutera 1 requête supplémentaire par lien.
– Si la galerie contient des fichiers autres que des images, même punition (car une icône doit être affichée et que je n’ai pas voulu toucher à la fonction qui la génère).
Mais ces deux cas ne se présentent normalement pas lorsque l’on créé une galerie.

See ya!