< Magento-Module deaktivieren - the right way

Magento ImportExport: Der Visibility-Bug

18.06.2012 10:35

Die Sichtbarkeit ("visility") wird beim Produktupdate gesetzt, obwohl sie in den Importdaten nicht enthalten ist? Schuld ist ein Bug in Magento, für den ich hier einen Fix bereit stelle.

Beim Produktimport mit der ImportExport-Schnittstelle von Magento (seit 1.5 CE bzw. 1.10 EE) gibt es mit manchen Attributen Probleme, wenn diese in den Importdaten nicht enthalten sind. Unter folgenden Bedingungen tritt das Problem auf:

  • Das Produkt wird aktualisiert, nicht neu angelegt
  • Eine Spalte mit dem Attribut fehlt oder ist leer für das fragliche Produkt
  • Das Attribut hat einen Standardwert (neben "visibility" betrifft das im Magento-Standard nur weniger genutzte Attribute, wie "enable_googlecheckout", "links_exist", "msrp_enabled", "msrp_display_actual_price_type" und "options_container")

Die Schnittstelle prüft in allen Fällen, in denen es für ein Attribut keine Importdaten gibt, ob es einen Standardwert gibt. Ist dies der Fall, so wird dieser gesetzt. Der Bug besteht darin, dass dies auch beim Aktualisieren eines Produktes passiert (wenn z.B. nur der Lagerbestand oder der Preis aktualisiert wird), wo das Produkt bereits einen Attributwert hat, und nicht nur beim Neuanlegen.

Der zum Beheben des Bugs zu ändernde Code befindet sich in der Klasse Mage_ImportExport_Model_Import_Entity_Product_Type_Abstract, Methode prepareAttributesForSave. Diese Methode kann wie folgt ersetzt werden:

/**
* Prepare attributes values for save: remove non-existent, remove empty values, remove static.
*
* @param array $rowData
* @return array
*/
public function prepareAttributesForSave(array $rowData)
{
$resultAttrs = array();

foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
if (!$attrParams['is_static']) {
if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) {
$resultAttrs[$attrCode] =
('select' == $attrParams['type'] || 'multiselect' == $attrParams['type'])
? $attrParams['options'][strtolower($rowData[$attrCode])]
: $rowData[$attrCode];
} elseif (null !== $attrParams['default_value']) {
if ($this->_isSkuNew($rowData['sku'])) {
$resultAttrs[$attrCode] = $attrParams['default_value'];
}
}
}
}
return $resultAttrs;
}

/**
* Check if the given sku belongs to a new product or an existing one
*
* @param $sku
* @return bool
*/
protected function _isSkuNew($sku)
{
$oldSkus = $this->_entityModel->getOldSku();
return !isset($oldSkus[$sku]);
}

Für eine saubere, updatefähige Lösung ohne Überschreiben von kompletten Core-Dateien reicht ein Rewrite leider nicht aus, da es sich um eine abstrakte Klasse handelt. Stattdessen müssen alle drei davon abgeleiteten Klassen überschreiben werden. Das passiert entweder über einen "normalen" Rewrite von

  • importexport/import_entity_product_type_simple
  • importexport/import_entity_product_type_configurable
  • importexport/import_entity_product_type_grouped

oder durch ein Überschreiben der Klassen für die einzelnen Produkttypen in der config.xml. Das sieht dann z.B. wie folgt aus:

<global>
[...]
<importexport>
<import_product_types>
<simple>fastsimpleimport/import_entity_product_type_simple</simple>
<configurable>fastsimpleimport/import_entity_product_type_configurable</configurable>
<virtual>fastsimpleimport/import_entity_product_type_simple</virtual>
<grouped>fastsimpleimport/import_entity_product_type_grouped</grouped>
</import_product_types>
</importexport>
</global>

Das Modul mit dem Model-Shortcut "fastsimpleimport" enthält dabei die neuen Klassen, die jeweils von den alten ableiten.

Anschließend muss der Cache geleert werden.

Eine entsprechende Lösung habe ich in meinem Modul AvS_FastSimpleImport, das im Prinzip ein ArrayAdapter für ImportExport ist, umgesetzt. Auch werde ich die Lösung Magento als Contributor zur Verfügung stellen.

Kommentare

Vinai Kopp, 22.06.12 11:52:
Hi Andreas,
ein guter Fund, da steckt sicherlich einige Arbeit drin. Vielen Dank für das debuggen und die Contribution!
Jürgen Graf, 19.10.12 12:40:
Hallo Andreas,
Danke für deine Überzeugung das ImportExportModul funktionierend zu bekommen..

Ich bin gerade dabei deine Änderungen an prepareAttributesForSave auszutesten - stell aber gerade fest, dass sich in meinem Fall an der Problematik nichts ändert. Habe die Klasse nach deiner Beschreibung direkt im Core geändert. Auch der Cache wurde geleert - updaten will ich testweise nur ein einziges SIMPLE Product, und auch hier nur den Preis.. allerdings nur für eine website/storeView!

Vielleicht hast du einen Hint wo ich kucken könnte.. Merci
Simon Scholl, 30.11.12 11:11:
" durch ein Überschreiben der Klassen für die einzelnen Produkttypen in der config.xml"

Das sieht mir aber eher nach Glück aus, dass es funktioniert. Die Werte werden ja in app/code/core/Mage/ImportExport/etc/config.xml gesetzt.

Beim Mergen der XMLs überschreibt eben zufällig das Modul die Core Konfiguration.
Andreas von Studnitz, 30.11.12 11:39:
Hallo Simon,
warum soll das Glück sein? Die Ladereihenfolge der Module steht fest (erst Mage_*, dann der Rest, siehe http://www.avs-webentwicklung.de/nc/blog/artikel/magento-module-deaktivieren-the-right-way.html), und damit funktioniert das zuverlässig. Das Überschreiben ist volle Absicht.
Kommentar hinzufügen












Den Code eingeben: *


* - Pflichtfeld