Woocommerce : classer par attribut de produit avec mise à jour automatique des produits

Mathieu Chartier Programmation 2 commentaires

Le technique du filtrage par attribut de produit est relativement commune et connue pour les développeurs de Woocommerce (WordPress) mais il me semblait bon de la rappeler rapidement pour évoquer le sujet qui en découle, à savoir la mise à jour automatique de tous les produits, et notamment de leurs valeurs d'attributs. Nous allons voir comment régler le problème de la sauvegarde manuelle des valeurs d'attributs des produits sans effort, en automatisant tout...

Par défaut, lorsque vous voulez trier par attribut de produit, Woocommerce classe dans n'importe quel ordre car les valeurs d'attributs ne sont pas sauvegardées au bon endroit en base de données, mais uniquement les liaisons entre une valeur donnée et un produit choisi. En d'autres termes, au lieu d'avoir dans la table de données du produit à la fois les informations courantes et les valeurs d'attributs, Woocommerce ne fait qu'enregistrer l'identifiant (ID) de l'attribut. C'est uniquement dans une autre table que les liaisons sont faites entre ce fameux attribut, sa valeur, et le produit. Cela a une conséquence importante, les valeurs n'étant jamais vraiment enregistrées dans la table des produits, le classement se fait sur des valeurs faussées (des valeurs sérialisées qui plus est), et donc dans le désordre.

La seule manière de pouvoir sauvegarder la valeur des attributs dans le produit lui-même est d'ajouter une fonction PHP spécifique, d'entrer dans le produit et de sauvegarder (on attache cette fonction au hook save_post). Si vous souhaitez faire une modification rapide et sauvegarder, cela ne prend pas en compte les attributs, il convient donc bien d'entrer dans chaque produit, un par un, puis de sauvegarder. C'est bien sympa si nous ne possédons qu'une dizaine de produits en ligne, mais quand le nombre s'accroît, la manipulation devient vite chronophage. Nous allons donc voir comment automatiser la sauvegarder des valeurs de tous les attributs d'un seul tenant, en un seul clic sur un bouton (en bas de l'article)...

Filtrer par valeur d'attribut de produit avec Woocommerce

Dans un premier temps, nous allons revoir la technique pour filtrer par valeur d'attribut de produit avec Woocommerce. Ce n'est pas extrêmement compliqué, il faut procéder en deux étapes :

  • La première phase consiste à créer les facteurs de classement qui seront applicables avec Woocommerce.
  • La seconde étape vise à ajouter les types de classement de produits dans les catégories (liste déroulante). Pour rappel, il est possible d'éditer le classement par défaut dans le menu Personnaliser > Woocommerce > Catalogue de produits si besoin.

Dans notre exemple, nous allons ajouter un tri alphabétique des produits, donc sans valeur d'attribut utile, et une classement par barème de prix (et non par prix !), à savoir un attribut créé pour l'occasion sur une boutique en ligne. Dans le code, les attributs de produits commencent toujours par "pa_" suivi du slug de l'attribut ("pa" pour "product attribute" bien entendu). Vous retrouverez donc des valeurs de ce type dans le code.

Voici sans plus attendre la fonction pour créer et personnaliser les critères de classement de Woocommerce.

/* Ajouter l'ordre alphabétique et par barème de prix dans Woocommerce */
function woocommerce_alphabetical_shop_ordering($sort_args) {
	$orderby_value = isset($_GET['orderby']) ? woocommerce_clean($_GET['orderby']) : apply_filters('woocommerce_default_catalog_orderby', get_option('woocommerce_default_catalog_orderby'));
	if('alphabetical' == $orderby_value) {
		$sort_args['orderby'] = 'title';
		$sort_args['order'] = 'asc';
		$sort_args['meta_key'] = '';
	}
	if('bareme_asc' == $orderby_value) {
		$sort_args['orderby'] = 'meta_value';
		$sort_args['order'] = 'asc';
		$sort_args['meta_key'] = 'pa_bareme-de-prix';
	}
	if('bareme_desc' == $orderby_value) {
		$sort_args['orderby'] = 'meta_value';
		$sort_args['order'] = 'desc';
		$sort_args['meta_key'] = 'pa_bareme-de-prix';
	}
	return $sort_args;
}
add_filter('woocommerce_get_catalog_ordering_args', 'woocommerce_alphabetical_shop_ordering');

Maintenant, voici la fonction PHP pour ajouter les types de tris de produits dans Woocommerce. Vous noterez que j'ai masqué trois types de tris ici, dont le tri par défaut (menu_order).

// Ajoute les types de classement dans les catégories de produits de Woocommerce
function woocommerce_catalog_orderby($sortby) {
	$menu_sortby = array(
		// 'menu_order' => $sortby['menu_order'],
		'popularity' => $sortby['popularity'],
		// 'price' => $sortby['price'],
		// 'price-desc' => $sortby['price-desc'],
		'bareme_asc' => __('Barème de prix > Ascendant', THEME_SLUG_LANG), // Tri par attribut de produit
		'bareme_desc' => __('Barème de prix > Descendant', THEME_SLUG_LANG), // Tri par attribut de produit
		'date' => $sortby['date'],
		'alphabetical' => __('Alphabétique', THEME_SLUG_LANG), // Tri alphabétique (sans attribut de produit)
	);
	return $menu_sortby;
}
add_filter('woocommerce_default_catalog_orderby_options', 'woocommerce_catalog_orderby');
add_filter('woocommerce_catalog_orderby', 'woocommerce_catalog_orderby');

Avec ces deux fonctions, vous obtenez bien un classement fonctionnel pour l'ordre alphabétique, mais pas encore pour les attributs de produits, pour les raisons inhérentes à Woocommerce précédemment évoquées. La capture suivante montre le résultat de la deuxième fonction PHP avec les nouveaux types de tris de produits (le résultat de la première ne pouvant pas vraiment être montré sur une image fixe).

Nouveaux types de tris de produits dans Woocommerce (avec et sans attribut de produit)

Sauvegarder les valeurs d'attributs dans Woocommerce avec save_post

Maintenant que nous savons ajouter les types de tris dans la boutique en ligne, il serait bien de rendre les tris par attributs de produits fonctionnels. Pour ce faire, nous allons devoir ajouter une courte fonction dont l'objectif est d'enregistrer les valeurs d'attributs directement dans le produit (et non plus par liaison de tables dans la base de données). Il s'agit juste de se rattacher au hook save_post pour forcer la sauvegarde des données, il serait également possible de doubler les hooks de rattachement avec edit_post par exemple, voire save_post_{$post->post_type}. Rappelons que ces méthodes d'enregistrement ne fonctionneront que si vous rentrez dans l'édition de la fiche produit et que vous enregistrez...

// Force la sauvegarde des valeurs d'attributs dans le produit (BDD)
function save_woocommerce_attr_to_meta($post_id) {
	// Récupère Tous les éléments par nom d'attributs (slug et valeur d'attribut)
    foreach($_REQUEST['attribute_names'] as $index => $value) {
    	// Met à jour la valeur de l'attribut dans le produit lui-même
        update_post_meta($post_id, $value, $_REQUEST['attribute_values'][$index]);
    }
}
add_action('save_post', 'save_woocommerce_attr_to_meta');

Voilà, c'est fait, il ne vous restera qu'à enregistrer chaque produit pour que la valeur des attributs soit enregistrée dans la base de données.

Mise à jour automatique des valeurs d'attributs de produits

Avec l'étape précédente, nous avons pu sauvegarder les valeurs d'attributs quand on enregistre un produit, mais cela oblige à refaire la manipulation pour chaque produit, et donc parfois pour des centaines voire milliers de produits déjà existants dans la base de données. En effet, il est tout-à-fait possible lors d'une refonte de site web ou d'amélioration d'une boutique de vouloir ajouter un nouvel attribut et de classer selon lui. Il serait absolument ignoble de devoir enregistrer chaque produit à la main juste pour contrer une sorte de défaut de Woocommerce.

Nous allons donc procéder à une technique simple pour contrer ce manque et ce problème bien embêtant (c'est du vécu, vous vous en doutez... ^^). Dans un premier temps, nous allons voir comment effectuer la sauvegarde automatique des attributs de produits avec Woocommerce, c'est la fonction PHP la plus importante de cet article. Il vous suffira de la rattacher à un hook courant de WordPress pour la lancer ("init", "wp_head", "wp_footer"...). Dans un second temps, nous verrons comment éviter de se rattacher à un hook, mais plutôt en passant par un nouveau bouton ajouter dans les réglages de Woocommerce, ce sera un peu plus "sexy" et fonctionnel. ;-)

Voici donc la fonction d'automatisation de l'enregistrement des attributs de produits. Pensez à supprimer le commentaire au niveau du hook si vous ne voulez pas passer par la seconde étape qui suivra (directement dans l'interface de Woocommerce).

// Mettre à jour automatiquement tous les attributs Woocommerce sans save_post
function autosave_all_custom_attributes() {
	// Récupère la liste des attributs Woocommerce
	$attribute_taxonomies = wc_get_attribute_taxonomies();
	
	// Récupère tous
	$args = array(
        'post_type' => 'product',
        'posts_per_page' => -1,
        // 'meta_key' => '_product_attributes'
    );
    $wp_query = get_posts($args);

	// Pour chaque attribut existant...
	foreach($attribute_taxonomies as $attribute) {
		// On reconstitue le nom de la taxonomy en pa_NOM-ATTRIBUT pour Woocommerce
		$attribute_name = 'pa_'.$attribute->attribute_name;

		// Pour chaque produit...
		foreach($wp_query as $product) {
    		// On récupère toutes les associations entre attributs et ID de produits
		    $relationships = get_the_terms($product->ID, $attribute_name);

		    if(!empty($relationships)) {
				foreach($relationships as $relationship) {
			    	if(!empty($relationship->term_id)) {
			    		// On serialize un tableau de la valeur de l'attribut donné
				    	$value = serialize(
				    		array($relationship->term_id)
				    	);
				    	update_post_meta($product->ID, $attribute_name, $value);
			    	}
		    	}
		    }
	    }
    }
}
// add_action('wp_head', 'autosave_all_custom_attributes');

Maintenant, voici comment nous allons ajouter un bouton dans Woocommerce pour effectuer cette procédure de sauvegarde automatique, qui pourra donc être utilisée à tout moment. Je vous mets une capture d'écran pour que vous repériez le menu et l'onglet à rejoindre pour trouver l'option. J'ai placé cela dans l'onglet "Produits > Attributs" du menu de réglages de Woocommerce.

Nouvelle option pour effectuer un enregistrement automatique des attributs de produits dans Woocommerce

Vous noterez qu'il faut trois fonctions plus longues que toutes les autres juste pour ajouter un fichu bouton. C'est un peu le comble dans cet article car jusqu'à présent, il s'agissait de fonctions PHP très courtes et efficaces. Là, on entre un peu plus dans le dur (ça reste accessible bien sûr) juste pour un simple bouton dans une l'interface de Woocommerce... :-)

// Ajoute une nouvelle section dans l'onglet "Produits" des réglages Woocommerce
function autosave_attributes_add_section($sections) {
	$sections['autosave_attributes'] = __('Attributs');
	return $sections;
}
add_filter('woocommerce_get_sections_products', 'autosave_attributes_add_section');

// Créé un nouveau type de champ pour l'admin de Woocommerce (submit)
function submit_autosave_fields($value) {
    $option_value = (array) WC_Admin_Settings::get_option($value['id']);
    if(!empty($value['class'])) {
    	$class = esc_attr($value['class']);
    } else {
    	$class = "button-primary";
    }
  ?><tr valign="top">
        <th scope="row" class="titledesc">
            <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html($value['title']); ?></label>
        </th>
        <td class="forminp forminp-<?php echo sanitize_title($value['type']); ?>">
			<fieldset>
				<legend class="screen-reader-text"><span><?php echo esc_html($value['title']); ?></span></legend>
				<input name="<?php echo esc_attr($value['id']); ?>" id="<?php echo esc_attr($value['id']); ?>" type="submit" class="<?php echo esc_attr($class); ?>" value="<?php echo esc_attr($value['desc']); ?>"/>
				<p class="description"><?php echo esc_html($value['desc_tip']); ?></p>
			</fieldset>
        </td>
    </tr><?php

    if(isset($_POST[$value['id']])) {
    	autosave_all_custom_attributes();
    }
}
add_action('woocommerce_admin_field_submit_autosave', 'submit_autosave_fields');

// Permet d'ajouter le formulaire dans le nouveau panneau d'options
function autosave_attributes_all_settings($settings, $current_section) {
	// Vérifie et créé la nouvelle section
	if($current_section == 'autosave_attributes') {
		$settings_slider = array();
		$settings_slider[] = array(
			'name' => __('Mise à jour automatique des attributs de produits'),
			'type' => 'title',
			'desc' => __('Le bouton suivant vous permet d\'effectuer une mise à jour automatique de tous les attributs de produits.'),
			'id' => 'autosave_attributes_title'
		);
		// Add second text field option
		$settings_slider[] = array(
			'name'     => __('Mise à jour automatique', 'text-domain'),
			'desc_tip' => __('En cliquant, vous validez la mise à jour automatique des attributs de produits.'),
			'id'       => 'autosave_attributes_check',
			'type'     => 'submit_autosave',
			'desc'     => __('Mettre à jour les attributs'),
		);
		$settings_slider[] = array('type' => 'sectionend', 'id' => 'autosave_attributes');
		return $settings_slider;
	} else { // Retourne les paramètres classiques en cas de problème...
		return $settings;
	}
}
add_filter('woocommerce_get_settings_products', 'autosave_attributes_all_settings', 10, 2);

C'est terminé, vous savez désormais comment trier par attribut de produits dans Woocommerce, tout en automatisant la sauvegarde des valeurs d'attributs de produits au bon endroit dans la base de données, vous devriez gagner pas mal de temps si vous n'aviez pas l'habitude de procéder ainsi... ;-)