Jump to content
Zebx

Aide Modif Requête Sql De Getattributecombinations

Recommended Posts

Bonjour,

 

J'aurais besoin d'un coup de pouce pour modifier la requête SQL dans getAttributeCombinations de la classe Product.

 

Je me sers en fait de cette fonction pour afficher l'ensemble des déclinaisons d'un produit sous forme d'un tableau.

 

Ca fonctionne très bien mais j'ai un petit souci pour adapter l'ordre de tri afin d'être vraiment parfait... car je souhaiterais que les attributs soient triés de la même façon que je les ai triés dans la gestion des "Attributs et Valeurs".

 

La requête originale est celle-ci, triée simplement par id de la déclinaison :

		$sql = 'SELECT pa.*, product_attribute_shop.*, ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, al.`name` AS attribute_name,
					a.`id_attribute`, pa.`unit_price_impact`
				FROM `'._DB_PREFIX_.'product_attribute` pa
				'.Shop::addSqlAssociation('product_attribute', 'pa').'
				LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute`
				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 pa.`id_product` = '.(int)$this->id.'
				GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group`
				ORDER BY pa.`id_product_attribute`';

Ce qui n'est pas génial car cela dépend totalement de l'ordre dans lequel on a créé les déclinaisons, donc ça peut être un fameux bordel... et si jamais on ajoute plus tard une déclinaison au produit, elle sera d'office classée à la fin du tableau.

 

 

J'ai donc déjà modifié la requête comme ceci, avec l'ajout de 2 ordres de tri :

		$sql = 'SELECT pa.*, product_attribute_shop.*, ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, al.`name` AS attribute_name,
					a.`id_attribute`, pa.`unit_price_impact`
				FROM `'._DB_PREFIX_.'product_attribute` pa
				'.Shop::addSqlAssociation('product_attribute', 'pa').'
				LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute`
				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 pa.`id_product` = '.(int)$this->id.'
				GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group`
				ORDER BY ag.`position`, a.`position`, pa.`id_product_attribute`';

C'est nettement mieux mais pas encore parfait car si la 1ère colonne est bien classée comme je le souhaite, la 2ème colonne reste toujours triée selon l'id de la déclinaison et non selon la position de l'attribut que j'ai définie dans son groupe.

 

Exemple :

 

Supposons que j'ajoute sur un produit un conditionnement par rouleau de 5m.

 

Ma requête modifiée me permet d'afficher ceci :

 

Couleur   Rouleau

Noir         3m

Noir         10m

Noir         5m

Bleu         3m

Bleu         10m

Bleu         5m

 

Alors que je souhaiterais arriver à ceci :

 

Couleur   Rouleau

Noir         3m

Noir         5m

Noir         10m

Bleu         3m

Bleu         5m

Bleu         10m

 

Dans la config des attributs, l'ordre souhaité étant évidemment le suivant : 3m, 5m, 10m.

 

 

Bref, j'ai un peu de mal avec le GROUP BY et ORDER BY de ma requête... qui évidemment s'arrête à la 1ère colonne... alors qu'idéalement je souhaiterais que la ou les colonnes suivantes soient aussi triées par "position".

 

 

Y aurait-il un spécialiste en requête SQL qui peut me filer un petit coup de pouce ? :)

Share this post


Link to post
Share on other sites

Bonjour,

 

D'après ce que je comprends de ce que vous voulez faire, il me semble que le problème de tri ne vienne pas de la requête SQL elle-même mais plutôt du code qui en exploite les résultats pour construire le tableau à afficher.

 

Dans l'exemple que vous donnez (2 attributs), la requête va retourner dans l'ordre:

  • selon la couleur:
    • d'abord les 3 déclinaisons noires, triées selon leur id
    • puis les 3 déclinaisons bleues triées selon leur id
  • puis, à nouveau les mêmes déclinaisons, selon la longueur cette fois:
    • d'abord les 2 déclinaisons de 3m, triées selon leur id
    • ensuite les 2 déclinaisons de 5m, triées selon leur id
    • puis les 2 déclinaisons de 10m, triées selon leur id

Pour obtenir le tableau final, vous êtes sans doute obligé de faire coïncider les 6 dernières lignes avec les 6 premières. Si à cette occasion vous ne ré-ordonnez pas vos lignes, le tableau final ne sera trié que par couleur puis id, mais pas par longueur.

 

Pouvez-vous soumettre ce code?

Cordialement

 

  • Like 1

Share this post


Link to post
Share on other sites

Bonjour,

 

Merci pour cette réponse, j'avais une mauvaise approche au départ et ça m'a aidé à mieux comprendre le déroulement :)

 

Donc effectivement c'est exactement ce qui se passe.

 

 

Pour le code, voici l'extrait en version résumée :

$attributeCombinations = $product->getAttributeCombinations($cookie->id_lang);

$attributes_groups = $product->getAttributesGroups($id_lang);
$groups_names = array();
foreach ($attributes_groups as $info) {
	$groups_names[$info['id_attribute_group']] = $info['public_group_name'];
}

foreach ($attributeCombinations as $attributeCombination) {
	$id_product_attribute = $attributeCombination ['id_product_attribute'];
	if (! array_key_exists ($id_product_attribute, $combinations_qty_prices)) {
		$combinations_qty_prices['combinations'][$id_product_attribute]['id_product_attribute'] = $id_product_attribute;
		$combinations_qty_prices['combinations'][$id_product_attribute]['reference'] = $attributeCombination['reference'];
	}
	$combinations_qty_prices['combinations'][$id_product_attribute]['attributes'][$groups_names[$attributeCombination['id_attribute_group']]] = $attributeCombination['attribute_name'];
}

En gros, toujours selon le même exemple précédent, seules les 6 premières lignes envoyées par la requête SQL sont conservées en entier et si dans la boucle on tombe sur un id de déclinaison déjà existante, les attributs viennent alors se greffer dans un sous-tableau d'attributs.

 

Ce qui donne donc un truc du genre pour une déclinaison :

"1078":{"id_product_attribute":"1078","reference":"W-10-1RL-BK","attributes":{"Couleur":"Noir","Packaging":"Rouleau de 3m"[spam-filter]

Donc en effet, mon tri n'est opérationnel que sur la première salve d'id de déclinaisons...

Mon résultat global est donc seulement trié par 1er attribut (couleur) puis trié par id déclinaison.

 

 

Du coup, une idée de comment je pourrais trier chaque groupe d'attributs par "position" sans que ça devienne une usine à gaz ? :D

 

 

Share this post


Link to post
Share on other sites

Bon j'ai un peu galéré mais j'ai à priori trouvé une solution :

$attributeCombinations = $product->getAttributeCombinations($cookie->id_lang);

$attributes_groups = $product->getAttributesGroups($id_lang);
$groups_names = array();
foreach ($attributes_groups as $info) {
	$groups_names[$info['id_attribute_group']] = $info['public_group_name'];
}

foreach ($attributeCombinations as $attributeCombination) {
	$id_product_attribute = $attributeCombination ['id_product_attribute'];
	if (! array_key_exists ($id_product_attribute, $combinations_qty_prices)) {
		$combinations_qty_prices['combinations'][$id_product_attribute]['id_product_attribute'] = $id_product_attribute;
		$combinations_qty_prices['combinations'][$id_product_attribute]['reference'] = $attributeCombination['reference'];
	}
	$combinations_qty_prices['combinations'][$id_product_attribute]['attributes'][$groups_names[$attributeCombination['id_attribute_group']]] = $attributeCombination['attribute_name'];
        $combinations_qty_prices['combinations'][$id_product_attribute]['attributes_pos'][$groups_names[$attributeCombination['id_attribute_group']]] = $attributeCombination['position'];
}
$positions = array();
foreach ($combinations_qty_prices['combinations'] as $comb) {
	foreach ($comb['attributes_pos'] as $key => $row) {
		$positions[$key][]  = $comb['attributes_pos'][$key];
	}
}
$param = array_merge($positions, array(&$combinations_qty_prices['combinations']));
call_user_func_array('array_multisort', $param);

Je retrie donc tout le tableau à la fin avec un array_multisort dynamique.

 

Pas sûr que ce soit la meilleure solution mais à priori ça fonctionne comme je le veux :D

 

Je reste néanmoins ouvert à toute autre idée si jamais y a mieux à faire ^^

Edited by Zebx (see edit history)

Share this post


Link to post
Share on other sites

C'est très bien puisque ça fonctionne!

 

L'utilisation d'array_multisort me semble tout à fait adaptée ici. Après, je ne suis pas spécialiste pour pouvoir me prononcer sur ses performances.

 

Par contre, vous pourriez légèrement optimiser votre code en évitant la seconde requête (getAttributesGroups) à la condition de remonter le nom public du groupe d'attributs à l'occasion de getAttributeCombinations, et en évitant la seconde boucle en construisant les paramètres de array_multisort dès la première, soit:

$sql = 'SELECT pa.*, product_attribute_shop.*,
            ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, agl.`public_name` AS public_group_name,
            a.`id_attribute`, a.`position`, al.`name` AS attribute_name
        FROM `'._DB_PREFIX_.'product_attribute` pa
        '.Shop::addSqlAssociation('product_attribute', 'pa').'
        LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute`
        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 pa.`id_product` = '.(int)$this->id.'
        GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group`
        ORDER BY ag.position, pa.`id_product_attribute`';

et

$sort_params = array();
$sorted_combinations = array();
$attributeCombinations = $product->getAttributeCombinations($id_lang);
foreach ($attributeCombinations as $ac) {
    $id_pa = $ac['id_product_attribute'];
    if (!isset($sorted_combinations[$id_pa])) {
        $sorted_combinations[$id_pa] = array(
            'id_product_attribute' => $id_pa,
            'reference' => $ac['reference'],
        );
    }
    $sorted_combinations[$id_pa]['attributes'][$ac['public_group_name']] = $ac['attribute_name'];
    $sort_params[$ac['public_group_name']][] = $ac['position'];
}
$sort_params[] = &$sorted_combinations;
call_user_func_array('array_multisort', $sort_params);

Ça suppose que toutes les combinaisons sont définies sur le même ensemble d'attributs. C'est vrai par le générateur de déclinaison, mais attention aux déclinaisons créées "manuellement"...

Edited by erouvier29 (see edit history)
  • Like 1

Share this post


Link to post
Share on other sites

Pour public_group_name, en effet, je peux remonter l'info aussi simplement, vu que j'ai de toute façon un override de ma classe product (à la base le code vient d'un module, donc ça explique les façon parfois détournées d'atteindre son but ^^).

 

En revanche pour la suppression de la seconde boucle, j'ai testé rapido mais à priori ça fonctionne pas.

En fait les positions de mon second attribut sont alors stockées par ordre d'arrivée depuis la requête SQL dans le tableau sort_params, et du coup l'array_multisort est inopérant.

 

L'intérêt de la seconde boucle étant justement de récupérer les positions du second attribut en fonction de l'ordre dans lequel mes déclinaisons ont d'abord été stockées une première fois dans le tableau avec le premier attribut.

 

Certes j'aime pas cette seconde boucle non plus, mais je vois pas trop comment l'éviter en fait...

 

Grand merci en tout cas pour les coups de pouce :)

Edited by Zebx (see edit history)

Share this post


Link to post
Share on other sites

N'auriez-vous pas laissé a.position dans la directive ORDER BY?

Je l'ai supprimé pour que, justement, les déclinaisons arrivent dans le même ordre (id croissant) pour chaque groupe d'attribut.

Ça avait l'air de marcher, mais c'est vrai que c'est complètement instable par rapport à une petite modif du reste...

  • Like 1

Share this post


Link to post
Share on other sites

Roooh, bien vu, c'est pourtant tellement limpide et plein de bon sens :lol:

 

Encore une fois merci, ça marche impec' et c'est mieux optimisé à présent... y a plus qu'à le passer en prod ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

Cookies ensure the smooth running of our services. Using these, you accept the use of cookies. Learn More