24 Août 2020 Par Cédric Moris Kelly
Woocommerce
Nous avons besoin de modifier trois templates dans le répertoire de notre thème pour créer notre grille de produits sur mesure :
woocommerce/content-product.php
woocommerce/loops/loop-start.php
woocommerce/loop/loop-end.php
Le modèle par défaut d’item de grille de produits dans Woocommerce est fourni par le template content-product.php :
wp-content/plugins/woocommerce/templates/content-product.php
Il intègre le bouton de mise au panier et un lien vers la page produit sur l’image du produit.
Vous pouvez le modifier en l’overriddant avec un fichier du mème nom dans le répertoire de votre thème selon le chemin de dossiers suivant :
wp-content/themes/votre-theme/woocommerce/content-product.php
C’est ce fichier que vous modifierez pour créer votre propre item de grille de produits.
Ce modèle content-product.php fournit le template pour un seul produit de la grille. Il ne fournit pas le conteneur de la grille de produits.
Le conteneur / wrapper de la grille de produits de Woocommerce est affiché par les fonctions :
woocommerce_product_loop_start(); /* et */ woocommerce_product_loop_end();
qui appellent respectivement les templates :
woocommerce/templates/loops/loop-start.php /* et */ woocommerce/templates/loop/loop-end.php
Allez voir dans le répertoire « templates » de Woocommerce. Ces deux fonctions sont utilisées dans les modèles suivants :
archive-product.php, single-product/up-sells.php, single-product/related.php, cart/cross-sells.php
Le wrapper de la grille de produits par défaut nous fournit la classe CSS .products ainsi qu’une classe générée par l’option dans l’administration de Woocommerce qui règle le nombre de colonnes pour la grille de produits.
Il en va de même pour tous les fichiers de template Woocommerce que vous souhaiteriez modifier. Reprenez le chemin à partir de :
wp-content/plugins/woocommerce/templates/
et créez des fichiers ou dossier du même nom dans le répertoire « woocommerce » de votre thème situé sur le chemin :
wp-content/themes/votre-theme/woocommerce/
On appelle cela « overrider » un template.
Si vous souhaitez modifier le conteneur de grille de produits de Woocommerce, vous pouvez overrider les templates :
woocommerce/templates/loop/loop-start.php
woocommerce/templates/loop/loop-end.php
Toujours en les copiant dans le répertoire woocommerce de votre thème.
Pour intégrer une grille responsive en HTML, l’utilisation d’un Framework aussi pratique que Bootsrap est une étape presque incontournable pour une intégration mobile facile.
Nous allons inclure la version 4.4.1 de la librairie CSS dans notre thème WordPress en ajoutant le code suivant au fichier functions.php à la racine de notre thème :
add_action( 'wp_enqueue_scripts', 'afg_enqueue_styles' );
function afg_enqueue_styles() {
wp_enqueue_style( 'bootstrap', '//stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css',array(), '4.4.1', 'all');
wp_enqueue_style( 'theme-style', get_template_directory_uri() . '/style.css', array('bootstrap'), '1', false );
}
Dans l’idéal, si vous avez la maîtrise sur l’inclusion de la feuille de style principale de votre thème (votre-theme/style.css), vous devrez indiquer à la fonction wp_enqueue_style() que Bootsrap est une dépendance. Cela vous permettra de reprendre le contrôle sur les styles de Bootstrap si nécessaire.
Maintenant que Bootsrap est disponible nous allons modifier le conteneur de grille de produits dans woocommerce/loop/loop-start.php
<?php
/**
* Product Loop Start
*
* This template can be overridden by copying it to yourtheme/woocommerce/loop/loop-start.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 3.3.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<ul class="products columns-<?php echo esc_attr( wc_get_loop_prop( 'columns' ) ); ?>">
La partie HTLM nous intéresse.
<ul class="products columns-<?php echo esc_attr( wc_get_loop_prop( 'columns' ) ); ?>">
Nous la remplaçons par :
<div class="row products">
columns-<?php echo esc_attr( wc_get_loop_prop( 'columns' ) );
Modifiez également le template woocommerce/loop/loop-end.php en changeant la balise HTML </ul> fermante par une balise </div> fermante.
<?php
/**
* Product Loop End
*
* This template can be overridden by copying it to yourtheme/woocommerce/loop/loop-end.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 2.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<!--</ul>-->
</div>
Dans votre thème, créez et éditer le fichier :
woocommerce/content-product.php
Voici l’original dans le plugin Woocommerce :
<?php
/**
* The template for displaying product content within loops
*
* This template can be overridden by copying it to yourtheme/woocommerce/content-product.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 3.6.0
*/
defined( 'ABSPATH' ) || exit;
global $product;
// Ensure visibility.
if ( empty( $product ) || ! $product->is_visible() ) {
return;
}
?>
<li <?php wc_product_class( '', $product ); ?>>
<?php
/**
* Hook: woocommerce_before_shop_loop_item.
*
* @hooked woocommerce_template_loop_product_link_open - 10
*/
do_action( 'woocommerce_before_shop_loop_item' );
/**
* Hook: woocommerce_before_shop_loop_item_title.
*
* @hooked woocommerce_show_product_loop_sale_flash - 10
* @hooked woocommerce_template_loop_product_thumbnail - 10
*/
do_action( 'woocommerce_before_shop_loop_item_title' );
/**
* Hook: woocommerce_shop_loop_item_title.
*
* @hooked woocommerce_template_loop_product_title - 10
*/
do_action( 'woocommerce_shop_loop_item_title' );
/**
* Hook: woocommerce_after_shop_loop_item_title.
*
* @hooked woocommerce_template_loop_rating - 5
* @hooked woocommerce_template_loop_price - 10
*/
do_action( 'woocommerce_after_shop_loop_item_title' );
/**
* Hook: woocommerce_after_shop_loop_item.
*
* @hooked woocommerce_template_loop_product_link_close - 5
* @hooked woocommerce_template_loop_add_to_cart - 10
*/
do_action( 'woocommerce_after_shop_loop_item' );
?>
</li>
Ce modèle est un peu déstabilisant à première vue car entièrement composé d’exécutions de Hooks.
Lire « Qu’est-ce qu’un hook ? »
Les commentaires sont très claires sur la manière d’utiliser chaque hook et sur les fonctions déjà connectées à chaque hook.
Pour filtrer le html généré par des fonctions telle que la fonction woocommerce_template_loop_product_title() ou l’overrider quand le html est placé dans un template en dure comme woocommerce/loop/rating.php ou woocommerce/loop/price.php
À mon sens la maintenance du code devient vite problématique avec cette conception à la fois factorisée et inconsistante de Woocommerce :
Factorisée car tout est découpé en petits morceaux réutilisables ou filtrables
Inconstistante car ces petits morceaux sont parfois des fonctions, parfois des templates.
Le fait d’écrire des modèles HTML à la fois dans des hooks et des fichiers dispersés rend le code de votre thème rapidement dure à maintenir. Je conseille d’ailleurs de regrouper les hooks woocommerce dans une Classe PHP de votre thème. Plus globalement, utilisez la POO pour structurer le code de votre thème.
C’est hooks sont donc conseillés quand vous faites des modifications ponctuelles. Si vous souhaitez créer une grille de produit entièrement sur mesure, préférez le choix 2.
Vous pouvez aussi faire le choix d’ignorer les plugins qui pourraient se connecter sur ces hooks, le but étant d’avoir sa propre grille de produits et donc d’avoir la main sur toutes les fonctionnalités futures que vous souhaiteriez ajouter.
Créons une classe PHP appelée afg_productGrid qui nous servira à regrouper les fonctions qui généreront notre grille de produit. Notez que les fonctions sont appelées méthodes en POO dans le contexte d’une classe.
<?php
if( ! defined( 'ABSPATH' ) ) exit;//If WP Constant ABSPATH doesn't exist, exit the script
/**
* We create a PHP class called 'Main' which contains all our templates parts
*/
if ( ! class_exists('afg_productGrid') ) {
class afg_productGrid {
public function getGridProduct() {
global $product;
$class='col-lg-3 col-md-6 afg-grid-product';
$rating = $product->get_average_rating();
$rating_count = $product->get_rating_count();
$id = $product->get_id();
$html = '';
$html .= '<div '.wc_product_class( $class, $product ).' id="post-'.$id.'">';// 'wc_product_class()' mixes and returns the CSS Classes we set in $class with Woocommerce classes.
$html .= '<figure role="group">';
$html .= $this->getProductGallery();
$html .= '</figure>';
$html .= '<div class="afg-text">';
$html .= '<div class="afg-price">';
$html .= $this->getPriceAndSale();
$html .= '</div>';
$html .= '<h2><a href="'.get_the_permalink().'" title="'.get_the_title().'">'.get_the_title().'</a></h2>';
ob_start();
woocommerce_template_single_add_to_cart();//USE ETHEIR THIS FOR FULL ADD TO CART FORM
/*OR*/
woocommerce_template_loop_add_to_cart();//THIS FOR A SIMPLE ADD TO CART BUTTON
$html .= ob_get_contents();
ob_end_clean();
$html .= '<div>';
$html .= $rating_count > 0 ? $this->getStarRatingHtml($rating) : '';
$html .= $this->getProductAttribute();
$html .= $this->getStockStatus();
$html .= '</div>';
$html .= '</div>';
$html .= '</div>';
return $html;
}
// We will show this methods in later chapters
public function getProductGallery(){}
public function getPriceAndSale(){}
public function getStarRatingHtml(){}
public function getProductAttribute(){}
public function getStockStatus(){}
}//end Class
}//endif Class exists
Nous enregistrons notre classe afg_ProductGrid dans le fichier :
/mon-theme/inc/class.productGrid.php
(Vous pourriez faire de même avec un plugin en créant le fichier /mon-plugin/inc/class.productGrid.php)
Nous pouvons charger notre classe en plaçant le hook suivant dans notre fichier :
/mon-theme/functions.php
add_action( 'init', 'afg_loadThemeIncludes' );
function afg_loadThemeIncludes($inc = null) {
$class_path = get_template_directory() . '/inc/';
foreach ( glob( $class_path."*.php" ) as $file ) {
require_once $file;//Be aware that all PHP files in /inc/ will be loaded.
}
}
Une fois que votre classe est correctement chargée, vous pouvez modifier woocommerce/content-product.php comme suit :
<?php
/**
* The template for displaying product content within loops
*
* This template can be overridden by copying it to yourtheme/woocommerce/content-product.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 3.6.0
*/
defined( 'ABSPATH' ) || exit;
global $product;
$main = new Main;
// Ensure visibility.
if ( empty( $product ) || ! $product->is_visible() ) {
return;
}
?>
<?php echo $main->getGridProduct(); ?>
L’objet $product Woocommerce nous fournit deux méthodes pour récupérer les images du produit :
$product->get_image_id() permet de récupérer l’ID de l’image mise en avant (featured image).
$product->get_gallery_image_ids() permet de récupérer les IDs des images de la galerie du produit.
public function getProductGallery() {
$html = '';
$imageIds = array();
global $product;
$imageIds = $product->get_gallery_image_ids();//We get product gallery image Ids
if( $product->get_image_id() ) array_unshift( $imageIds, $product->get_image_id() );//We put featured image Id on the top of the Array
if (count($imageIds) >= 1 ) {
foreach ($imageIds as $imageId) {
$html .= '<figure>';
$html .= sprintf(
'<img src="%s" alt="%s"/>',
esc_url( wp_get_attachment_image_url($imageId, 'afg_classic' ) ),
get_post_meta($imageId, '_wp_attachment_image_alt', TRUE)
);
$html .= '</figure>';
}
}
return $html;
}
Nous avons besoin d’un peu de Javascript pour animer notre galerie d’images.
Nous allons utiliser pour ça la micro librairie Js Slick.
Nous l’incluons dans notre fonction d’ajout de scripts et styles dans mon-theme/functions.php, lignes 3 et 5 dans l’exemple ci-dessous.
add_action( 'wp_enqueue_scripts', 'afg_enqueue_styles' );
function afg_enqueue_styles() {
wp_enqueue_style( 'slick-css', '//cdn.jsdelivr.net/npm/[email protected]/slick/slick.css',array(), '1.8.1', 'all');
wp_enqueue_style( 'bootstrap', '//stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css',array(), '4.4.1', 'all');
wp_enqueue_script( 'slick-js', '//cdn.jsdelivr.net/npm/[email protected]/slick/slick.js',array(), '1.8.1', 'all');
wp_enqueue_style( 'theme-style', get_template_directory_uri() . '/style.css', array('slick-css','bootstrap'), '1', false );
wp_enqueue_script( 'main-script', get_template_directory_uri() . '/assets/js/functions.js', array( 'jquery','slick-js' ), '1', true );
wp_localize_script( 'main-script', 'afterglowAjax', array(
'ajaxurl'=> admin_url( 'admin-ajax.php' )
) );
}
Nous créons le fichier mon-theme/assets/js/functions.js
jQuery(document).ready(function($){
$('.afg-grid-product > figure').slick({
infinite: true,
dots: true,
autoplay: true,
autoplaySpeed: 2000,
responsive: [
{
breakpoint: 992,
settings: {
arrows: true,
dots: false,
}
}
]
});
});
Vous pouvez retrouver ici toutes les options de Slick.
Dans cet exemple nous instancions un slider image par image, en lecture automatique et bouclée.
Nous affichons les contrôles par pastilles et par flèches.