J'essaie actuellement d'améliorer quelques modules concernant les performances.
Certains d'entre vous connaissent peut-être l' utilisation de la walk()
méthode de collecte, ce qui est très utile pour éviter de parcourir directement les produits.
En plus de cela et grâce à @Vinai, on peut également utiliser la delete()
méthode de collecte .
Mais j'ai remarqué que les fichiers natifs de Magento 1 n'utilisent pas toujours l'une de ces méthodes pour la suppression.
L'un des pires codes que j'ai vus est la massDelete()
méthode à partir de app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php
laquelle les produits sont chargés dans une boucle avant la suppression .
foreach ($productIds as $productId) {
$product = Mage::getSingleton('catalog/product')->load($productId);
Mage::dispatchEvent('catalog_controller_product_delete', array('product' => $product));
$product->delete();
}
J'ai donc fait quelques tests de performances, ajouté quelques appels de journalisation pour vérifier le temps pris et l'utilisation de la mémoire pour la suppression de 100 produits.
Test 1: walk
méthode
J'ai remplacé le code d'origine collé ci-dessus par ce code:
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('entity_id')
->addIdFilter($productIds)
->walk('delete');
Et mes résultats sont les suivants sur mon serveur de développement merdique (moyenne basée sur 10 tests):
- Code d'origine: 19,97 secondes, 15,84 Mo utilisés
- Code personnalisé: 17,12 secondes, 15,45 Mo utilisés
Ainsi, pour la suppression de 100 produits, mon code personnalisé est 3 secondes plus rapide et utilise 0,4 Mo de moins.
Test 2: utilisation de la delete()
méthode de collecte
J'ai remplacé le code d'origine par celui-ci:
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('entity_id')
->addIdFilter($productIds)
->delete();
Et les résultats sont époustouflants :
- Code d'origine: 19,97 secondes, 15,84 Mo utilisés
- Code personnalisé: 1,24 seconde, 6,34 Mo utilisés
Donc, pour la suppression de 100 produits, mon code personnalisé est 18 secondes plus rapide et utilise 9 Mo de moins.
Comme indiqué dans les commentaires, il semble que cette méthode ne déclenche pas les événements Magento (après chargement, après suppression) ni le vidage d'index / cache.
Question
Ma question est donc la suivante: y a-t-il une raison pour laquelle l'équipe principale de Magento n'a pas mieux utilisé la méthode de walk('delete')
collecte de l'événement ou au delete()
lieu de charger les produits en boucle (ce que nous savons tous est une très très mauvaise pratique)?
L'objectif principal est de connaître ces points clés en cas de développement d'un module: y a-t-il des cas particuliers où l'on ne peut pas utiliser la méthode walk
/ collection delete()
?
EDIT: la raison n'est certainement pas à cause de l' catalog_controller_product_delete
événement envoyé car le même code peut être trouvé à plusieurs endroits (vérifiez les massDelete
méthodes) dans le noyau Magento. J'ai utilisé l'exemple des produits pour mettre en évidence les performances car ce sont généralement les plus grandes entités
la source
getSingleton()
comme mesure de performance, au lieu de l'utilisation évidente de la collection. Oh, et il est également possible de déclencher l'événement avec une collection, mais pas avec lewalk()
raccourci.delete()
effectue une requête DELETE au lieu de charger la collection et de supprimer chaque produit. Avec celui-là, vous perdrez vraiment les événements.Réponses:
Remarque, mais vous devriez envisager d'utiliser le Varien Profiler pour cela!
Bien que je ne doute pas que votre modification améliorerait les performances, il serait utile de fournir les résultats "avant" pour comparer les améliorations.
Eh bien, nous savons par d'autres questions sur ce forum ce qui suit:
Je dirais donc que l'exemple que vous avez trouvé est probablement l'un des nombreux joyaux potentiellement cachés dans le code qui ont été écrits il y a longtemps et / ou par un développeur moins expérimenté. Comme une grande partie du code de base (et du code de la communauté!), Il aurait été testé sur un petit ensemble de données et non testé en combat, donc les performances peuvent ne pas avoir été étroitement surveillées.
Votre amélioration est-elle bénéfique et plus conforme aux meilleures pratiques que le code d'origine? Oui. En tant que développeur communautaire Magento [1.x], vous n'avez cependant pas la possibilité de contribuer aux améliorations suggérées, comme vous le faites avec Magento 2, donc ma suggestion serait de l'implémenter dans un module local si vous en avez besoin pour les performances dans l'un de vos magasins. , ou ignorez-le si cela ne vous affecte pas, mais vous l'avez remarqué en faisant des recherches.
En tant que mise à jour de la modification de votre question, je suis sûr que vous savez que la méthode de marche de Varien_Data_Collection accepte un rappel arbitraire, vous seriez donc libre de l'utiliser pour tout ce que vous vouliez probablement. Pour distribuer l'événement dans l'exemple d'origine, vous pouvez le faire avec la fonction de marche, ainsi que la suppression.
La seule raison pour laquelle je pourrais imaginer que le chargement du produit avant de le supprimer serait utile peut être que les observateurs attachés à cet événement peuvent avoir besoin d'un ensemble de données complet non disponible sans charger le produit au préalable. Si tel est le cas, cela expliquerait pourquoi ils utilisent un singleton plutôt qu'un modèle pour au moins minimiser les frais généraux des objets.
la source
Je pense qu'ils le font pour déclencher l'
catalog_controller_product_delete
événement qui est utilisé par Mage_Tag.catalog_product_delete_before
oucatalog_product_delete_after
signifierait que ce n'était pas nécessaire, même si je pense. Je me demande si cet événement particulier est également utilisé pour la journalisation des actions d'administration.la source
massDelete()
action deCustomerController.php
Je pense que la suppression en masse devrait fonctionner comme supprimer un seul produit (entièrement chargé).
Car
$collection->delete()
la réponse est déjà donnée. Si vous ne déclenchez pasdeleter_before
,delete_after
je pourrais éventuellement casser certaines extensions et contourner certains observateurs utilisés dans le noyau.$collection->walk('delete')
pourrait éventuellement fonctionner, mais présente toujours l'inconvénient que les données produit ne sont pas complètes. Cela peut également interrompre les observateurs personnalisés s'ils s'appuient sur des données supplémentaires, par exemple un objet de stock.Je suppose que , si vous changez
->addAttributeToSelect('entity_id')
de->addAttributeToSelect('*')
et ajouter->setFlag('require_stock_items', true)
(pour ajouter des données de stocks aux produits) , il ne fonctionnera pas mieux que « boucle de suppression ».On dirait un mauvais style, mais je pense que c'est bon pour les deux actions de suppression en masse.
J'utilise aussi
walk()
etdelete()
pour des modèles personnalisés, mais je sais qu'il n'y a pas d'observateurs ouentity_id
c'est suffisant. Juste pour mentionner,walk()
fonctionnerait avec tous les événements utilisés dans le noyau, car ils n'utilisent que$product->getId()
, mais vous ne connaissez pas les observateurs tiers.la source