Vollständige URLs in Dataflow exportierten CSV-Dateien von Magento

Hallo Leute,

Preisvergleichsportale sind ein gutes Mittel um kontrolliert Kunden auf seinen Onlineshop los zu lassen. Dazu nötig ist aber eine CSV-Datei, die alle nötigen Daten enthält. Dies kann man erreichen, indem man sich entweder teure Plugins kauft oder die etwas komplizierte Funktion von Magento nutzt. Es gibt dabei allerdings ein kleines Problem, welches ich mit diesem Beitrag lösen möchte.

Das Problem

Das Problem auf welches ich beim Export von Produktdaten in eine CSV-Datei gestoßen bin: Wenn man die URL zu Produktbildern in der exportierten CSV-Datei haben möchte, wird nicht die vollständige URL angezeigt, also:

p/r/produkt.jpg

statt

https://website.de/media/catalog/product/p/r/produkt.jpg

Die Lösung

Die Lösungsidee klingt simpel: Ich möchte einen Prefix-String vor die Ausgabe schreiben. Klingt einfach, aber benötigt ein wenig Arbeit am Quelltext.

Zunächst muss eine eigenes MapperModel geschrieben werden. Erstellt dazu eine Datei und die fehlenden Ordner in

app/code/local/Mage/Dataflow/Model/Convert/Mapper/MyColumn.php

mit folgendem Inhalt:

<?php
class Mage_Dataflow_Model_Convert_Mapper_MyColumn extends Mage_Dataflow_Model_Convert_Mapper_Column
{
    public function map()
    {
        $batchModel  = $this->getBatchModel();
        $batchExport = $this->getBatchExportModel();

        $batchExportIds = $batchExport
            ->setBatchId($this->getBatchModel()->getId())
            ->getIdCollection();

        $onlySpecified = (bool)$this->getVar('_only_specified') === true;

        if (!$onlySpecified) {
            foreach ($batchExportIds as $batchExportId) {
                $batchExport->load($batchExportId);
                $batchModel->parseFieldList($batchExport->getBatchData());
            }

            return $this;
        }

        if ($this->getVar('map') && is_array($this->getVar('map'))) {
            $attributesToSelect = $this->getVar('map');
        }
        else {
            $attributesToSelect = array();
        }
        if ($this->getVar('prepend') && is_array($this->getVar('prepend'))) {
                $prepend = $this->getVar('prepend');
        } else {
                $prepend = array();
        }
        if (!$attributesToSelect) {
            $this->getBatchExportModel()
                ->setBatchId($this->getBatchModel()->getId())
                ->deleteCollection();

            throw new Exception(Mage::helper('dataflow')->__('Error in field mapping: field list for mapping is not defined.'));
        }

        foreach ($batchExportIds as $batchExportId) {
            $batchExport = $this->getBatchExportModel()->load($batchExportId);
            $row = $batchExport->getBatchData();

            $newRow = array();
            foreach ($attributesToSelect as $field => $mapField) {
                $prepend_2 = isset($prepend[$field]) ? $prepend[$field] : '';
                $newRow[$mapField] = isset($row[$field]) ? $prepend_2 . $row[$field] : null;
            }

            $batchExport->setBatchData($newRow)
                ->setStatus(2)
                ->save();
            $this->getBatchModel()->parseFieldList($batchExport->getBatchData());
        }

        return $this;
    }
}

Es handelt sich dabei um eine Kopie der Funktion map() aus der Datei /app/code/core/Mage/Dataflow/Model/Convert/Mapper/Column.php mit kleineren Anpassungen in Zeile 30-34 und Zeile 49-50. Im Grunde haben wir uns jetzt eine eigene Funktion geschaffen, der wir eine weitere Variable „prepend“ übergeben können, welche dann zu den Werten einer bestimmten Spalte hinzugefügt wird.

Nun müssen wir noch die Datei

app/code/core/Mage/Dataflow/Model/Convert/Profile/Collection.php

in das Verzeichnis

app/code/local/Mage/Dataflow/Model/Convert/Profile/Collection.php

kopieren und eine Zeile im Code anpassen (Achtung dazu muss ein weiterer Ordner hinzugefügt werden). Sucht nach folgender Passage im Code (müsste bei Zeile 165 sein):

 /** @var $varNode Varien_Simplexml_Element */
            foreach ($actionNode->var as $key => $varNode) {
                if ($varNode['name'] == 'map') {
                    $mapData = array();
                    foreach ($varNode->map as $mapNode) {
                        $mapData[(string)$mapNode['name']] = (string)$mapNode;
                    }
                    $container->setVar((string)$varNode['name'], $mapData);
                }  else {

und erweitert die if-Abfrage wie folgt:

 /** @var $varNode Varien_Simplexml_Element */
            foreach ($actionNode->var as $key => $varNode) {
                if ($varNode['name'] == 'map' || $varNode['name'] == 'prepend') {
                    $mapData = array();
                    foreach ($varNode->map as $mapNode) {
                        $mapData[(string)$mapNode['name']] = (string)$mapNode;
                    }
                    $container->setVar((string)$varNode['name'], $mapData);
                }  else {

Dies ist nötig, damit die Variable mit dem Namen „prepend“ auch aufgelöst wird.

Funktionsweise

Die Funktionsweise kann man am besten an einem Beispiel erklären. Ich habe dazu in der Administrationsoberfläche von Magento über System -> Import/Export -> Dataflow – Erweiterte Profile ein Profil angelegt.

2014-07-17 09_53_02-Erweiterte wiederkehrende Leistungen _ Import und Export _ System _ Magento Admi

 

Das erstellte Beispiel soll SKU (Artikelnummer), Artikelname und das Produktfoto in eine CSV exportieren.

<action type="catalog/convert_adapter_product" method="load">
	<var name="store"><![CDATA[2]]></var>
	<var name="filter/qty/from"><![CDATA[1]]></var>
	<var name="filter/qty/to"><![CDATA[999999999]]></var>
	<var name="filter/status"><![CDATA[1]]></var>
</action>

<action type="catalog/convert_parser_product" method="unparse">
	<var name="store"><![CDATA[2]]></var>
</action>

<action type="dataflow/convert_mapper_myColumn" method="map">
	<var name="map">
		<map name="sku"><![CDATA[Artikelnummer]]></map>
		<map name="name"><![CDATA[Artikelbezeichnung]]></map>
		<map name="image"><![CDATA[image]]></map>
	</var>
	<var name="prepend">
		<map name="image"><![CDATA[https://www.bier-kaufen.de/media/catalog/product]]></map>
	</var>
	<var name="_only_specified">true</var>
</action>

<action type="dataflow/convert_parser_csv" method="unparse">
	<var name="delimiter"><![CDATA[,]]></var>
	<var name="enclose"><![CDATA["]]></var>
	<var name="fieldnames">true</var>
</action>

<action type="dataflow/convert_adapter_io" method="save">
	<var name="type">file</var>
	<var name="path">var/export/bika/</var>
	<var name="filename"><![CDATA[products.csv]]></var>
</action>

Da vielleicht nicht jeder schon mit Dataflow-XML-Dateien gearbeitet hat, möchte ich dieses kleine Beispiel zerlegen und erklären. Die XML teilt sich in 5 Bereiche auf:

<action type="catalog/convert_adapter_product" method="load">
	<var name="store"><![CDATA[2]]></var>
	<var name="filter/qty/from"><![CDATA[1]]></var>
	<var name="filter/qty/to"><![CDATA[999999999]]></var>
	<var name="filter/status"><![CDATA[1]]></var>
</action>

<action type="catalog/convert_parser_product" method="unparse">
	<var name="store"><![CDATA[2]]></var>
</action>

In diesem ersten Bereich werden allgemeine Artikelfilter festgelegt. Als z.B.:

  • Es soll store=2 (der Shop den ich verwenden möchte) verwendet werden
  • Es sollen nur Artikel angezeigt werden, mit einem Lagerbestand von 1 bis 999999999
  • Es sollen nur Artikel angezeigt werden, deren status=1 (aktiv) ist.
<action type="dataflow/convert_mapper_myColumn" method="map">
	<var name="map">
		<map name="sku"><![CDATA[Artikelnummer]]></map>
		<map name="name"><![CDATA[Artikelbezeichnung]]></map>
		<map name="image"><![CDATA[BildURL]]></map>
	</var>
	<var name="prepend">
		<map name="image"><![CDATA[https://www.bier-kaufen.de/media/catalog/product]]></map>
	</var>
	<var name="_only_specified">true</var>
</action>

Hier ist nun der interessante Bereich, weswegen der ganze Aufwand getrieben wird. Die Map-Funktion mapped das Feld in der Datenbank auf das Feld in der CSV-Datei. In diesem Beispiel also:

  • Datenbankfeld  „sku“ wird auf die Spalte „Artikelnummer“ in der CSV gemapped
  • Datenbankfeld „name“ wird auf die Spalte „Artikelbezeichnung“ in der CSV gemapped
  • Datenbankfeld „image“ wird auf die Spalte „BildURL“ in der CSV gemapped

Bitte beachtet, dass hier unser eigener Mapper type=“dataflow/convert_mapper_myColumn“ statt dem normalen type=“dataflow/convert_mapper_Column“ verwendet wird.

In der zweiten Variable „prepend“ legen wir dann fest, dass das Feld „image“ noch zusätzlich den Prefix „https://www.bier-kaufen.de/media/catalog/product“ vorangestellt werden soll. Dies kann natürlich mit jedem anderen Feld auch gemacht werden.

<action type="dataflow/convert_parser_csv" method="unparse">
	<var name="delimiter"><![CDATA[,]]></var>
	<var name="enclose"><![CDATA["]]></var>
	<var name="fieldnames">true</var>
</action>

In diesem Teil der XML wird festgelegt wie die CSV-Datei aussehen soll. Also was als Separator verwendet werden soll, oder dass die erste Zeile die Spaltenbezeichnung sein soll.

<action type="dataflow/convert_adapter_io" method="save">
	<var name="type">file</var>
	<var name="path">var/export/bika/</var>
	<var name="filename"><![CDATA[products.csv]]></var>
</action>

Am Schluss legen wir dann nur noch fest, wo die Datei gespeichert wird. Wenn wir nun den Export starten, fällt eine CSV-Datei mit dem gewünschten Inhalt und einer kompletten URL beim Bild heraus.

Fazit

Ich finde es schade, dass Magento eine solche Funktionalität nicht schon von Haus aus mitbringt. Bei der Recherche einer Lösung bin ich auf viele Betroffene gestoßen. Die hier beschriebene Umsetzung ist leider auch nicht 100%-ig schön, weil wir die „Collection.php“ komplett mit einer Eigenen ersetzen. Wenn hier jemand bessere Lösungen hat, immer her damit :). Man sollte bei jedem Update von Magento zur Sicherheit prüfen, ob noch alles so funktioniert, wie gewollt.

Mir haben bei der Lösungsfindung zwei Blog- bzw. Stackoverflow-Beiträge geholfen:

  • http://stackoverflow.com/questions/21789380/magento-advanced-profiles-export-prepend-url-to-image-path um zu verstehen was geändert werden muss
  • http://www.magentocommerce.com/knowledge-base/entry/introduction-to-magento-dataflow um zu verstehen wie Dataflow arbeitet

Ich würde mich über Feedback sehr freuen 🙂 und hoffe dem einen oder anderen weitergeholfen zu haben.

6 Antworten auf „Vollständige URLs in Dataflow exportierten CSV-Dateien von Magento“

  1. Hi,

    Thanks so much for your tutorial and help! This is working for me, but not totally.

    I am finding that the first product output is getting the URL / Image prepended. However everything else is still NOT getting prepended.

    Any idea’s on as to why?

    I’m running Enterprise 1.14.0.1

    Thanks so much in advance.

    =-S

    1. Hi Steve,

      I really don’t have any idea why not. I also nerver ran into this problem. Have you checked your error-log? Seems like the loop does not work right. But this loop is not touched with the changeings. Compare original code with the code you find on my webpage. Is there any other change?

      Regards
      Timo

  2. grazie mille per la soluzione sig. Cornelius Schwandt. funziona perfettamente.

    avete qualche soluzione per esportare anche ALBERO CATEGORIE COMPLETO con nome e sottocategorie?
    magento esporta solo l’attributo „CategoryIDs“.
    quando importo il CSV in altro negozio, le categorie non vengono create. 🙁

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.