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 :
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 :
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 !!