Zebx Posted January 17, 2016 Share Posted January 17, 2016 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 ? Link to comment Share on other sites More sharing options...
erouvier29 Posted January 18, 2016 Share Posted January 18, 2016 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 1 Link to comment Share on other sites More sharing options...
Zebx Posted January 18, 2016 Author Share Posted January 18, 2016 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 ? Link to comment Share on other sites More sharing options...
Zebx Posted January 19, 2016 Author Share Posted January 19, 2016 (edited) 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 Je reste néanmoins ouvert à toute autre idée si jamais y a mieux à faire ^^ Edited January 19, 2016 by Zebx (see edit history) Link to comment Share on other sites More sharing options...
erouvier29 Posted January 19, 2016 Share Posted January 19, 2016 (edited) 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 January 19, 2016 by erouvier29 (see edit history) 1 Link to comment Share on other sites More sharing options...
Zebx Posted January 19, 2016 Author Share Posted January 19, 2016 (edited) 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 January 19, 2016 by Zebx (see edit history) Link to comment Share on other sites More sharing options...
erouvier29 Posted January 19, 2016 Share Posted January 19, 2016 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... 1 Link to comment Share on other sites More sharing options...
Zebx Posted January 19, 2016 Author Share Posted January 19, 2016 Roooh, bien vu, c'est pourtant tellement limpide et plein de bon sens Encore une fois merci, ça marche impec' et c'est mieux optimisé à présent... y a plus qu'à le passer en prod Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now