#Prestashop : Changer l'ordre d'affichage des attributs sans module

Dans Prestashop 1.4, les attributs sont affichés par ordre alphabétique.

De base, il n’est donc pas possible de gérer l’ordre d’affichage des attributs.

Néanmoins, avec l’override il est désormais possible de gérer l’ordre.

Notre méthode consite à :

  • modifier le nom des attributs en ajoutant en préfixe, un numéro qui servira à indiquer l’ordre de l’attribut
  • modifier le fichier product.tpl de votre thème Prestashop
  • Overrider la classe Cart.php et modifier certains fichiers de module

1 Modifier le nom des attributs

Dans votre backoffice Prestashop, modifiez le nom de vos attributs en ajoutant un numéro suivi d’un point :

Prestashop : ordre des attributs

Prestashop : ordre des attributs

Attention, si vous utilisez déjà un point dans le nom de vos attributs, utilisez un autre caractère qui ne sera pas utilisé dans le nom de l’attribut.

Pour l’instant le nom des attributs s’affichent dans la boutique avec ce préfixe :

Prestashop : ordre attribut

Prestashop : ordre attribut

Nous devons donc supprimer ce préfixe.

2 Où sont affichés les attributs dans votre boutique Prestashop ?

Dans  Prestashop, les attributs sont affichés dans différentes pages :

  • la page produit
  • la page récapitulatif de la commande
  • la page historique de mes commandes (dans le compte client)
  • le bloc panier
  • la facture pdf
  • le bloc liste de cadeaux (si module wishlist est activé)
  • la page Mes listes (dans le compte client)

Selon ces pages, nous aurons besoin de modifier un fichier tpl, la class Cart.php ou un fichier dans un module.

3 Les modifications

3.1 Modification du fichier product.tpl

Ce fichier se trouve dans le dossier de votre thème Prestashop.

Avec smarty, nous devons trouver la position du point dans le nom de l’attribut, puis supprimer les caractères se trouvant avant ce point.

On remplace donc la ligne suivante :

[code lang="php" firstline="302" highlight="307"]

{if isset($groups)}

<!-- attributes -->

<div id="attributes">{foreach from=$groups key=id_attribute_group item=group}

{if $group.attributes|@count}

<label for="group_{$id_attribute_group|intval}">{$group.name|escape:'htmlall':'UTF-8'} :</label>

{assign var="groupName" value="group_$id_attribute_group"}

<select name="{$groupName}" onchange="javascript:findCombination();{if $colors|@count > 0}$('#wrapResetImages').show('slow');{/if};"> {foreach from=$group.attributes key=id_attribute item=group_attribute}</select> <select name="{$groupName}" onchange="javascript:findCombination();{if $colors|@count > 0}$('#wrapResetImages').show('slow');{/if};"><option title="{$group_attribute|escape:'htmlall':'UTF-8'}" selected="selected" value="{$id_attribute|intval}"> {$group_attribute|escape:'htmlall':'UTF-8'}</option></select> <select name="{$groupName}" onchange="javascript:findCombination();{if $colors|@count > 0}$('#wrapResetImages').show('slow');{/if};"> {/foreach}</select>

{/if}

{/foreach}</div>

{/if}

[/code]

Par :

[code lang="php" firstline="302" highlight="308,309,310,311,312,313"]

{if isset($groups)}

<!-- attributes -->

<div id="attributes">{foreach from=$groups key=id_attribute_group item=group}

{if $group.attributes|@count}

<label for="group_{$id_attribute_group|intval}">{$group.name|escape:'htmlall':'UTF-8'} :</label>

{assign var="pos" value=$group_attribute|strpos:"."}

{if $pos!=false}

{$group_attribute|substr:($pos+1)|escape:'htmlall':'UTF-8'}

{else}

{$group_attribute|escape:'htmlall':'UTF-8'}

{/if}

<select name="{$groupName}" onchange="javascript:findCombination();{if $colors|@count > 0}$('#wrapResetImages').show('slow');{/if};"> {foreach from=$group.attributes key=id_attribute item=group_attribute}</select> <select name="{$groupName}" onchange="javascript:findCombination();{if $colors|@count > 0}$('#wrapResetImages').show('slow');{/if};"><option title="{$group_attribute|escape:'htmlall':'UTF-8'}" selected="selected" value="{$id_attribute|intval}"> {$group_attribute|escape:'htmlall':'UTF-8'}</option></select> <select name="{$groupName}" onchange="javascript:findCombination();{if $colors|@count > 0}$('#wrapResetImages').show('slow');{/if};"> {/foreach}</select>

{/if}

{/foreach}</div>

{/if}

[/code]

3.2 Override de la classe Cart.php

Cette modification permettra d’afficher la liste des attributs sans le préfixe(ex : 01.,02., etc…).

On obtiendra le bon affichage pour les pages ou éléments suivants :

  • la page récapitulatif de la commande
  • la page historique de mes commandes (dans le compte client)
  • le bloc panier

Commençons par créer le fichier Cart.php pour l’override dans le dossier override/classes (à la racine de votre Prestashop) :

[code lang="php"]

<!--?php class Cart extends CartCore { } ?-->

[/code]

La fonction qui récupère le nom des attributs dans la classe Cart.php se nomme cacheSomeAttributesLists :

[code lang="php" firstline="450" highlight="472,473,474,475,476"]

public static function cacheSomeAttributesLists($ipaList, $id_lang)

{

$paImplode = array();

foreach ($ipaList as $id_product_attribute)

if ((int)$id_product_attribute AND !array_key_exists($id_product_attribute.'-'.$id_lang, self::$_attributesLists))

{

$paImplode[] = (int)$id_product_attribute;

self::$_attributesLists[(int)$id_product_attribute.'-'.$id_lang] = array('attributes' => '', 'attributes_small' => '');

}

if (!count($paImplode))

return;

$result = Db::getInstance()->ExecuteS('

SELECT pac.id_product_attribute, agl.public_name AS public_group_name, al.name AS attribute_name

FROM '._DB_PREFIX_.'product_attribute_combination pac

LEFT JOIN '._DB_PREFIX_.'attribute a ON a.id_attribute = pac.id_attribute

LEFT JOIN '._DB_PREFIX_.'attribute_group ag ON ag.id_attribute_group = a.id_attribute_group

LEFT JOIN '._DB_PREFIX_.'attribute_lang al ON (a.id_attribute = al.id_attribute AND al.id_lang = '.(int)$id_lang.')

LEFT JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (ag.id_attribute_group = agl.id_attribute_group AND agl.id_lang = '.(int)$id_lang.')

WHERE pac.id_product_attribute IN ('.implode($paImplode, ',').')

ORDER BY agl.public_name ASC');

foreach ($result as $row)

{

self::$_attributesLists[$row['id_product_attribute'].'-'.$id_lang]['attributes'] .= $row['public_group_name'].' : '.$row['attribute_name'].'-big,';

self::$_attributesLists[$row['id_product_attribute'].'-'.$id_lang]['attributes_small'] .= $row['attribute_name'].'-small,';

}

foreach ($paImplode as $id_product_attribute)

{

self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes'] = rtrim(self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes'], ', ');

self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes_small'] = rtrim(self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes_small'], ', ');

}

}

[/code]

La boucle à la ligne 472 récupère le nom de l’attribut : $row['attribute_name'].

grâce aux fonctions php strpos et substr, nous allons supprimer le préfixe pour qu’il ne s’affiche pas.

Voici le fichier Cart.php (dans le dossier classe/override) complet :

[code lang="php" highlight="138,139,140,141,142,143"]

<!--?php class Cart extends CartCore { protected static $_attributesLists = array(); /** * Return cart products * * @result array Products */ public function getProducts($refresh = false, $id_product = false) { if (!$this--->id)

return array();

// Product cache must be strictly compared to NULL, or else an empty cart will add dozens of queries

if ($this->_products !== NULL AND !$refresh)

return $this->_products;

$sql = '

SELECT cp.id_product_attribute, cp.id_product, cu.id_customization, cp.quantity AS cart_quantity, cu.quantity AS customization_quantity, pl.name,

pl.description_short, pl.available_now, pl.available_later, p.id_product, p.id_category_default, p.id_supplier, p.id_manufacturer, p.on_sale, p.ecotax, p.additional_shipping_cost, p.available_for_order,

p.quantity, p.price, p.weight, p.width, p.height, p.depth, p.out_of_stock, p.active, p.date_add, p.date_upd, IFNULL(pa.minimal_quantity, p.minimal_quantity) as minimal_quantity,

t.id_tax, tl.name AS tax, t.rate, pa.price AS price_attribute, pa.quantity AS quantity_attribute,

pa.ecotax AS ecotax_attr, pl.link_rewrite, cl.link_rewrite AS category, CONCAT(cp.id_product, cp.id_product_attribute) AS unique_id,

IF (IFNULL(pa.reference, '') = '', p.reference, pa.reference) AS reference,

IF (IFNULL(pa.supplier_reference, '') = '', p.supplier_reference, pa.supplier_reference) AS supplier_reference,

(p.weight+ pa.weight) weight_attribute,

IF (IFNULL(pa.ean13, '') = '', p.ean13, pa.ean13) AS ean13, IF (IFNULL(pa.upc, '') = '', p.upc, pa.upc) AS upc,

pai.id_image pai_id_image, il.legend pai_legend

FROM '._DB_PREFIX_.'cart_product cp

LEFT JOIN '._DB_PREFIX_.'product p ON p.id_product = cp.id_product

LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (p.id_product = pl.id_product AND pl.id_lang = '.(int)$this->id_lang.')

LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product_attribute = cp.id_product_attribute)

LEFT JOIN '._DB_PREFIX_.'tax_rule tr ON (p.id_tax_rules_group = tr.id_tax_rules_group

AND tr.id_country = '.(int)Country::getDefaultCountryId().'

AND tr.id_state = 0)

LEFT JOIN '._DB_PREFIX_.'tax t ON (t.id_tax = tr.id_tax)

LEFT JOIN '._DB_PREFIX_.'tax_lang tl ON (t.id_tax = tl.id_tax AND tl.id_lang = '.(int)$this->id_lang.')

LEFT JOIN '._DB_PREFIX_.'customization cu ON (cp.id_product = cu.id_product AND cp.id_product_attribute = cu.id_product_attribute AND cu.id_cart = cp.id_cart)

LEFT JOIN '._DB_PREFIX_.'product_attribute_image pai ON (pai.id_product_attribute = pa.id_product_attribute)

LEFT JOIN '._DB_PREFIX_.'image_lang il ON (il.id_image = pai.id_image AND il.id_lang = '.(int)$this->id_lang.')

LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (p.id_category_default = cl.id_category AND cl.id_lang = '.(int)$this->id_lang.')

WHERE cp.id_cart = '.(int)$this->id.'

'.($id_product ? ' AND cp.id_product = '.(int)$id_product : '').'

AND p.id_product IS NOT NULL

GROUP BY unique_id

ORDER BY cp.date_add ASC';

$result = Db::getInstance()->ExecuteS($sql);

// Reset the cache before the following return, or else an empty cart will add dozens of queries

$productsIds = array();

$paIds = array();

foreach ($result as $row)

{

$productsIds[] = $row['id_product'];

$paIds[] = $row['id_product_attribute'];

}

// Thus you can avoid one query per product, because there will be only one query for all the products of the cart

Product::cacheProductsFeatures($productsIds);

self::cacheSomeAttributesLists($paIds, $this->id_lang);

$this->_products = array();

if (empty($result))

return array();

foreach ($result AS $row)

{

if (isset($row['ecotax_attr']) AND $row['ecotax_attr'] > 0)

$row['ecotax'] = (float)($row['ecotax_attr']);

$row['stock_quantity'] = (int)($row['quantity']);

// for compatibility with 1.2 themes

$row['quantity'] = (int)($row['cart_quantity']);

if (isset($row['id_product_attribute']) AND (int)$row['id_product_attribute'])

{

$row['weight'] = $row['weight_attribute'];

$row['stock_quantity'] = $row['quantity_attribute'];

}

if ($this->_taxCalculationMethod == PS_TAX_EXC)

{

$row['price'] = Product::getPriceStatic((int)$row['id_product'], false, isset($row['id_product_attribute']) ? (int)($row['id_product_attribute']) : NULL, 2, NULL, false, true, (int)($row['cart_quantity']), false, ((int)($this->id_customer) ? (int)($this->id_customer) : NULL), (int)($this->id), ((int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) ? (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) : NULL), $specificPriceOutput); // Here taxes are computed only once the quantity has been applied to the product price

$row['price_wt'] = Product::getPriceStatic((int)$row['id_product'], true, isset($row['id_product_attribute']) ? (int)($row['id_product_attribute']) : NULL, 2, NULL, false, true, (int)($row['cart_quantity']), false, ((int)($this->id_customer) ? (int)($this->id_customer) : NULL), (int)($this->id), ((int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) ? (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) : NULL));

$tax_rate = Tax::getProductTaxRate((int)$row['id_product'], (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}));

$row['total_wt'] = Tools::ps_round($row['price'] * (float)$row['cart_quantity'] * (1 + (float)($tax_rate) / 100), 2);

$row['total'] = $row['price'] * (int)($row['cart_quantity']);

}

else

{

$row['price'] = Product::getPriceStatic((int)$row['id_product'], false, (int)$row['id_product_attribute'], 6, NULL, false, true, $row['cart_quantity'], false, ((int)($this->id_customer) ? (int)($this->id_customer) : NULL), (int)($this->id), ((int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) ? (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) : NULL), $specificPriceOutput);

$row['price_wt'] = Product::getPriceStatic((int)$row['id_product'], true, (int)$row['id_product_attribute'], 2, NULL, false, true, $row['cart_quantity'], false, ((int)($this->id_customer) ? (int)($this->id_customer) : NULL), (int)($this->id), ((int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) ? (int)($this->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) : NULL));

/* In case when you use QuantityDiscount, getPriceStatic() can be return more of 2 decimals */

$row['price_wt'] = Tools::ps_round($row['price_wt'], 2);

$row['total_wt'] = $row['price_wt'] * (int)($row['cart_quantity']);

$row['total'] = Tools::ps_round($row['price'] * (int)($row['cart_quantity']), 2);

}

if (!isset($row['pai_id_image']) OR $row['pai_id_image'] == 0)

{

$row2 = Db::getInstance()->getRow('

SELECT i.id_image, il.legend

FROM '._DB_PREFIX_.'image i

LEFT JOIN '._DB_PREFIX_.'image_lang il ON (i.id_image = il.id_image AND il.id_lang = '.(int)$this->id_lang.')

WHERE i.id_product = '.(int)$row['id_product'].' AND i.cover = 1');

if (!$row2)

$row2 = array('id_image' => false, 'legend' => false);

else

$row = array_merge($row, $row2);

}

else

{

$row['id_image'] = $row['pai_id_image'];

$row['legend'] = $row['pai_legend'];

}

$row['reduction_applies'] = ($specificPriceOutput AND (float)$specificPriceOutput['reduction']);

$row['id_image'] = Product::defineProductImage($row, $this->id_lang);

$row['allow_oosp'] = Product::isAvailableWhenOutOfStock($row['out_of_stock']);

$row['features'] = Product::getFeaturesStatic((int)$row['id_product']);

if (array_key_exists($row['id_product_attribute'].'-'.$this->id_lang, self::$_attributesLists))

$row = array_merge($row, self::$_attributesLists[$row['id_product_attribute'].'-'.$this->id_lang]);

$this->_products[] = $row;

}

return $this->_products;

}

public static function cacheSomeAttributesLists($ipaList, $id_lang)

{

$paImplode = array();

foreach ($ipaList as $id_product_attribute)

if ((int)$id_product_attribute AND !array_key_exists($id_product_attribute.'-'.$id_lang, self::$_attributesLists))

{

$paImplode[] = (int)$id_product_attribute;

self::$_attributesLists[(int)$id_product_attribute.'-'.$id_lang] = array('attributes' => '', 'attributes_small' => '');

}

if (!count($paImplode))

return;

$result = Db::getInstance()->ExecuteS('

SELECT pac.id_product_attribute, agl.public_name AS public_group_name, al.name AS attribute_name

FROM '._DB_PREFIX_.'product_attribute_combination pac

LEFT JOIN '._DB_PREFIX_.'attribute a ON a.id_attribute = pac.id_attribute

LEFT JOIN '._DB_PREFIX_.'attribute_group ag ON ag.id_attribute_group = a.id_attribute_group

LEFT JOIN '._DB_PREFIX_.'attribute_lang al ON (a.id_attribute = al.id_attribute AND al.id_lang = '.(int)$id_lang.')

LEFT JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (ag.id_attribute_group = agl.id_attribute_group AND agl.id_lang = '.(int)$id_lang.')

WHERE pac.id_product_attribute IN ('.implode($paImplode, ',').')

ORDER BY agl.public_name ASC');

foreach ($result as $row)

{

$att_name_prefix=$row['attribute_name'];

$pos=strpos($att_name_prefix,".");

if($pos != false)

{

$att_name_prefix=substr($att_name_prefix,($pos+1));

}

self::$_attributesLists[$row['id_product_attribute'].'-'.$id_lang]['attributes'] .= $row['public_group_name'].' : '.$att_name_prefix.',';

self::$_attributesLists[$row['id_product_attribute'].'-'.$id_lang]['attributes_small'] .= $att_name_prefix.',';

}

foreach ($paImplode as $id_product_attribute)

{

self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes'] = rtrim(self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes'], ', ');

self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes_small'] = rtrim(self::$_attributesLists[$id_product_attribute.'-'.$id_lang]['attributes_small'], ', ');

}

}

}

?>

[/code]

3.3 Modification du module Wislist

Nous utilisons la même technique que pour l’override de Cart.php.

Le fichier à modifier est Wishlist.php (ligne 280) dans le dossier modules/wishlist :

[code lang="php" firstline="280" highlight="282,283,284,285,286,287,288,289,290"]

$products[$i]['attributes_small'] = '';

if ($result)

foreach ($result AS $k => $row){

$att_name_prefix=$row['attribute_name'];

$pos=strpos($att_name_prefix,".");

if($pos !== false)

{

$att_name_prefix=substr($att_name_prefix,($pos+1));

}

$products[$i]['attributes_small'] .= $att_name_prefix.', ';

}

$products[$i]['attributes_small'] = rtrim($products[$i]['attributes_small'], ', ');

[/code]

3.4 Modification du module Mailalerts

Le fichier à modifier est mailalerts.php (ligne 533) dans le dossier modules/mailalerts :

[code lang="php" firstline="533" highlight="535,536,537,538,539,540,541,542,543"]

$products[$i]['attributes_small'] = '';

if ($result)

foreach ($result AS $k => $row){

$att_name_prefix=$row['attribute_name'];

$pos=strpos($att_name_prefix,".");

if($pos !== false)

{

$att_name_prefix=substr($att_name_prefix,($pos+1));

}

$products[$i]['attributes_small'] .= $att_name_prefix.', ';

}

$products[$i]['attributes_small'] = rtrim($products[$i]['attributes_small'], ', ');

[/code]

Pour résumer, en modifiant 4 fichiers (20 min de travail), vous pouvez maintenant gérer l’ordre d’affichage des attributs sans  module !!