Behebung des Rundungsfehlers in Magento und PayPal

Rounding Error Fix for Magento and PayPal

Der Rundungsfehler wurde in Magento 1.8 behoben. Bei starker Modifikation durch die vielen Erweiterungen kann ein Update von Magento aufwändig werden, sodass die Rundungsfehlerbehebung mit einer leichtgewichtigen Erweiterung für viele Shop-Betreiber weiterhin interessant bleibt.

Rounding Error Fix for Magento and PayPal auf Magento Connect

Ursprung des Rundungsfehlers

Nach PayPal-Anforderungen müssen alle Preise als kaufmännisch auf zwei Nachkommastellen gerundete Fixpunktzahlen übermittelt werden.

Bei kaufmännischer Rundung wird beispielsweise 1,005 auf 1,01 gerundet und 1,004 auf 1,00. Offensichtlich kann dies zu einem Rundungsfehler führen, je nach dem in welchem Rechenschritt gerundet wird:

  • Rundung der Summe: 1,004 + 1,004 = 2,008 ≈ 2,01
  • Rundung der Summanden: 1,004 ? 1,00; 1,00 + 1,00 = 2,00 ≠ 2,01

Wenn bei PayPal-Zahlungen Warenkorbartikel übermittelt werden, werden ihre Nettopreise und der Mehrwertsteuerbetrag übermittelt, sodass PayPal auf seiner Seite den Brutto-Gesamtbetrag berechnet. Durch die Umrechnung der Bruttopreise kann es zu einem Rundungsfehler kommen:

  • Angenommen der Warenkorb enthält drei verschiedene virtuelle Artikel (d. h. keine Versandkosten).
  • Jeder Artikel kostet brutto 9,99 €, sodass der Brutto-Warenkorbwert 3 * 9,99 € = 29,97 € beträgt.
  • An PayPal werden Nettopreise und der Mehrwertsteuerbetrag übertragen:
    • 29,97 € / 1,19 * 0,19 = 4,7851 € ≈ 4,79
    • 9,99 € / 1,19 = 8,3949 ≈ 8,39 €
    • 3 * 8,39 € + 4,79 € = 29,96 € ≠ 29,97 €
  • Durch den Rundungsfehler ist eine Abweichung um 0,01 € entstanden!

Doch in der Praxis bekommt PayPal die Daten so übermittelt, dass der Brutto-Gesamtbetrag mit dem von Magento bis auf Cent genau übereinstimmt:

Obwohl der Bruttopreis bei allen drei Produkten gleich war, wurde in einem der drei Fälle ein anderer Nettopreis berechnet!

Die Korrektur des Rundungsfehlers findet allerdings nicht im PayPal-Modul statt, sondern bereits im Warenkorb. In der Methode Mage_Tax_Model_Sales_Total_Quote_Subtotal ::_totalBaseCalculation wird der Rundungsfehler auf den Nettopreis des nächsten Produkts in der Liste vor der Rundung aufaddiert wird. Die Rundungsfehler werden im Array _roundingDeltas von _deltaRound verwaltet. Im obigen Beispiel rechnet Magento, reduziert auf das Wesentliche, wie folgt:

  • 1. Artikel: 9,99 / 1,19 = 8,39495798
    • Kaufmännisch gerundet: 8,39
    • Rundungsfehler: 8,39495798 – 8,39 = 0,00495798
  • 2. Artikel: 9,99 / 1,19 + 0,00495798 = 8,39991597
    • Kaufmännisch gerundet: 8,40
    • Rundungsfehler: 8,39991597 – 8,40 = -0,000084034
  • 3. Artikel: 9,99 / 1,19 – 0,000084034 = 8,39487395
    • Kaufmännisch gerundet: 8,39
    • Rundungsfehler: 8,39487395 – 8,39 = 0,00487395

Die Ergebnisse dieser Berechnung lassen sich bereits beim Befüllen des Warenkorbs beobachten. In der Datenbank sind die gerundeten und korrigierten Nettopreise beispielsweise in der Spalte base_price der Tabelle sales_flat_quote_item sichtbar.

Der Ursprung des Rundungsfehlers ist also nicht das PayPal-Modul. Wir haben beobachtet, dass beim Auftreten des Rudnungsfehlers dieser von dem Mehrwertsteuerbetrag ausgeht. Die beschriebene Rudnungsfehlerkorrektur wirkt sich auch auf die Berechnung des Mehrwertsteuerbetrags aus.

Bisherige Lösungsansätze

Im Web gibt es zahlreiche Berichte und Lösungsansätze für die Behebung des Rundungsfehlers. In allen Fällen wird die Rundungsgenauigkeit in Mage_Core_Model_Store::roundPrice von zwei auf vier Nachkommastellen erhöht.

Diese Anpassung verursacht einen Rundungsfehler an anderen Stellen, dem durch weitere Anpassungen entgegengewirkt werden muss. In einigen Lösungen werden Formulardaten des PayPal-Moduls durch Addieren oder Subtrahieren von 0.01 vor dem Absenden manipuliert.

Keines dieser Ansätze behebt den Rundungsfehler vollständig und für alle Konstellationen von Produktpreisen und Steuereinstellungen. Dennoch danken wir allen Authoren, die ihre Lösungen veröffentlicht haben, denn sie haben uns bei der Entwicklung dieser Erweiterung sehr geholfen!

Behebung des Rundungsfehlers mit Xonu_RoundingErrorFix

Auch unsere Erweiterung überschreibt Mage_Core_Model_Store::roundPrice und manipuliert dort die Rundungsgenauigkeit.

Dabei wird abhängig von der aufrufenden Klasse entschieden, auf wieviele Nachkommastellen gerundet wird: Für Aufrufe, die von der Klasse Mage_Tax_Model_Sales_Total_Quote_Subtotal ausgehen wird auf zwei, für alle restlichen Aufrufe auf vier Nachkommastellen genau gerundet.

Wie oben gezeigt, ist diese Klasse in die Berechnung der kaufmännisch gerundeten Nettopreise mit Rundungsfehlerkorrektur involviert. Erhöhung der Rundungsgenauigkeit auf vier Nachkommastellen stört diese Rechenlogik erheblich!

PayPal verwendet für die Verarbeitung der Zahlung die Methode Mage_Sales_Model_Order_Payment::registerCaptureNotification. U. a. hier wird der der „Betrugsverdacht“ erkannt, wenn _isCaptureFinal einen false zurückgibt:

public function registerCaptureNotification($amount)
{
    ....

    if (!$invoice) {
        if ($this->_isCaptureFinal($amount)) {
            $invoice = $order->prepareInvoice()->register();
            $order->addRelatedObject($invoice);
            $this->setCreatedInvoice($invoice);
        } else {
            $this->setIsFraudDetected(true);
            $this->_updateTotals(array('base_amount_paid_online' => $amount));
        }
    }

    ....
}

In Mage_Sales_Model_Order_Payment::_isCaptureFinal wird der über PayPal gezahlter Betrag, übermittelt in der IPN, mit dem Gesamtbetrag der Bestellung verglichen:

protected function _isCaptureFinal($amountToCapture)
{
    $amountToCapture = $this->_formatAmount($amountToCapture, true);
    $orderGrandTotal = $this->_formatAmount($this->getOrder()->getBaseGrandTotal(), true);
    if ($orderGrandTotal == $this->_formatAmount($this->getBaseAmountPaid(), true) + $amountToCapture) {
        if (false !== $this->getShouldCloseParentTransaction()) {
            $this->setShouldCloseParentTransaction(true);
        }
        return true;
    }
    return false;
}

Dabei wird die Methode Mage_Sales_Model_Order_Payment::_formatAmount verwendet, sodass wenn roundPrice auf vier statt zwei Nachkommastellen rundet, die Beträge nicht übereinstimmen könnten, weil der von PayPal übermittelte Betrag immer auf zwei Nachkommastellen gerundet ist:

protected function _formatAmount($amount, $asFloat = false)
{
     $amount = Mage::app()->getStore()->roundPrice($amount);
     return !$asFloat ? (string)$amount : $amount;
}

Die Erweiterung überschreibt auch diese Methode, sodass sie auf zwei Nachkommastellen genau rundet:

protected function _formatAmount($amount, $asFloat = false)
{
    $amount = round($amount,2);
    return !$asFloat ? (string)$amount : $amount;
}

Eine weitere Methode im PayPal-Modul scheitert an der Erhöhung der Nachkommastellen in roundPrice: Mage_Paypal_Model_Api_Abstract::_filterAmount und liefert in bestimmten Fällen ein Ergebnis, welches wiederum einen Rundungsfehler verursacht:

protected function _filterAmount($value)
{
    return sprintf('%.2F', $value);
}

Auch diese Methode überschreiben wir:

protected function _filterAmount($value)
{
    return sprintf('%.2F', round($value, 2));
}

In dem Update der Erweiterung werden zusätzlich noch die PayPal-Formulardaten vor der Übermittlung mit den Bestelldaten verglichen. Bei einer Abweichung wird der Rundungsfehler durch gezielte Anpassung der Formulardaten korrigiert.

Rounding Error Fix for Magento and PayPal

Rounding Error Fix for Magento and PayPal auf Magento Connect

SSL in der XAMPP-Konfiguration von Apache aktivieren

Bei Entwicklungsarbeiten rund um die Umschaltung von http:// auf https:// in Magento sollte in der lokalen Entwicklungsumgebung SSL aktiviert werden. In XAMPP-Konfiguration von Apache ist SSL als Voreinstellung deaktiviert.

Davon ausgehend, dass virtuelle Hosts und lokale Domains verwendet werden, soll diese Anleitung bei der Aktivierung von SSL unterstützen.

Einrichtung virtueller Domains

Lokale Domains werden in der hosts-Datei verwaltet. Diese Datei befindet sich unter Windows unter:

%SystemRoot%\system32\drivers\etc\hosts

Die hosts-Datei könnte die folgende Zeile enthalten:

127.0.0.1           local.xonu.de

Zum Bearbeiten der hosts-Datei muss der verwendete Texteditor als Administrator gestartet werden!

Einrichtung des virtuellen Hosts

In der XAMPP-Konfiguration von Apache werden virtuelle Hosts in der folgenden Datei verwaltet:

xampp\apache\conf\extra\httpd-vhosts.conf

Überprüfen Sie, dass die Einbettung von httpd-vhosts.conf in der Hauptkonfigurationsdatei httpd.conf (im Verzeichnis xampp\apache\conf) enthalten und aktiv ist, d. h. nicht mit Raute am Zeilenanfang auskommentiert wurde:

Include "conf/extra/httpd-vhosts.conf"

Die Konfiguration des virtuellen Hosts für unverschlüsselte Verbindungen könnte wie folgt aussehen:

<VirtualHost *:80>
    DocumentRoot "/htdocs/local.xonu.de/httpdocs"
    ServerName local.xonu.de
    ErrorLog "logs/xonu-error.log"
    ErrorLog "logs/xonu-custom.log"
</VirtualHost>

Für verschlüsselte Verbindungen muss ein weiterer Block für den Port 443 eingefügt werden:

<VirtualHost *:443>
    DocumentRoot "/htdocs/local.xonu.de/httpdocs"
    ServerName local.xonu.de
    SSLEngine on
    SSLCertificateFile "conf/ssl.crt/server.crt"
    SSLCertificateKeyFile "conf/ssl.key/server.key"
</VirtualHost>

Wie auch das Log-Verzeichnis, wird der Pfad zu den Zertifikat-Dateien relativ zum Hauptverzeichnis von Apache \xampp\apache angegeben. Diese Dateien sind in XAMPP bereits enthalten.

In früheren Versionen von Apache musste noch in xampp\apache\conf\extra\httpd-vhosts.conf nach NameVirtualHost *:80 die neue Zeile mit NameVirtualHost *:443 eingefügt werden. In der aktuellen und zukünftigen Versionen ist dies nicht mehr nötig.

Beim Start von Apache im XAMPP Control Panel könnten folgende Fehler gemeldet werden:

[Apache]     Error: Apache shutdown unexpectedly.
[Apache]     This may be due to a blocked port, missing dependencies,
[Apache]     improper privileges, a crash, or a shutdown by another method.
[Apache]     Check the "/xampp/apache/logs/error.log" file
[Apache]     and the Windows Event Viewer for more clues

In diesem Fall muss noch das SSL-Modul von Apache aktiviert werden. Dazu bitte in httpd.conf die folgende Zeile mit Strg+F finden und die Kommentar-Raute entfernen:

LoadModule ssl_module modules/mod_ssl.so

Sichere Verbindung für ein virtuelles Magento-Verzeichnis erzwingen

Als Nachtrag zum Artikel über Router und Controller in Magento, kann das Laden eines Controllers über eine verschlüsselte Verbindung (sofern konfiguriert) erzwungen werden, sodass von http auf https automatisch weitergeleitet wird:

<config>
<!-- ... -->
    <frontend>
        <routers>
            <eindeutiger_router_name>
                <use>standard</use>
                <args>
                    <module>Xonu_Test</module>
                    <frontName>start</frontName>
                </args>
            </eindeutiger_router_name>
        </routers>

        <!-- Verschlüsselte Verbindung erzwingen: -->
        <secure_url>
            <eindeutiger_url_name>/start</eindeutiger_url_name>
        </secure_url>
    </frontend>
</config>

Mit dieser Konfiguration leitet http://local.xonu.de/start automatisch auf https://local.xonu.de/start weiter.

Magento 1.8: Rundungsfehler behoben!

Seit dem 25. Oktober kann die finale Version von Magento 1.8 (Community Edition) heruntergeladen werden.

Unter den Neuerungen in Magento 1.8 wird die Behebung des Rundungsfehlers zurecht im ersten Punkt unter den Highlights erwähnt.

Der Rundungsfehler in früheren Versionen war inzwischen so bekannt geworden, dass Trusted Shops bei der Abnahme absichtlich einen Warenkorb mit Rundungsfehler zu kombiniert versucht. Die Abnahme wird verweigert bis der Rundungsfehler behoben wurde.

Der bereits im Warenkorb sichtbare Rundungsfehler pflanzt sich konsequent fort bis zur Erfassung der Bestellung im Backend:

Mit 14,99 EUR kommt Magento 1.8 zum richtigen Ergebnis!

Auf Magento Connect haben wir eine Erweiterung veröffentlicht, die den Rundungsfehler in Magento 1.7 und PayPal behebt: Rounding Error Fix for Magento and PayPal

Eigenen Code in einem beliebigen Unterverzeichnis von Magento ausführen

Eine beliebte Frage in einer Magento-Schulung für Entwickler: Wie kann unter einer frei gewählten URL in Magento eigener PHP-Code ausgeführt werden?

Die Magento-Installation ist z. B. unter http://domain.de/shop/ erreichbar. Unter http://domain.de/shop/beliebiges-unterverzeichnis/landingpage.html soll ein PHP-Code ausgeführt werden und „Hallo Welt!“ ausgeben.

Eigenes virtuelles Unterverzeichnis in Magento

Der Router bestimmt das (virtuelle) Unterverzeichnis, bei dessen Aufruf der damit verknüpfte Controller ausgeführt wird.

Hierfür muss eine Magento-Erweiterung mit drei Dateien erstellt werden, damit wir beim Aufruf des Verzeichnisses http://127.0.0.1/custom/ unseren eigenen Code ausführen können:

app/etc/modules/Xonu_Controller.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Xonu_Controller>
            <active>true</active>
            <codePool>local</codePool>
        </Xonu_Controller>
    </modules>
</config>

Durch diese Konfiguration wird der Code der Erweiterung im Verzeichnis app/code/<codePool>/<Namespace>/<Name>, also app/code/app/code/local/Xonu/Controller erwartet. Die Namen Xonu und Controller sind frei gewählt.

Um Fehler zu vermeiden, sollten der Namensraum und der Name der Erweiterung stets nur einen Großbuchstaben am Anfang enthalten (z. B. statt MyController, sollte Mycontroller verwendet werden).

app/code/local/Xonu/Controller/etc/config.xml

<?xml version="1.0"?>
<config>
    <global> <!-- Der Router wird im Frontend und Backend berücksichtigt. -->
    </global>

    <admin> <!-- Der Router wird im Frontend und Backend berücksichtigt. -->
    </admin>

    <frontend> <!-- Der Router wird nur im Frontend berücksichtigt. -->
        <routers>
            <xonu_controller_router> <!-- Frei gewählter Name des XML-Knotens. -->
                <use>standard</use>
                <args>
                    <!-- Hauptverzeichnis/Klassenprefix der Erweiterung. -->
                    <module>Xonu_Controller</module>
                    <!-- Frei gewählter Name für das erste virtuelle Unteverzeichnis. -->
                    <frontName>custom</frontName>
                </args>
            </xonu_controller_router>
        </routers>
    </frontend>
</config>

app/code/local/Xonu/Controller/controllers/IndexController.php

<?php

class Xonu_Controller_IndexController extends Mage_Core_Controller_Front_Action {

    public function indexAction() {
        echo "Hallo Welt!";
    }
}

Die Benennungen des Unterverzeichnisses controllers, klein geschrieben, der Datei IndexController.php und des darin definierten Klassennamens Xonu_Controller_IndexController und ihre Ableitung von einer bestimmten Core-Klasse ist von Magento vorgeschrieben.

Nach dem der Konfigurationscache aktualisiert wurde, falls aktiv, wird bereits das registrierte Unterverzeichnis funktionieren:

http://127.0.0.1/custom

Hallo Welt!

Gleiches gilt für die virtuelle Unterverzeichnisse /custom/index und /custom/index/index. Das erste Unterverzeichnis index bezieht sich auf den Namen des Controllers IndexController.php. Das zweite Unterverzeichnis index bezieht sich auf den Präfix im Namen der darin definierten Methode indexAction(). Für die folgenden Aufrufe bekommen wir also die gleiche Ausgabe:

http://127.0.0.1/custom/index
http://127.0.0.1/custom/index/index

Durch Einführung einer weiteren Methode mit einem beliebigen Präfix, gefolgt vom Schlüsselnamen Action, können wir weitere virtuelle Unterverzeichnisse für die URL http://127.0.0.1/custom/index definieren:

app/code/local/Xonu/Controller/controllers/IndexController.php

<?php

class Xonu_Controller_IndexController extends Mage_Core_Controller_Front_Action {

    public function indexAction() {
        echo "Hallo Welt!";
    }

    public function anotherAction() {
        echo "Xonu_Controller_IndexController::anotherAction";
    }

}

Die zweite Methode anotherAction() kann durch die URL http://127.0.0.1/custom/index/another aufgerufen werden und liefert die folgende Ausgabe:

http://127.0.0.1/custom/index/another

Xonu_Controller_IndexController::anotherAction

Das erste Unterverzeichnis wird durch den Präfix im Namen der Controller-Klasse und der php-Datei bestimmt. Weiteres Unterverzeichnis /custom/second können wir durch die Definition einer weiteren Controller-Klasse registrieren:

app/code/local/Xonu/Controller/controllers/SecondController.php

<?php

class Xonu_Controller_SecondController extends Mage_Core_Controller_Front_Action {

    public function indexAction() {
        echo "Xonu_Controller_SecondController::indexAction";
    }

    public function anotherAction() {
        echo "Xonu_Controller_SecondController::anotherAction";
    }

}

Nun bekommen wir folgende Ausgaben:

http://127.0.0.1/custom/second
http://127.0.0.1/custom/second/index

Xonu_Controller_SecondController::indexAction

Noch tiefere Unterverzeichnisse können als Parameter über spezielle Methoden von Magento ausgelesen werden:

app/code/local/Xonu/Controller/controllers/DeeplinkController.php

<?php

class Xonu_Controller_DeeplinkController extends Mage_Core_Controller_Front_Action {

    public function evaluateAction() {
        echo '<pre>';
        echo "Xonu_Controller_DeeplinkController::indexAction\n";

        echo "\n".'$_GET:'."\n";
        print_r($_GET);

        echo "\n".'Mage::app()->getRequest()->getParams():'."\n";
        print_r(Mage::app()->getRequest()->getParams());
    }
}

Beispielaufrufe und -Ausgaben:

http://127.0.0.1/custom/deeplink/evaluate/param1/value1/param2/value2

Xonu_Controller_DeeplinkController::indexAction

$_GET:
Array
(
)

Mage::app()->getRequest()->getParams():
Array
(
    [param1] => value1
    [param2] => value2
)

GET-Parameter können direkt über die GET-Variable als auch über die Methoden von Magento ausgelesen werden. Letzteres ist durch interne Filter sicherer und deshalb der Arbeit direkt mit der GET-Variable vorzuziehen:

http://127.0.0.1/custom/deeplink/evaluate/param1/value1/?param2=value2

Xonu_Controller_DeeplinkController::indexAction

$_GET:
Array
(
    [param2] => value2
)

Mage::app()->getRequest()->getParams():
Array
(
    [param1] => value1
    [param2] => value2
)

http://127.0.0.1/custom/deeplink/evaluate/?param1=value1&param2=value2

Xonu_Controller_DeeplinkController::indexAction

$_GET:
Array
(
    [param1] => value1
    [param2] => value2
)

Mage::app()->getRequest()->getParams():
Array
(
    [param1] => value1
    [param2] => value2
)

Das eigene Verzeichnis beliebig gestalten

Bei der Gestaltung virtueller Unterverzeichnisse ist man also an bestimmte Regeln von Magento gebunden. Durch das URL Rewrite Verwaltung lassen sich diese Verzeichnisstrukturen hinter einer beliebigen URL verbergen.

Im Backend, unter Katalog > URL Rewrite Verwaltung legen wir einen neuen URL Rewrite an. Im obersten Menü wählen wir Benutzerdefiniert aus und füllen das Formular aus:

Magento URL Rewrite Management

  • Store: Hier ist das StoreView gemeint, in dem die URL gelten soll. Es gibt keine Möglichkeit die URL mit einem Eintrag für alle StoreViews festzulegen; in diesem Fall muss die URL für jeden StoreView einzeln angelegt werden. Beachten Sie auch, dass die Spalte store_id in der Tabelle core_url_rewrite, wo die URL Rewrites verwaltet werden, niemals die 0 enthält.
  • ID Pfad: Neben der automatisch vergebenen Id, muss dem Rewrite eine zusätzliche interne Id vergeben werden. Dies kann ein beliebiger eindeutiger Wert sein und wird in der Spalte id_path gespeichert.
  • Anfragepfad: Das ist die URL, die frei gestaltet werden kann. Sie kann z.B. beliebige virtuelle Unterverzeichnisse enthalten. Die URL wird ohne führenden Slash und ohne Domainnamen eingegeben. Für die URL http://domain.de/beliebiges-unterverzeichnis/landingpage.html, wird als Anfragepfad beliebiges-unterverzeichnis/landingpage.html angegeben. Befindet sich die Magento-Installation in einem Unterverzeichnis, z.B. http://domain.de/shop, enthält der Anfragepfad dieses Unterverzeichnis nicht.
  • Zielpfad: Dies ist das Verzeichnis unter dem ein Controller aufgerufen wird, welches an bestimmte Vorgaben geknüpft ist und deshalb nicht ganz frei gestaltet werden kann. Auch dieses Verzeichnis wird, wie Anfragepfad, ohne führenden Slash und ohne Domain (und Shop-Verzeichnis) eingetragen: custom/deeplink/evaluate/param1/value1/param2/value2.
  • Weiterleitung: Für unseren Zweck ist Nein die optimale Einstellung. Wird die Weiterleitung aktiviert, wird der Nutzer zu dem Zielpfad weitergeleitet und sieht ihn in der Adressleiste des Browsers.

Nach der Speicherung des Rewrites bekommen wir unter der neuen URL die folgende Ausgabe:

http://127.0.0.1/beliebiges-unterverzeichnis/landingpage.html

Xonu_Controller_DeeplinkController::indexAction

$_GET:
Array
(
)

Mage::app()->getRequest()->getParams():
Array
(
    [param1] => value1
    [param2] => value2
)

Wir können weitere GET-Parameter übergeben, doch diese können ausschließlich über die GET-Variable ausgelesen werden:

http://127.0.0.1/beliebiges-unterverzeichnis/landingpage.html?param3=value3

Xonu_Controller_DeeplinkController::indexAction

$_GET:
Array
(
    [param2] => value3
)

Mage::app()->getRequest()->getParams():
Array
(
    [param1] => value1
    [param2] => value2
)

XML, XPath, PHP: DOMXPath::query() funktioniert nicht?

Möchte man XML-Daten dynamisch einlesen und den Code möglichst generisch gestalten, ist DOMXPath::query() sehr hilfreich: Die Pfade können in einer separaten Datenquelle als Zeichenketten hinterlegt werden.

Liest man Tutorials auf php.net und in anderen Blogs, bleibt oft ein für die Praxis wichtiges Detail unerwähnt. Es folgt ein kurzes Beispiel einer XML-Antwort:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns="http://api.xonu.de" id="123">
    <name>xonu</name>
</company>

Das Auslesen der id über XPath funktioniert problemlos:

$xpathStr = '@id';

// $xmlStr <-- XML-Antwort
$domDoc = new DOMDocument();
$domDoc->loadXML($xmlStr);
$domXpath = new DOMXPath($domDoc);

$domNode = $domXpath->query($xpathStr);

if($domNode->length) {
    print $domNode->item(0)->nodeValue;
} else {
    print 'Nichts gefunden.';
}

Die Ausgabe dieses Skripts ist der Wert des id-Attributes des Wurzelknotens: 123. Nun wollen wir den Wert des Unterknotens name auslesen:

$xpathStr = 'name';

Doch das funktioniert nicht, die Ausgabe ist „Nichts gefunden.“. Verschiedene äquivalente Formulierungen des Pfades für das id-Attribut funktionieren, aber nicht für den Unterknoten name:

$xpathStr = '@id'; // Ausgabe: 123
$xpathStr = '//@id'; // Ausgabe: 123
$xpathStr = '/*/@id'; // Ausgabe: 123
$xpathStr = 'name'; // Ausgabe: Nichts gefunden.
$xpathStr = '//name'; // Ausgabe: Nichts gefunden.
$xpathStr = '/*/name'; // Ausgabe: Nichts gefunden.

Durch Entfernen des xmlns-Attributs kommt man dem Problem auf die Spur: Dieses Attribut fehlt oft in den Tutorial-Beispielen, ist aber in der Praxis meistens vorhanden! Nach dem Entfernen von xmlns=“http://api.xonu.de“ aus der XML-Antwort, funktionieren alle oben aufgelisteten X-Pfade.

Der in xmlns angegebener XML-Namespace, also der Namensraum, muss explizit registriert werden:

$xmlNs = $domDoc->documentElement->lookupnamespaceURI(null);
$domXpath->registerNamespace('x', $xmlNs);

Jetzt können wir den Unterknoten name über die folgenden X-Pfade abfragen:

$xpathStr = 'x:name';
$xpathStr = '//x:name';
$xpathStr = '/*/x:name';

Es könnten allerdings bereits hunderte X-Pfade definiert worden sein (z. B. im Profil eines XML-Adapters), die nun alle überarbeitet werden müssten, um sich auf einen bestimmten Namensraum zu beziehen. Um dies zu vermeiden, kann das xmlns-Attribut programmatisch gelöscht werden:

$xmlNs = $domDoc->documentElement->lookupnamespaceURI(null);
// $domXpath->registerNamespace('x', $xmlNs);
$domDoc->documentElement->removeAttributeNS($xmlNs, '');

Nun würden alle X-Pfade ohne eines expliziten Bezugs auf einen Namensraum funktionieren.

Beispielcode zum Ausprobieren herunterladen (domxpath-example.php)

Magento-Konfiguration als XML-Baum anzeigen, debuggen, analysieren

Magento fügt alle XML-Konfigurationsdateien aus den Verzeichnissen app/etc, app/etc/modules und config.xml aus den etc-Verzeichnissen einzelner Module zu einem einzigen XML-Baum zusammen. Um Magento besser zu verstehen und bei der Entwicklung eigener Module kann es hilfreich sein, einen Einblick in diesen Magento-Konfigurationsbaum zu bekommen.

getXmlString() als Methode von getConfig() gibt eine speichersparende Ausgabe ohne Einschübe und Zeilenumbrüche. Sie ist für Entwicklungsumgebungen geeignet, die XML visualisieren können (z. B.  in Eclipse: Strg+O in der Code-Ansicht oder die Design-Ansicht):

$configXML = Mage::app()->getConfig()->getXmlString();

asNiceXml() gibt geordneten XML-Code mit Zeilenumbrüchen und hierarchischen Einschüben aus:

$configXML = Mage::getConfig()->getNode()->asNiceXml();

Wenn der XML-Konfigurationsbaum auf einen bestimmten XPath reduziert werden soll, kann dieser als String-Parameter von getNode() übergeben werden. Der nachfolgende Beispiel listet alle registrierten Helper auf:

$configXML = Mage::getConfig()->getNode('global/helpers')->asNiceXml();

Die Umfangreiche Ausgabe kann in einer Datei gespeichert werden. Der nachfolgende Code speichert die XML-Ausgabe im Log-Verzeichnis von Magento:

file_put_contents(Mage::getBaseDir('log').DS.'magento_config.xml', $configXML);

 

Hauptknoten des Magento-Konfigurationsbaums

  • config
    • modules – Modulkonfiguration (codePool, active, depends)
    • global – Definitionen, die global gelten (models, resources, blocks, helpers, events, fieldsets, template, cache…)
    • frontend – Definitionen, die nur im Frontend gelten (events, secure_url, routers, translate, layout)
    • adminhtml – Definitionen, die nur im Backend gelten (events, global_search, translate, layout)
    • default – Konfiguration für alle Store Views, Voreinstellungen
    • stores – Konfiguration für die Store Views (default, admin, [store_code])
    • websites – Konfiguration für die Websites
    • admin – Backend-Router und Fieldsets (routers, fieldsets, attributes)
    • install – Magento-Installation (nicht Modul-Installation)
    • crontab – Cronjobs innerhalb des Magento-Systems
    • varien – Altlasten von Varien
    • phoenix – Spezielle Definitionen für MoneyBookers
    • [custom] – Spezielle Definitionen für eigene Module möglich

Postfix-Konfiguration: www-data im Return-Path ändern

Im Quellecode einer Email gibt es deutlich mehr Informationen als das Email-Programm dem Nutzer anzeigt. Eines dieser verborgenen Informationen ist der Return-Path, der von Spam-Filtern gerne ausgewertet wird. Passt dieser nicht, wird die Email in den Spam-Ordner verschoben.

Auch kann es beim Zielserver dazu führen, dass er die Email mit dem „falschen“ Return-Path mit der Fehlermeldung „501: Sender address must contain a domain“ gar nicht annimmt.

In der Voreinstellung eines Debian/Ubuntu Servers wird als Return-Path www-data@xonu.de verwendet. Im Magento-Shop wird meistens eine andere Email-Adresse als Absender eingetragen, z.B. bestellungen@mein-shop.de. Die Tatsache, dass der Return-Path-Wert sich von from From-Wert unterscheidet, kann dazu führen, dass Emails in den Spam-Ordner verschoben werden.

Durch Anpassung der Postfix-Konfiguration auf dem Server kann der Return-Path geändert werden:

1. Im Verzeichnis /etc/postfix/ erstellen Sie die Datei mit dem Namen canonical und folgendem Inhalt:

www-data   bestellungen@mein-shop.de

2. Führen Sie auf der Kommandozeile den folgenden Befehl aus:

postmap hash:/etc/postfix/canonical

Überprüfen Sie, dass im Verzeichnis /etc/postfix/ die Datei canonical.db erstellt wurde.

3. Fügen Sie der Postfix-Konfigurationsdatei /etc/postfix/main.cf folgende Zeile hinzu:

canonical_maps = hash:/etc/postfix/canonical

Beachten Sie, hier wird die Datei ohne Erweiterung angegeben. Postfix sucht beim Start automatisch die Datei mit der Erweiterung .db.

4. Starten Sie Postfix neu:

/etc/init.d/postfix restart

Schicken Sie eine Test-Email (Bestellung aus dem Backend manuell versenden) und überprüfen Sie den Return-Path im Quellcode der Email:

Der obige Screenshot zeigt die Ecke des Email-Fensters von Thunderbird, daneben einen Auszug aus dem Email-Quelltext: Der From-Wert gleicht dem Wert von Return-Path.

Optimierung der Button-Lösung von German Setup

Entsprechend der Button-Lösung, ändert German Setup den letzten Schritt im Checkout so ab, dass die Häckchen für Bestellbedingungen (AGB und Widerrufbelehrung) oberhalb der Tabelle mit Warenkorbwaren angezeigt werden. Neben jeder Bestellbedingung wird dabei der Link [Anzeigen] in eckigen Klammern angezeigt.

Die aktuelle Button-Lösung von German Setup ist in zwei Bereichen verbesserungswürdig:

  • Die Bestellbedingungen sollen über den Link [Anzeigen] nicht nur mit Linksklick, sondern mit der mittleren Maustaste oder über das Browser-Kontextmenü gezielt im neuen Tab geöffnet werden können.
  • Der Link sollte in die Checkbox-Beschriftung integriert werden:

Absichtliches Öffnen im neuen Tab oder Fenster

Um sicher zu gehen, dass das Shop-Fenster geöffnet bleibt, versuchen einige Kunden die Bestellbedingungen mit der mittleren Maustaste oder über das Browser-Kontextmenü (Rechtsklick) gezielt im neuen Fenster zu öffnen, weil aus der Gestaltung des Links [Anzeigen] nicht zu erkennen ist, dass er bereits ein neues Fenster öffnet.

Mit dem JavaScript-Befehl window.Open wird nach dem Klick ein neues Fenster öffnet (OnClick-Ereignis), in dem die Inhalte der Bestellbedingungen angezeigt werden:

Der href-Parameter des Links ist auf „#“ gesetzt. Der Nachteil dieser Lösung ist, dass wenn ein Nutzer versucht, das Fenster absichtlich in einem neuen Tab zu öffnen, ob über das Browser-Kontextmenü oder durch den Klick mit der mittleren Maustaste bzw. mit dem  Mausrad, öffnet sich nicht der Inhalt der Bestellbedingung, sondern (verwirrenderweise) der erste Schritt des Checkouts.

Durch eine einfache Anpassung des Links kann dieses Problem behoben werden: Das target-Attribut des Links wird dem Fensternamen (zweites Parameter von window.Open) gesetzt. Das href-Attribut des a-Tags wird dem URL-Ziel des Fensters gleichgesetzt.

Vereinfacht, ist der Original-Link wie folgt aufgebaut:

<a href="#"
   OnClick="window.open(
     '{Ziel-URL}',
     '',
     '{Fensterparameter}')">
[Anzeigen]</a>

Damit das Öffnen im Neuen Tab funktioniert, muss der Link wie folgt umgestaltet werden:

<a href="{Ziel-URL}"
   target="{Fenster-ID}"
   OnClick="window.open(
     '{Ziel-URL}'),
     '{Fenster-ID}',
     '{Fensterparameter}'">
[Anzeigen]</a>

Beim Auslösen des OnClick-Ereignisses, öffnet das sich wie gewohnt das Fenster. Beim Klick mit der mittleren Maustaste zum Öffnen des Links im neuen Fenster, öffnet sich so tatsächlich ein neues Browser-Tab mit den erwarteten Inhalten der Bestellbedingung.

Zum Realisieren dieser Optimierung, kopieren Sie die folgende Datei unter Beibehaltung der Verzeichnisstruktur in Ihr Theme-Verzeichnis (mehr zum Erstellen und Anpassen von Magento-Themes):

app/design/frontend/base/default/germansetup/checkout/onepage/agreements.phtml

Und ersetzen Sie die Zeile ~52 mit dem modifizierten Link:

<a
   href="<?php echo $this->getUrl('germansetup/frontend/agreements', array('id' => $_a->getId())) ?>"
   target="agreement-<?php echo $_a->getId()?>"
   onclick="window.open(
            '<?php echo $this->getUrl('germansetup/frontend/agreements', array('id' => $_a->getId())) ?>',
            'agreement-<?php echo $_a->getId()?>',
            'width=600,height=600,left=0,top=0,location=no,status=yes,scrollbars=yes,resizable=yes').focus(); return false;">
   <?php echo $this->__('[Show]') ?>
</a>

Integration in die Checkbox-Beschriftung

Der Link [Anzeigen] ist zwar eine sichere und einfache Lösung, bringt aber keine schöne Optik mit sich. Als eine optisch schönere Lösung wäre die Integration des Links zur Anzeige von Inhalten einzelner Bestellbedingungen direkt in die Checkbox-Überschrift, sodass beispielsweise „Allgemeine Geschäftsbedingungen“ zum Link wird:

Die zusätzliche Anforderung wäre die weiterhin einfache Pflege des Inhalts der Checkbox-Überschrift ganz ohne HTML-Code: Der zu verlinkende Bereich des Texts wird mit eckigen Klammern markiert:

D. h. aus „Ich habe die Allgemeinen Geschäftsbedingungen gelesen und stimme diesen ausdrücklich zu.“ wird „Ich habe die [Allgemeinen Geschäftsbedingungen] gelesen und stimme diesen ausdrücklich zu.“

Der einfachheit halber wird dazu die gleiche Datei (als Kopie im eigenen Theme-Verzeichnis) wie im vorherigen Optimierungsvorschlag modifiziert:

app/design/frontend/base/default/germansetup/checkout/onepage/agreements.phtml

Die neue Datei sieht wie folgt aus (sie enthält die vorher beschriebene Optimierung ebenfalls):

<?php
$helper = $this->helper('germansetup');
?>

<?php if (!$this->getAgreements()) return; ?>

<form action="" id="checkout-agreements" onsubmit="return false;">
<ol>
<?php foreach ($this->getAgreements() as $_a): ?>
    <?php
        $checkBoxLabel = $_a->getIsHtml() ? $_a->getCheckboxText() : $this->htmlEscape($_a->getCheckboxText());
        $matches = array();
        if(preg_match('/\[([^\]]*)\]/i', $checkBoxLabel, $matches)) {
            $linkMarkup = $matches[0];
            $linkLabel  = $matches[1];
            $clickableLink = '<a href="'.$this->getUrl('germansetup/frontend/agreements', array('id' => $_a->getId())).'" target="agreement-'.$_a->getId().'" onclick="window.open(\''.$this->getUrl('germansetup/frontend/agreements', array('id' => $_a->getId())).'\', \'agreement-'.$_a->getId().'\', \'width=600,height=600,left=0,top=0,location=no,status=yes,scrollbars=yes,resizable=yes\').focus(); return false;">'.$linkLabel.'</a>';
            $checkBoxLabel = str_replace($linkMarkup, $clickableLink, $checkBoxLabel);
        }
    ?>
    <li>
        <p>
            <input type="checkbox" id="agreement-<?php echo $_a->getId()?>" name="agreement[<?php echo $_a->getId()?>]" value="1" title="<?php echo $this->htmlEscape($_a->getCheckboxText()) ?>" />
            <label for="agreement-<?php echo $_a->getId()?>"><?php echo $checkBoxLabel; ?></label>
        </p>
    </li>
<?php endforeach ?>
</ol>
</form>

Der reguläre Ausdruck \[([^\]]*)\] extrahiert den Text in der eckigen Klammer, mit und ohne Klammer verteilt auf zwei Gruppen $matches[0] und $matches[1], und ersetzt ihn durch den a-Tag mit der gleichen sichbaren Bezeichnung, die als zweite Gruppe $matches[1] ohne der eckigen Klammer extrahiert wurde.

 

Weiterlesen:

Symbolische Links funktionieren nicht, .htaccess funktioniert nicht

Fehlerbeschreibung

Die Startseite von Magento lässt sich laden, aber viele oder alle Unterseiten, darunter Produkt- und Kategorie-Links, führen zu einem Fehler.

Ursachen und Lösungen

Magento-Testumgebung mit Beispieldaten

  • Wenn Sie eine Magento-Testumgebung mit Beispieldaten einrichten, werden die Produkt- und Kategorie-Links nicht sofort funktionieren. Das Problem kann durch Neuindizierung behoben werden. Gehen Sie zu System > Index-Verwaltung und Klicken Sie dort auf Neuaufbau in der Zeile „Katalog URL Rewrites“. Danach sollten alle Links im Magento-Shop mit Beispieldaten funktionieren.

Typische Flüchtigkeitsfehler

  • Ist die .htaccess im Hauptverzeichnis von Magento vorhanden? Hin und wieder gibt es Situationen, in den versteckte Dateien beim Komprimieren des Verzeichnisses nicht berücksichtigt werden. Folglich sind sie nach dem Enpacken nicht vorhanden.
  • Wurde die .htaccess umbenannt? Die Datei muss mit einem Punkt beginnen und in Kleinbuchstaben geschrieben sein.

Konfiguration von Apache

Nach dem Umzug in eine neue Serverumgebung kann es sein, dass die symbolische Links von Magento nicht mehr funktionieren. Dies hängt oft mit der Konfiguration von Apache zusammen.

In der Standardkonfiguration von Apache in Ubuntu Server 10.04 LTS ist die Unterstützung von .htaccess über AllowOverride None deaktiviert. Zusätzlich ist das Apache-Modul mod_rewrite standardmäßig nicht geladen.

  • Ist die Verwendung von .htaccess erlaubt? Bei Ubuntu Server 10.04 LTS befindet sich die betroffene Konfigurationsdatei von Apache unter /etc/apache2/sites-available/default:
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /var/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
</Directory>

Vorausgesetzt Ihre Magento-Installation befindet sich im Verzeichnis /var/www/ (oder einem Unterverzeichnis), wird die Nutzung von .htaccess mit dem zweiten Befehl AllowOverride None verboten. Ändern Sie die entsprechende Zeile zu AllowOverride All:

<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /var/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all
</Directory>

Starten Sie Apache z. B. mit /etc/init.d/apache2 restart neu, um die geänderte Konfiguration zu laden.

  • Ist der Apache-Modul rewrite aktiviert? In diesem Fall sind weitere Informationen in der Ausgabe von phpinfo() auffindbar: STRG+F nach mod_rewrite. Versuchen Sie andernfalls das Modul mit a2enmod zu aktivieren und Apache neu zustarten:
a2enmod rewrite
/etc/init.d/apache2 restart

Neue Version von Fixed Gross Price bietet höhere Kompatibilität

Fixed Gross Price auf Magento Connect

Die Verbesserungsvorschläge, die von Entwicklern und Shop-Betreibern mir zugeschickt wurden, haben wir in die neue Version von Fixed Gross Price einfließen lassen.

Die neue Version fixiert nun den Bruttopreis auch bei Bestellungen, die im Backend erstellt werden, vielen Dank an Matthias.

Die Erweiterung ist nun auch kompatibel mit Magento 1.5, wo die Methode hasQuote() noch nicht deffiniert war, vielen Dank an Benjamin für den Hinweis.

Bei einer bestimmten Shop-Konfiguration wurde der Bruttopreis im Katalog nicht fixiert, wenn der Kunde eingeloggt war. Dieses Problem wurde nun ebenfalls behoben.

Die neue Version kann nun von Magento Connect installiert werden.

Weiterlesen: Zum ursprünglichen Artikel über Fixed Gross Price.

Bedingte Umleitung über .htaccess (Conditional Redirect)

Magento prüft die Domain, unter der der Shop aufgerufen wurde, und leitet automatisch zu der eingestellten Basis-URL weiter. Für die Weiterleitung wird das Magento-Framework geladen und Datenbankabfragen abgesetzt. Um dies zu vermeiden und die Weiterleitung zu beschleunigen, ist es empfehlenswert, sie über die .htaccess-Datei im Hauptverzeichnis von Magento umzusetzen.

Mit Strg+F die Zeile RewriteEngine on suchen und danach folgende Zeilen einfügen:

RewriteCond %{HTTP_HOST} !^mein-shop.de [NC]
RewriteRule .* http://mein-shop.de [R=301,L]

Die negierte Bedingung sorgt dafür, dass nicht nur von www.mein-shop.de weitergeleitet wird, sondern von jeder beliebigen Domain unter der der Shop, erreichbar ist. Also z.B. auch meinshop.de und www.meinshop.de (ohne Bindestrich).

Bei Verwendung eines Full-Page-Caches wird die Weiterleitung über die .htaccess-Datei sogar erforderlich, da die statische Seite, die ein solcher Cache ausliefert, keine Weiterleitungsfunktion enthält.

MySQL-InnoDB-Speicher automatisch freigeben

Bei der Entwicklung mit Magento ist der Magento-Cache oft deaktiviert. Damit die Testumgebung trotzdem möglichst schnell bleibt, lohnt es sich, diese in einer RAM-Disk oder zumindest auf einer SSD-Festplatte laufen zu lassen. Doch bei schnellem Speicher ist jedes Gigabyte immer noch sehr kostbar!

Magento verwendet die InnoDB-Engine. Diese Engine bringt viele Vorteile mit sich gegenüber der MyISAM-Engine, aber auch einen Nachteil: Durch Löschen einer InnoDB-Tabelle oder eines ganzen Datenbankschemas mit InnoDB-Tabellen wird der Speicher innerhalb des Tabellenraums zwar teilweise freigegeben, aber die Größe der Tabellenraum-Datei (typische Speicherorte: mysql/data/ibdata1 oder /var/lib/mysql/ibdata1) nicht automatisch reduziert. Der Speicher auf der Festplatte bleibt belegt.

Durch mehrfache Wiederherstellungen von Datenbanken, Importe, Tabellen für Abfragetests und sonstige Experimente wächst der InnoDB-Tabellenraum auf der Festplatte immer weiter, sodass irgendwann der Speicher einer RAM-Disk aufgebraucht und kein vernünftiges Arbeiten mehr möglich ist. Eine Defragmentierung des InnoDB-Tabellenraums ist nicht vorgesehen. Um den Speicher auf der Festplatte freizugeben, muss der Tabellenraum komplett neu angelegt werden:

  1. Alle Datenbanken werden exportiert (z.B. mit mysqldump),
  2. alle Datenbankschemata mit InnoDB-Tabellen gelöscht (DROP SCHEMA …),
  3. der Datenbankserver wird heruntergefahren,
  4. Tabellenraumdateien werden gelöscht (in der Standardkonfiguration sind das die Dateien drei Dateien ibdata1, ib_logfile0, ib_logfile1 im Verzeichnis mysql/data)
  5. der Datenbankserver gestartet,
  6. alle gelöschten Datenbankschemata angelegt (CREATE SCHEMA …),
  7. Tabellenstrukturen und Daten von Exports wiederhergestellt.

Damit in Zukunft der Festplattenspeicher bereits beim Löschen von InnoDB-Tabellen freigegeben wird, müssen die Tabellendaten in jeweils einem eigenen Tabellenraum gespeichert sein. Hierzu kann vor dem Start des Datenbankservers der Parameter innodb_file_per_table in die my.ini bzw. my.cnf im mysqld-Block eingetragen werden:

[mysqld]
...
innodb_file_per_table
...

Die Datei ibdata1 wird u. a. für diverse Verwaltungsdaten weiterhin benötigt. Die eigentlichen Nutzdaten und Indizes werden nun aber in *.ibd-Dateien im jeweiligen Schema-Verzeichnis abgelegt (z.B. catalog_category_product.ibd).

Weitere Informationen zum Per-Table Tablespace in der MySQL-Referenz.

Leere Seite (Blank Page) oder HTTP-Fehler 500 beim Speichern von PayPal-Einstellungen im Backend von Magento

Beispielfehlermeldung

Der Fehler äußert sich darin, dass beim Versuch PayPal-Einstellungen im Backend zu speichern, in FireFox eine leere Seite angezeigt wird; auch der Quellcode der Seite ist leer. In Chrome wird auf den „HTTP-Fehler 500 (Internal Server Error)“ hingewiesen:

Leere Seite (Blank Page) beim Speichern von PayPal-Einstellungen

Auch im Internet Explorer wird „HTTP 500 Internet Serverfehler“ unauffällig in der Tab-Überschrift angezeigt, sonst aber die Meldung: „Die Website kann diese Seite nicht anzeigen.“ ausgegeben.

In FireFox ist der Fehler im FireBug sichtbar:

Die Rückkehr zu den Einstellungen ist über die Zurück-Schaltfläche des Browsers möglich: die PayPal-Einstellungen wurden nicht gespeichert.

Grundsätzlich ist aber das Speichern von Einstellungen im Backend möglich und der Shop funktioniert auch sonst einwandfrei (u. a. Kunden können sich registrieren, Checkout funktioniert, Bestellungen können aufgegeben werden).

Der Fehler kann auf anderen Unterseiten im Shop auftreten. Bei aktivierter Fehleranzeige könnte der folgende Fehler gemeldet werden: „Call to undefined function mcrypt_module_open()“. Die Fehlerbehebung ist in diesem Fall gleich.

Ursache

Typischerweise hat der Shop einen Serverumzug hinter sich, d. h. er wurde in einer neuen Serverumgebung von einem Backup wiederhergestellt.

Der Server ist nicht vollständig für den Betrieb eines Magento-Shops eingerichtet; in den meisten Fällen fehlt das für die Datenverschlüsselung zuständige PHP-Modul mcrypt, das in vorkonfigurierten Serverumgebungen (z. B. LAMPP) oft nicht standardmäßig enthalten ist.

Fehlerbehebung

Der Fehler kann durch Installation oder Aktivierung von mcrypt behoben werden. Wenn es sich um einen Ubuntu- oder Debian-Server handelt, kann mcrypt mit dem folgenden Bash-Befehl installiert werden (Administrator-Rechte werden vorausgesetzt):

apt-get install php5-mcrypt

In einigen Fällen muss das Modul noch explizit mit dem folgenden Befehl aktiviert werden:

php5enmod mcrypt

Anschließend kann der Webserver neugestartet werden:

/etc/init.d/apache2 restart

Weiterlesen: PayPal-Einstellungen in Magento 1.7.x

Gesamte MySQL-Datenbank nach einer Zeichenkette durchsuchen

In SQL/MySQL gibt es keinen Befehl, mit dem die gesamte Datenbank durchsucht werden könnte. Denkbar wäre eine Prozedur, die alle Tabellen mit Spalten relevanter Datentypen (u. a. VARCHAR, CHAR, TEXT) durchsucht.

Einfacher geht es mit dem folgenden Trick: Datenbank wird als SQL-Skript exportiert/gesichert. Danach wird die SQL-Datei in einem Texteditor geöffnet und durchsucht.

Export der Datenbank ist über die Kommandozeile möglich:

mysqldump -uBenutzername Datenbankname > /var/tmp/Dateiname.sql

Ein Editor wie EditPad und viele Hex-Editoren würden dabei auch mit großen SQL-Dateien gut zurecht kommen.

Einrichtung von Währungen in Magento

In Magento gibt es eine Basiswährung, in der alle Produkt- und Servicepreise im Backend angegeben werden. Der Preis in Basiswährung wird nach dem aktuellen Kurs umgerechnet und dem Kunden im Frontend angezeigt.

Verfügbare Währungen

Die Einstellung verfügbarer Währungen ist in System > Konfiguration > Allgemein: Currency Setup möglich. Die standardmäßig angezeigte Währung muss dabei auch in der Liste erlaubter Währungen aktiviert sein.

Werden mehrere Einträge in der Liste erlaubter Währungen aktiviert, wird u. a. in der Kategorieübersicht ein Währungsumschalter angezeigt, sodass der Kunde wählen kann, in welcher Währung die Preise angezeigt werden.

Hinweis: Durch einen einfachen Linksklick in dieser Liste wird nur die zuletzt angeklickte Währung ausgewählt. Alle anderen Währungen (die sich außerhalb des sichtbaren Bereichs befinden) werden dabei abgewählt. Halten Sie die Steuerungstaste gedrückt, wenn Sie mehrere Währungen auswählen.

Wenn der Währungsumschalter nicht angezeigt werden soll, muss die standardmäßig angezeigte Währung als einzige in der Liste erlaubter Währungen ausgewählt werden.

Umrechnungskurse

Ein manueller Abgleich ist durch Klick auf die Schaltfläche Import in System > Währungen verwalten möglich. Hinter WebserviceX verbirgt sich ein kostenfreier Service, der den aktuellen Umrechnungskurs zurückgibt. Beim Abgleich der Kurse wird beispielsweise die folgende URL aufgerufen:

http://www.webservicex.net/CurrencyConvertor.asmx/ConversionRate?FromCurrency=EUR&ToCurrency=USD

Sofern in System > Konfiguration > Allgemein: Einrichten der Währung > Einstellungen für terminierten Import aktiviert ist, wird der Umrechnungskurs automatisch aktualisiert. Voraussetzung dafür ist, dass die cron.php im Hauptverzeichnis von Magento regelmäßig aufgerufen wird (Einrichten von Cronjobs in Magento).

Währungsumschalter deaktivieren

Sofern mehrere Währungen in einem StoreView erlaubt sind, wird standardmäßig ein Währungsumschalter in der linken Spalte angezeigt.

Sein Block ist in der folgenden Layout-Datei definiert:

frontend/base/default/layout/directory.xml

Mit dem entsprechenden Befehl in der eigenen Layout-Datei lässt sich der Währungsumschalter ausblenden:

<remove name="currency" />

Mehrere Währungen anbieten

Die Basiswährung, in der Magento alle Preise berechnet, lässt sich nur im Website-Scope festlegen. Alle Preise werden in der Basiswährung berechnet und zum Anzeigen in eine Fremdwährung umgerechnet. Dies hat gravierende Konsequenzen, die beim Planen eines Magento-Shops unbedingt berücksichtigt werden müssen!

Alle Produktpreise werden in der Basiswährung angegeben

Der Produktpreis in Fremdwährung lässt sich nicht direkt beeinflussen! Kostet ein Artikel 9,99 EUR, wird sein Preis entsprechend dem Kurs umgerechnet: 13,55 USD. Der Händler hat keine Möglichkeit, den Preis in USD auf z.B. 12,99 USD zu ändern.

Zahlungsmodule übermitteln den Gesamtbetrag in Basiswährung

Zahlungsmodule übermitteln den Preis oft in Basiswährung. Kauft der Kunde für 100 USD ein (das ist der Preis, der ihm im Frontend angezeigt wurde), übermittelt der Zahlungsmodul 73,68 EUR als zu zahlender Betrag, sofern EUR als Basiswährung eingestellt ist.

Tendenziell führt ein solcher Kunde sein Konto in USD. Durch Zahlungsanbieter-eigene Umrechnungskurse kann es passieren, dass der Kunde einen abweichenden Betrag zahlen muss. So wurde ihm beispielsweise im Shop 100 USD als der Gesamtwert seines Warenkorbs inkl. Versandkosten angezeigt, könnte sein Konto mit 102 USD belastet werden. Nicht jeder Kunde hat dabei Verständnis für die technischen Herausforderungen, die den abweichenden Betrag begründen.

Preise in verschiedenen Währungen frei gestalten

Um den genannten Problemen entgegen zu wirken, lässt sich der Shop in mehrere Websites aufteilen. So kann für jede Website eine eigene Basiswährung festgelegt werden.

Um den Preis in der jeweiligen Währung frei gestalten zu können, muss unter System > Katalog > Preis > Katalogpreis-Gültigkeit noch eingestellt werden, dass der Produktpreis nur für eine Website Gültigkeit hat.

Der Händler kann dem Produkt nun zwei Preise unabhängig voneinander zuweisen: 12,99 USD und 9,99 EUR. Nun muss für jedes Produkt der Preis unabhängig voneinander in der jeweiligen Währung gepflegt werden.

Beim aktuellen Umrechnungskurs ist das gleiche Produkt in USD in diesem Beispiel günstiger. Aufmerksame Kunden werden dies mit der Zeit herausfinden und in Foren und anderen Kanälen darüber berichten. Um dem entgegen zu wirken, könnte für die jeweilige Website die Liste der belieferten Länder entsprechend gewählt werden.

ACHTUNG: Bei dieser Konfiguration ist Vorsicht geboten, denn Magento (selbst in der heute aktuellen Version 1.7.0.2) behandelt die Preise wie sonstige Produktattribute; wird für die aktuelle Webseite kein Wert hinterlegt, wird der Standardwert (intern: store_id=0) ohne Umrechnung verwendet. Wird als Produktpreis beispielsweise 9,99 EUR festgelegt, wird im US-Shop 9,99 USD als Preis verwendet, sofern USD-Preis hinterlegt wurde! Gleiche Logik gilt für Sonderpreise und Staffelpreise.

Memcached und Magento: Unknown number format type ‚boolean‘. Format “ must be a valid number format string.

Magento lässt sich mit Memcached – genügend Arbeitsspeicher und Administratorrechte auf dem Server vorausgesetzt – recht einfach beschleunigen.

Beispielfehlermeldung

Eine Fehlermeldung, mit der ich auf einer frischen Installation von Magento 1.7.0.2 konfrontiert wurde:

Memcached und Magento: Unknown number format type 'boolean'. Format '' must be a valid number format string.

Ursache

Die Fehlermeldung wurde beim Aufruf vom Backend, später auch im Frontend, ausgegeben. Durch Abschaltung von Memcache konnte ich sichergehen, dass der Fehler damit zusammenhängt.

Fehlerbehebung

Durch einfügen der Zeile prefix mit dem frei gewählten alphanumerischen Wert shop1 in der Konfiguration von Memcache, wurde der Fehler in meinem Fall behoben:

<cache>
   <prefix>shop1</prefix>
   <backend>memcached</backend>
   <memcached>
        <servers>
            <server>
                <host><![CDATA[127.0.0.1]]></host>
                <port><![CDATA[11211]]></port>
                <persistent><![CDATA[1]]></persistent>
            </server>
        </servers>
        <compression><![CDATA[0]]></compression>
        <cache_dir><![CDATA[]]></cache_dir>
        <hashed_directory_level><![CDATA[]]></hashed_directory_level>
        <hashed_directory_umask><![CDATA[]]></hashed_directory_umask>
        <file_name_prefix><![CDATA[]]></file_name_prefix>
    </memcached>
</cache>

Hintergrundinformationen

Memcached verwendet die Prefix-Einstellung bei der Identifikation von gecachten Daten. Wenn auf dem gleichen Server mehrere Magento-Shops laufen, ist es wichtig, durch verschiedene Prefix-Werte sicherzustellen, dass sie nicht auf gecachte Daten des jeweils anderen Shops zugreifen.

Memcached-Konfiguration des zweiten Shops:

<cache>
   ...
   <prefix>shop2</prefix>
   ...
</cache>

Kategorien in Magento importieren, Kategorien programmatisch erstellen

Von Haus aus bietet Magento keinen Import oder Export von Kategorien. Mit dieser kostenfreien Erweiterung ist der Import von Kategorien aus einer CSV-Datei möglich.

Doch weder diese noch andere Erweiterungen auf Connect bieten alle nötigen Freiheiten. So habe ich keine Extension gefunden, bei der die interne Nummer einer Kategorie angegeben werden kann. Dies ist wichtig, um bei einer automatischen Synchronisierung mit einem externen System bei der Zuweisung von Produkten zu einer Kategorie direkt die externen Kategorienummern zu verwenden (und sich die zusätzlichen Joins und Zuweisungstabellen zu ersparen).

Dabei ist eine programmatische Erstellung einer Kategorie in Magento recht einfach und übersichtlich:

$objCategory = Mage::getModel('catalog/category');
$objCategory->setStoreId(0);

// $entity_id = 100;
// $path = '1/3';

$aryCategory = array(
    'entity_id' => $entity_id,
    'path' => $path.'/'.$entity_id,
    'name' => 'Bezeichnung',
    'is_active' => 1,
    'include_in_menu' => 1,
    'url_key' => 'bezeichnung',
    'is_anchor'  => 0,
    'position'  => 1,
    'display_mode' => 'PRODUCTS_AND_PAGE',
    'created_at' => date("Y-m-d H:i:s", time()),
    'level' => $level = count(explode('/', $path))
);

$objCategory->addData($aryCategory);

try {
    $objCategory->save();
} catch (Exception $e) {
    print $e->getMessage();
}

Es lohnt sich, die Tabelle catalog_category_entity zu analysieren, um zu verstehen wie Magento die Kategorien auf der Datenbankebene verwaltet. Einige der Attribute, die in dem o. g. Beispiel gesetzt werden, findet man direkt in dieser Tabelle als Spalten wieder.

In einigen Quellen stellte ich fest, dass die Spalte level (Ebenentiefe) keinen Wert bekommt. Dieser wird von Magento leider nicht automatisch gesetzt! Wird die Ebenentiefe nicht angegeben, können tiefere Ebenen im Backend im Produkteditor nicht aufgeklappt werden.

Der Bau des eigenen Pfads path ist sehr komfortabel, wenn man zum Importieren von Kategorien auf verschiedenen Ebenen eine rekursive (selbst aufrufende) Funktion als Lösung wählt.

Fixed Gross Price Extension: Gleiche Bruttopreise für alle Kunden

Fixed Gross Price auf Magento Connect

Magento arbeitet mit fixierten Nettopreisen, sodass der Händler stets den gleichen Nettopreis bekommt. Kommt ein Kunde aus einem Land, in dem die Mehrwertsteuer höher ist als die im eigenen Land, muss er einen höheren Bruttopreis zahlen, sofern im Shop der korrekte Mehrwertsteuersatz eingestellt wurde.

Das nachfolgende Diagramm zeigt den Rechenweg von Magento zur Bestimmung des Bruttopreises. Unter jedem Block ist ein Beispielwert zur besseren Erklärung angegeben:

Ab einer bestimmten Lieferschwelle ist der Händler verpflichtet, dem Kunden die Mehrwertsteuer seines Landes auszuweisen. Die neue Extension Fixed Gross Price fixiert den Bruttopreis, sodass alle Kunden den gleichen Preis zahlen und dabei die korrekte Mehrwertsteuer ausgewiesen bekommen.

Die Berechnung des Bruttopreises in Magento und das Funktionsprinzip der Fixed Gross Price Extension ist in diesem Artikel ausführlich dokumentiert.

SSL-Sicherheitswarnung im Magento-Warenkorb: Informationen werden über eine unverschlüsselte Verbindung gesendet

Nach dem ich die Verschlüsselung im Frontend aktiviert habe, wird der Warenkorb über https:// geladen. Klicke ich dort z. B. auf die Schaltfläche „Warenkorb aktualisieren“, bekomme ich in Firefox die folgende Fehlermeldung:


Deutsche Version:

Obwohl diese Seite verschlüsselt ist, werden die von Ihnen eingegebenen Informationen über eine unverschlüsselte Verbindung gesendet und können leicht von Dritten gelesen werden.

Sollen diese Informationen wirklich gesendet werden?

Englische Version:

Although this page is encrypted, the information you have entered is to be sent over an unencrypted connection and could easily be read by a third party.

Are you sure you want to continue sending this information?

Abgesehen davon, dass diese Warnung beim Kunden Misstrauen erweckt, wird man nach dem Klick auf „Fortsetzen“ auf die Startseite des Shops weitergeleitet!

Problembehebung

Der Warenkorb in Magento, erreichbar unter der URL /checkout/cart/, ist nicht zum Laden über verschlüsselte Verbindung vorgesehen.

Eine benutzerdefinierte Warenkorb-Schaltfläche hat den Warenkorb aber mit dem folgenden Link aufgerufen und Verschlüsselung erzwungen:

echo Mage::getUrl('checkout/cart', array('_secure'=>true));

Da die unsichere Basis-URL keine Verschlüsselung verwendet, haben alle Unterformulare des Warenkorbs (Warenkorb leeren, Warenkorb aktualisieren, Gutschein-Code, Versandkosten) eine unverschlüsselten Action-URL, die die o. g. Warnung verursacht.

Durch Anpassung des Warenkorb-Links wurde das Problem in diesem Fall behoben:

echo Mage::getUrl('checkout/cart');

Weiterlesen: SSL in der XAMPP-Entwicklungsumgebung aktivieren

German Setup: inkl. MwSt. anpassen, zzgl. Versand wird nicht angezeigt, Code existiert bereits

German Setup ist eine großartige Erweiterung für deutsche Shops. Im Gegensatz zu Market Ready Germany installiert sie keine unnötigen Zusatzerweiterungen und führt Anpassungen rücksichtsvoll und erst auf Anfrage durch, wobei diese im Vorfeld in System > German Setup > Setup konfiguriert werden können.

In German Setup empfohlene Erweiterungen

Im Menü System > German Setup > Empfohlene Erweiterungen gibt es eine kleine Liste von empfohlenen Erweiterungen, die allerdings für Magento ab Version 1.7.0.0 reduziert werden darf:

  • Zahlung per Vorkasse ist in Magento 1.7 bereits enthalten. Für die Bankverbindung und Hinweise steht ein mehrzeiliges Textfeld zur Verfügung, der auch HTML-Tags aufnehmen kann. Die Information kann somit beliebig gestaltet werden.
  • Die Region-Auswahl kann für bestimmte Länder in System > Konfiguration > Allgemein: Allgemein > Bundesland Optionen ausgeschaltet werden. Die Erweiterungen wie No Region werden damit überflüssig, zumal eine landabhängige Abschaltung der Region-Auswahl damit gar nicht möglich wäre.

zzgl. Versand wird nicht angezeigt

Nach der Einrichtung von GermanSetup werden zwei CMS-Seiten für Versand- und Zahlungsinformationen angelegt.

Wer sich vom Deutschen Händlerbund e. V. beraten lässt, bekommt den Vorschlag, diese zwei Seiten zu einer zu kombinieren. Die neue Seite hat beispielsweise den URL-Bezeichner zahlung-und-versand:

GermanSetup bemerkt die Löschung der Seite mit Versandinformationen und blendet den Link aus dem Mehrwertsteuer-Hinweis aus.

Erst wenn unter System > Konfiguration > Katalog: Katalog > Preis: CMS-Seite für Versandkosten eine CMS-Seite gewählt wurde, wird der Hinweis: „zzgl. Versandkosten“ wieder eingeblendet.

Nach dieser Anpassung muss noch der Konfigurationscache aktualisiert werden: System > Cache-Verwaltung > Cache-Art: Konfiguration.

Anzeige von „Inkl. MwSt.“ anpassen

German Setup zeigt Prozente abhängig von der Steuerklasse, die einem Produkt zugewiesen ist und dem für den StoreView eingestelltem Standard-Zielland einstellbar in System > Konfiguration > Verkäufer: Steuer > Standard Ursprung für Steuerberechnung. Die deutsche Übersetzung des Block-Titels ist ziemlich missverständlich:

Die korrekte Mehrwertsteuer bekommt der Kunde spätestens im letzten Schritt im Checkout angezeigt, abhängig von seiner Rechnungsanschrift als Voreinstellung. Ist die MwSt. des Ziellandes höher, muss der Kunde einen höheren Produktpreis zahlen, denn Magento arbeitet mit festen Netto-Preisen.

Möchte man die Prozentzahl ausblenden, ist die Bearbeitung der Übersetzung am einfachsten. In der Übersetzung wurde jeweils der erste Platzhalter %s in den Wert des Dummy-Attributs des Link-Tags verschoben:

"Incl. Tax, plus <a href=""%s"">Shipping Cost</a>","Inkl. MwSt., zzgl. <a dummy=""%s"" href=""%s"">Versand</a>"
"Incl. %s Tax, plus <a href=""%s"">Shipping Cost</a>","Inkl. MwSt., zzgl. <a dummy=""%s"" href=""%s"">Versand</a>"
"Excl. %s Tax, plus <a href=""%s"">Shipping Cost</a>","Zzgl. MwSt., zzgl. <a dummy=""%s"" href=""%s"">Versand</a>"
"Incl. Tax","Inkl. MwSt."
"Incl. %s Tax","Inkl. MwSt."
"Excl. %s Tax","Zzgl. MwSt."

Der Platzhalter darf nicht entfernt werden, da die Prozentzahl sonst als URL zu der Versandkostenseite eingefügt werden würde.

Diese Zeilen können in locale/de_DE/translate.csv im eigenen Theme-Verzeichnis gespeichert werden. Die Originalzeilen sind in der folgenden Datei zu finden:

app/locale/de_DE/FireGento_GermanSetup.csv

Wer nach einer flexibleren Lösung sucht, kann die folgende Template-Datei modifizieren:

app/design/frontend/base/default/template/germansetup/price_info.phtml

Code existiert bereits.

German Setup erstellt jeweils drei Steuerregeln in Steuer > Steuerregeln verwalten für Endkunden und Unternehmen:

  • Produkte mit 19% MwSt.
  • Produkte mit 7% MwSt.
  • Versand mit 19% MwSt.

Möchte man ein neues Land einführen (z. B. Norwegen), muss man nach dem Anlegen in Steuer > Steuerzonen und -sätze zwangsläufig auch die Steuerregeln bearbeiten. Doch beim Speichern einer Steuerregel kommt die Fehlermeldung:

Fehlermeldung: Code existiert bereits.

Der Grund dafür ist, dass die Regel mit dem gleichen Namen „Produkte mit 19% MwSt.“ bereits für Unternehmen existiert. Beim programmatischen Erstellen von Steuerregeln werden offenbar gleiche Namen zugelassen, nicht aber beim Bearbeiten im Backend.

Die einfache Lösung: Die Regeln sollen verschiedene Namen bekommen. Statt „Produkte mit 19% MwSt.“ wird z. B. „Produkte mit 19% MwSt. (Endkunden)“ eingetragen.

E-Mail-Templates

German Setup richtet auch E-Mail-Templates ein, die den rechtlichen Anforderungen in Deutschland entsprechen. Unter anderem werden beispielsweise in der Bestellbestätigung AGB, Widerrufsrecht und Impressum angezeigt.

Während Magento in der Voreinstellung E-Mail-Vorlagen aus dem Verzeichnis app\locale verwendet (z.B. app\locale\de_DE\template\email), speichert German Setup die Vorlagen in der Datenbank ab. Sie können dann unter System > Transaktions-E-Mails verwaltet werden. Zu beachten ist hier, dass sich die Verwaltung mehrsprachiger Vorlagen über die Datenbank sehr umständlich gestaltet, da Magento hier keinen StoreView-Filter bietet. Leider verknüpft German Setup die Vorlagen aus der Datenbank in der Standardeinstellung, sodass diese für anderssprachige StoreViews manuell aufgelöst und auf Vorlagen aus dem Locale-Ordner verwiesen werden müssen.

Die Überprüfung einzelner Templates und ihrer Verknüpfungen lohnt sich, denn bei den vielen Vorlagen geht den Autoren das eine oder andere Detail verloren. So wurde in der zum Zeitpunkt des Artikels aktuellen Version die Vorlage für „Neue Rechnung“ an Stelle von „Neue Bestellung“ unter System > Konfiguration > Verkäufe: Verkaufs-E-Mails eingetragen.

Und in der Vorlage für „Neues Konto“ wird der Kunde mit „herzlich willkommen in unserem Shop zum Thema Geld- und Energiesparen.“ begrüßt, statt den Platzhalter zu verwenden: „herzlich willkommen bei {{var store.getFrontendName()}}.“, wie es in anderen Vorlagen der Fall ist.

Weiterlesen: Optimierung der Button-Lösung von German Setup

Nach Eingabe der Versandadresse geht es im Checkout nicht weiter: Bug in DHL Intraship

Für den Versand mit DHL Intraship gibt es eine kostenfreie Extension, die die Versandabwicklung automatisiert (Ausführlicher Artikel zur Versandautomatisierung über DHL Intraship).

Im Download-Manager wird für DHL Intraship die Version 12.09.10 (stable) angezeigt. Zum aktuellen Zeitpunkt (22.11.2012) ist das die neueste Version:

Dieser Bugreport basiert auf Erfahrung mit Magento 1.7.0.0 Community Edition mit dem Base-Theme. Außer DHL Intraship sind keine Erweiterungen installiert, die Checkout modifizieren.

In dieser Version von DHL Intraship gibt es einen recht versteckten Bug, der den Kunden daran hindert, seine Bestellung abzuschließen!

Bugreport

Möchte ein registrierter Kunde an eine von der Rechnungsanschrift abweichende Adresse versenden, wird er im nächsten Schritt Versandinformationen eingeben müssen. Hier integriert die DHL Intraship Extension seit kurzem die Option zum Versand an eine Packstation.

Ist diese Option deaktiviert, sieht das Formular wie gewohnt aus. Hat sich der Kunde eingeloggt und Aktiviert „Versand an Packstation“ im Schritt „Versandinformationen“ bekommt er die Liste von Packstationen in der Nähe seiner Anschrift:

Geht der Kunde als Gast zur Kasse und möchte an eine von der Rechnungsanschrift abweichende Adresse versenden, funktioniert „Versand an Packstation“ nicht: die oben gezeigte Auswahlliste wird nicht eingeblendet, wenn die Option aktiviert wird.

Unabhängig davon, ob „Versand an Packstation“ aktiviert ist oder nicht, bringt der Klick auf „Fortsetzen“ den Kunden nicht zum nächsten Schritt. Beim Klick passiert gar nichts: keine Ladeanzeige, keine Fehlermeldungen:

 

Abhilfe

DHL Intraship ist ein Paket bestehend aus zwei Extensions (siehe app/etc/modules):

  • Dhl_Intraship.xml
  • Dhl_Account.xml

Die Extension Dhl_Intraship.xml implementiert die Versandautomatisierung über das Backend von Magento.

Die Extension Dhl_Account.xml integriert u. a. die Option „Versand an Packstation“ in Checkout. Deaktivieren Sie die Extension Dhl_Account.xml, um das Problem im Checkout zu beheben. (Artikel: Extensions in Magento deaktivieren)

Die Option „Versand an Packstation“ verschwindet aus dem Checkout, aber damit auch die Blockierung des Schritts „Versandinformationen“.

Magento Table Rates richtig konfigurieren

In dem offiziellen Artikel Table Rate Shipping gibt es eine gute Anleitung zum Einrichten von Table Rates. Einige wichtige Dinge werden dort allerdings nicht erwähnt oder gerne überlesen. Als Folge falscher Konfiguration funktioniert Magento Table Rates nicht richtig und berechnet nicht die gewünschten Versandkosten. In diesem Artikel fasse ich zusammen, worauf man bei der Konfiguration von Table Rates achten sollte.

Zur Vereinfachung wollen wir nur nach Deutschland versenden. Ab 100 EUR ist der Versand kostenfrei, sonst beträgt er 5 EUR inkl. MwSt. Die korrekte CSV-Konfiguration für diese Vorgabe sieht wie folgt aus:

Country,Region/State,"Zip/Postal Code","Order Subtotal (and above)","Shipping Price"
DEU,*,*,84.0000,0.0000
DEU,*,*,0.0000,5.0000
  • Download und Upload der CSV-Konfiguration ist nur im Website-Bereich möglich.
    Schalten Sie den Umschalter für Konfigurationsbereiche auf die gewünschte Website um. Erst jetzt kann die aktuelle Konfiguration heruntergeladen und nach Anpassung hochgeladen werden.
  • Zwischensumme als Netto-Betrag, Versandbetrag als Brutto-Betrag angeben.
    In diesem Beispiel muss als Zwischensumme für den kostenlosen Versand 84 EUR statt 100 EUR angegeben werden (84 EUR = 100 EUR / 1,19), aber 5 EUR als Versandkosten für Beträge darunter. Beachte auch die Einstellung in:
    System > Konfiguration > Verkäufe: Steuer > Berechnung: Versandkosten
  • Die Reihenfolge der Zeilen ist wichtig.
    Je höher die Zwischensumme, desto weiter oben muss sie stehen. Vertauscht man die beiden Zeilen, wird in diesem Beispiel der kostenlose Versand nicht angeboten!
  • Das Trennzeichen zwischen den Spalten muss ein Komma sein.
    Bearbeitung der CSV-Datei mit einem deutschsprachigen Excel führt tendentiel dazu, dass beim Speichern Semikolons als Trennzeichen eingesetzt werden. Deshalb sollte die Datei mit einem einfachen Texteditor bearbeitet werden.
  • Die Länder-Codes werden nach ISO 3166 ALPHA-3 angegeben.
    Länder-Codes nach ISO 3166 in Wikipedia

Und wem die Konfigurationsmöglichkeiten dieser Versandmethode nicht ausreichen, sollte auf die kostenfreie Multiple Table Rates Extension umsteigen.

Attributwerte von Kategorien und Produkten über SQL-Abfragen zugreifen

Hin- und wieder ist ein Zugrif auf Attributwerte von Kategorien und Produkten über SQL-Abfragen notwendig. Solche Abfragen sind bei Fehlersuche und -Korrektur hilfreich, aber auch beim Import und Export von Daten.

Aufgrund des flexiblen Entity-Attribute-Value-Modells (EAV-Modells) von Magento sind aus dem Backend bekannte Attributcodes (z. B. price) und Attributwerte (z. B. 9.90) auf verschiedene Tabellen verteilt, die über JOIN miteinander verknüpft werden müssen.

Die nachfolgende SQL-Abfragen sind ausschließlich lesende Abfragen und demonstrieren, wie einzelne Produkt- und Kategorieattribute zugegriffen werden können.

Produkte

Produktnamen und URL-Keys

Namen, URL-Keys und andere Werte von einzeiligen Textfeldern sind in der Tabelle catalog_product_entity_varchar gespeichert. Die Spalte für Nutzdaten hat den Datentyp VARCHAR(255), welcher aufgrund der UTF8-Kodierung 1-3 Byte pro Zeichen und 1 Byte für die Kodierung der Länge verwendet.

Produktnamen:

SELECT entity_id, sku, eav_vc.value as name, store_id FROM catalog_product_entity
JOIN catalog_product_entity_varchar eav_vc USING(entity_id)
JOIN eav_attribute eav ON (eav_vc.entity_type_id=eav.entity_type_id AND eav_vc.attribute_id=eav.attribute_id
AND eav.attribute_code = 'name');

URL-Key (diese Abfrage unterscheidet sich von der vorherigen nur im Attributcode):

SELECT entity_id, sku, eav_vc.value as name, store_id FROM catalog_product_entity
JOIN catalog_product_entity_varchar eav_vc USING(entity_id)
JOIN eav_attribute eav ON (eav_vc.entity_type_id=eav.entity_type_id AND eav_vc.attribute_id=eav.attribute_id
AND eav.attribute_code = 'url_key');

Preise

Magento speichert Preise in der Tabelle catalog_product_entity_decimal. Um Rundundsfehler zu vermeiden, wird für Preise ein Festpunkt- einem Fließpunkt-Datentyp vorgezogen. Die Nutzspalte hat den Datentyp DECIMAL(12,4), d. h. der Preis kann insgesamt 8 Stellen vor dem Komma und 4 Nachkommastellen aufnehmen. Ein Produkt kann in Magento also maximal 100 Millionen teuer sein.

SELECT entity_id, sku, eav_dec.value as price FROM catalog_product_entity
JOIN catalog_product_entity_decimal eav_dec USING(entity_id)
JOIN eav_attribute eav ON (eav_dec.entity_type_id=eav.entity_type_id AND eav_dec.attribute_id=eav.attribute_id
AND eav.attribute_code = 'price');

Kombinierte Abfrage

Produktname, URL-Key und Preise lassen sich in einer Abfrage kombinieren. Für verschiedene Attributspalten werden dabei gleiche Tabellen mehrfach über verschiedene Aliases referenziert:

SELECT entity_id, sku, eav_vc2.value name, eav_vc1.value url_key, eav_dec.value as price FROM catalog_product_entity
JOIN catalog_product_entity_decimal eav_dec USING(entity_id)
JOIN eav_attribute eav ON (eav_dec.entity_type_id=eav.entity_type_id AND eav_dec.attribute_id=eav.attribute_id AND eav_dec.store_id=0
AND eav.attribute_code = 'price')
JOIN catalog_product_entity_varchar eav_vc1 USING(entity_id)
JOIN eav_attribute eav1 ON (eav_vc1.entity_type_id=eav1.entity_type_id AND eav_vc1.attribute_id=eav1.attribute_id AND eav_vc1.store_id=0
AND eav1.attribute_code = 'url_key')
JOIN catalog_product_entity_varchar eav_vc2 USING(entity_id)
JOIN eav_attribute eav2 ON (eav_vc1.entity_type_id=eav2.entity_type_id AND eav_vc2.attribute_id=eav2.attribute_id AND eav_vc2.store_id=0
AND eav2.attribute_code = 'name');

Für mehr Übersicht wurde diese Abfrage auf die Standardwerte reduziert. Name und URL-Key in anderen StoreViews wird nicht aufgelistet.

Werenbestand

Der Warenbestand wird in der Tabelle cataloginventory_stock_item gespeichert, wobei neben qty (Anzahl, engl.: quantity) auch die Spalte is_in_stock (Im Lager) für die Anzeige des Produkts im Frontend ausschlaggebend sind:

SELECT entity_id, sku, eav_vc.value as name, qty, is_in_stock
FROM catalog_product_entity p
JOIN catalog_product_entity_varchar eav_vc USING(entity_id)
JOIN eav_attribute eav ON (eav_vc.entity_type_id=eav.entity_type_id AND eav_vc.attribute_id=eav.attribute_id AND store_id=0
AND eav.attribute_code = 'name')
JOIN cataloginventory_stock_item ON(p.entity_id=product_id);

Kategorien

Name, URL-Key und vollständige Pfade zu allen Kategorien im Shop auflisten:

SELECT cat.entity_id, parent_id, eav_vc.value name, eav_vc1.value url_key, request_path, rew.store_id
FROM catalog_category_entity cat
JOIN catalog_category_entity_varchar eav_vc USING(entity_id)
JOIN eav_attribute eav ON(eav_vc.entity_type_id=eav.entity_type_id AND eav_vc.attribute_id=eav.attribute_id AND eav_vc.store_id=0
AND eav.attribute_code = 'name')
JOIN catalog_category_entity_varchar eav_vc1 USING(entity_id)
JOIN eav_attribute eav1 ON(eav_vc1.entity_type_id=eav1.entity_type_id AND eav_vc1.attribute_id=eav1.attribute_id AND eav_vc1.store_id=0
AND eav1.attribute_code = 'url_key')
JOIN core_url_rewrite rew ON (rew.category_id=cat.entity_id AND rew.store_id=1)
WHERE id_path LIKE 'category/%';

Das Ergebnis wird auf die Standardwerte reduziert. Um Werte für alle StoreViews anzeigen zu lassen, muss überall die Bedingung store_id=0 entfernt werden.

Tipp zum einfachen Kopieren der Abfragen: Doppelklick in den Rahmen deaktiviert die Formatierung und markiert die gesamte Abfrage.

Mein Benutzerkonto: einzelne Menüpunkte deaktivieren und Reihenfolge ändern

In einem typischen Online-Shop, der physische Produkte verkauft, sind nicht alle Funktionen und Menüpunkte des Benutzerkontos relevant. Um Verwirrung von Kunden und überflüssige Fragen zu vermeiden, sollten irrelevante Menüpunkte deaktiviert werden:

  • Benutzerkonto Übersicht
  • Benutzerkonto Information
  • Adressbuch
  • Meine Bestellungen
  • Zahlungsgfreigaben
  • Wiederkehrende Profile
  • Meine Kundenmeinungen
  • Meine Schlagworte
  • Mein Wunschzettel
  • Meine Anwendungen (neu seit Magento 1.7.0.0)
  • Newsletter Abonnements
  • Meine Downloadartikel

Magento bietet von sich aus keine bequeme Möglichkeit das Menü im Benutzerkonto zu bearbeiten; Deaktivierung und Änderung der Reihenfolge einzelner Menüpunkte muss durch Anpassung der Layout-Dateien erfolgen. Der Menüpunkt „Meine Downloadartikel“ verschwindet durch Deaktivierung des Moduls Mage_Downloadable.

Mit der kostenfreien Erweiterung Customer Navigation von Webguys lässt sich das Menü im Benutzerkonto viel einfacher und bequemer bearbeiten.

Sie ist auch mit Magento 1.7 kompatibel, berücksichtigt in der aktuellen Version aber nicht den neuen Menüpunkt „Meine Anwendungen“ (engl.: My Applications). Hierzu gibt es einen Patch bestehend aus den zwei Dateien Navigation.php und System.xml. Nach der Installation der Erweiterung sind damit die folgenden Dateien zu überschreiben:

  • app/code/community/Webguys/CustomerNavigation/etc/system.xml
  • app/code/community/Webguys/CustomerNavigation/Block/Account/Navigation.php

Und so kann das Ergebnis aussehen:

Frontend Benutzerkonto Menü: nicht genutzte Menüpunkte ausblenden

 

Magento durch Deaktivierung ungenutzter Standardmodule etwas beschleunigen

Durch Deaktivierung ungenutzter Standardmodule, kann Magento etwas beschleunigt werden, da diese beim Aufruf des Backends und Frontends dann nicht geladen werden.

Praxisbewährte Deaktivierung durch Umbenennung

So kann beispielsweise das Modul für downloadbare Produkte durch Umbenennen der Datei app/etc/modules/Mage_Downloadable.xml in Mage_Downloadable.xml-disabled deaktiviert werden. Magento liest beim Start alle *.xml-Dateien im Verzeichnis app/etc/modules ein, um festzustellen, welche Module und Erweiterungen zu laden sind. Bei Deaktivierung durch Umbenennung darf die Datei also keine xml-Endung tragen.

Der Vorteil dieser Methode, ist dass deaktivierte Module bereits in der Dateiübersicht erkennbar sind.

Die eigentlich korrekte Deaktivierung

Für die Deaktivierung von Modulen haben Magento-Entwickler eigentlich die Einstellung active der jeweiligen xml-Datei in app/etc/modules vorgesehen. So kann das Modul für downloadbare Produkte durch Setzen von active=false in Mage_Downloadable.xml deaktiviert werden:

<config>
    <modules>
        <Mage_Downloadable>
            <active>false</active>
            <codePool>core</codePool>
            <depends>
                <Mage_Catalog />
                <!--<Mage_Sales />-->
            </depends>
        </Mage_Downloadable>
    </modules>
</config>

Der Nachteil dieser Methode ist dass jede einzelne xml-Datei geöffnet werde müsste, um herauszufinden, welche Module aktiviert und welche deaktiviert sind. Bei der oben vorgeschlagenen Deaktivierung durch Umbenennung sind deaktivierte Module dagegen in der Dateiliste sofort erkennbar.

Vermeintliche Deaktivierung im Backend

Eine Übersicht der Module, die in Magento geladen sind bekommt man im Backend in System > Konfiguration > Erweitert > Erweitert.

Hier kann auch die Ausgabe eines Moduls deaktiviert werden. Dies ist jedoch mit Deaktivierung des Moduls nicht gleichzusetzen.

Die aktuelle Version des deutschen Sprachpackets für Magento liefert für dieses Block eine gute Übersetzung für „Disable Modules Output“ mit dem Hinweis:

Modulausgaben verstecken (Achtung: Module werden immernoch ausgeführt!)

Wird beispielsweise die Ausgabe einer Erweiterung deaktiviert, die zusätzliche Spalten in der Bestellübersicht einblendet, verschwinden nicht etwa nur die Zusatzspalte sondern die gesamte Bestellübersicht.

Module sind übrigens ebenfalls Erweiterungen mit dem einzigen Unterschied, dass deren Code im Core-Codepool im Verzeichnis app/code/core gespeichert ist und sie standardmäßig mit Magento mitgeliefert werden.

Image attributes processor v1.0.22 – error creating media/catalog/product/3: 2,mkdir(): No such file or directory

Auf einem frisch installiertem Magento 1.7.x wird der Import von Produktbildern mit Magmi (v0.7.17a) und dem Plug-In Image attributes processor (v1.0.22) in der jeweils aktuellen Version nicht funktionieren.

Beispielfehlermeldung

Image attributes processor v1.0.22 - error creating media/catalog/product/3: 2,mkdir(): No such file or directory

Ursache

Der Grund dafür ist dass das Verzeichnis media/catalog/product nach der Installation von Magento nicht existiert. Er wird erst beim Upload eines ersten Produktbildes automatisch angelegt.

Laut PHP-Dokumentation erstellt mkdir standardmäßig Verzeichnisse nicht rekursiv:

bool mkdir ( string $pathname
      [, int $mode = 0777
      [, bool $recursive = false [, resource $context ]]] )

In diesem Fall wäre dies aber erforderlich.

Fehlerbehebung

Der Autor ist informiert und wird den Bugfix in der nächsten Version einbauen.

Um den Bug in der aktuellen Version zu beheben, muss die Datei imageitattributeemprocessor.php angepasst werden, in dem jeder der zwei Aufrufe von mkdir ein true als den dritten Parameter übergeben bekommen.

Zeilen 548 und 561 (Tipp: Strg+F nach mkdir) vorher:

$tst=@mkdir($l1d,Magmi_Config::getInstance()->getDirMask());
$tst=@mkdir($l2d,Magmi_Config::getInstance()->getDirMask());

Zeilen 548 und 561 nachher:

$tst=@mkdir($l1d,Magmi_Config::getInstance()->getDirMask(), true);
$tst=@mkdir($l2d,Magmi_Config::getInstance()->getDirMask(), true);

Die Datei imageitattributeemprocessor.php ist zu finden in:
magmi/plugins/extra/itemprocessors/imageprocessor/

Eigenes Magento-Theme nachträglich bereinigen

Vor einiger Zeit habe ich eine Strategie vorgestellt, wie man ein Magento-Theme einfacher erstellen kann. Wer diese Strategie nicht kennt, muss ständig zwischen dem Base- und dem eigenen Theme-Verzeichnis wechseln und jede Datei inklusive der Verzeichnisstruktur einzeln in das eigene Theme-Verzeichnis kopieren und dort anpassen.

Da dies sehr umständlich ist, nimmt der eine oder andere den einfachen Weg und kopiert das gesamte Base-Theme in das eigene Theme-Verzeichnis. Dies hat u. a. den Nachteil, dass die Update-Fähigkeit von Magento dadurch verloren geht.

Welche Möglichkeiten gibt es, um einen solchen Theme nachträglich zu bereinigen und Update-Fähigkeit wiederherzustellen?

Unveränderte Dateien manuell zu Löschen ist riskant und aufwändig. Im Web gibt es einige Tools mit den sich Verzeichnisse rekursiv vergleichen lassen. Auch können diese Tools die Inhalte der Dateien vergleichen, sodass sie sich von einer neueren aber dennoch inhaltlich gleicher Datei nicht verwirren lassen. Doch alle diese Tools sind für Synchronisierung von Verzeichnissen konzipiert. Einen Magento-Theme konnte ich leider mit keinem der Tools bereinigen, die ich ausprobiert hatte.

So wird es viel einfacher eine eigene Lösung zu Programmieren, als noch mehr Programme zu testen.

Die folgende Funktion erstellt eine Liste aller Dateien im eigenen Theme-Verzeichnis und vergleicht jede einzelne inhaltlich mit der Datei im base-Verzeichnis. Das Resultat ist ein Array mit allen Dateien des eigenen Theme-Verzeichnisses mit Angaben ob:

  • verändert (changed),
  • unverändert (unchanged)
  • oder im base-Verzeichnis nicht vorhanden (basenotfound):
function dirCompare($myThemePath, $baseThemePath, &$output = array(), $path = '.')
{
    $ignore = array( 'cgi-bin', '.', '..' );
    $dh = @opendir( "$myThemePath/$path" ); 

    while( false !== ( $file = readdir( $dh ) ) ){ 

        if( !in_array( $file, $ignore ) ){ 

            if( is_dir( "$myThemePath/$path/$file" ) ){
                dirCompare( $myThemePath, $baseThemePath, $output, "$path/$file");
            } else {
            	$id = sizeof($output)+1;
            	if(file_exists("$baseThemePath/$path/$file"))
            	{
            		$leftFile = file_get_contents("$myThemePath/$path/$file");
            		$rightFile = file_get_contents("$baseThemePath/$path/$file");

            		if($leftFile == $rightFile)
            			$output[$id]['compare'] = 'unchanged';
            		else
            			$output[$id]['compare'] = 'changed';
            	}
            	else
            		$output[$id]['compare'] = 'basenotfound';            		

            	$output[$id]['fullpath'] = "$myThemePath/$path/$file";
            	$output[$id]['path'] = "$path/$file";
            	$output[$id]['file'] = "$file";
            }
        }
    }
    closedir($dh);
    return $output;
}

Diese Liste kann nun flexibel verarbeitet werden. Entweder können unveränderte Dateien direkt mit PHP gelöscht werden, oder als Stapelverarbeitung gespeichert werden, die über das Betriebssystem ausgeführt wird.

Der nachfolgende Beispiel zeigt, wie mit Hilfe der Funktion eine Windows-Stapelverarbeitung erstellt werden kann, die alle unveränderten Dateien im eigenen Theme-Verzeichnis durch Umbenennung „deaktiviert“:

// Theme-Verzeichnisse relativ zu der PHP-Skript-Datei
$base_theme = 'app/design/frontend/base/default';
$user_theme = 'app/design/frontend/default/mytheme';

$result = array();
$output = '';

dirCompare($user_theme, $base_theme, $result);
// print '<pre>'; print_r($result); exit;

foreach($result as $element)
{
	switch($element['compare'])
	{
		case 'unchanged':

			$file = $element['file'];

			$fullpath = $element['fullpath'];
			$fullpath = str_replace('./', '', $fullpath);
			$fullpath = str_replace('/', '\\', $fullpath);

			$output .= "REN \"$fullpath\" \"$file-disabled\"\n";
			break;	

		case 'changed':
			break;	

		case 'basenotfound':
			break;
	}
}

print $output;
// file_put_contents('rename.bat', $output);

Zur Vereinfachung werden die Befehle der Stapelverarbeitung direkt im Browser ausgegeben. Beispielausgabe:

REN "app\design\frontend\default\mytheme\etc\widget.xml" "widget.xml-disabled"
REN "app\design\frontend\default\mytheme\layout\authorizenet.xml" "authorizenet.xml-disabled"
REN "app\design\frontend\default\mytheme\layout\bundle.xml" "bundle.xml-disabled"
REN "app\design\frontend\default\mytheme\layout\captcha.xml" "captcha.xml-disabled"
...
...

Durch Entfernen der Kommentarzeichen in der letzten Zeile werden die Befehle mit der file_put_contents automatisch in einer Datei gespeichert.

Bereits die Umbenennung reicht aus, um Updatefähigkeit des Themes wiederherzustellen.

Mit einem Kommandozeilenbefehl können alle Dateien mit dem Muster *.*-disabled rekursiv gelöscht werden. Automatische Löschung leerer Verzeichnisse ist mit einem etwas komplexeren Batch-Skript oder mit kostenfreien Zusatzprogrammen möglich.

Bestellabwicklung in Magento: Status und Zustände von Bestellungen verstehen

Nach dem der Kunde im Checkout auf „Bestellung abschließen“ klickt, wird der Warenkorb in eine Bestellung umgewandelt. Die Bestellung wird im Backend sichtbar. Danach ist für die Bestellabwicklung eine Abfolge vorgesehen, bei der die Bestellung den Zustand wechselt: Durch Erstellen einer Rechnung wechselt der Zustand in processing, nach Erstellung des Lieferscheins kommt die Bestellung in den Zustand complete.

Der Status der Bestellung und die für die Abwicklung vorhandene Optionen werden von Magento abhängig von dem Zustand der Bestellung bereitgestellt. Das folgende Diagramm veranschaulicht die Bestellabwicklung in Magento:

Bestellabwicklung in Magento: Zustandsübergangsdiagramm

Zustand und Status

Jede Bestellung hat zwei Eigenschaften: Zustand (engl.: state) und Status (engl.: status).

Nur der Status ist dabei für den Kunden im Frontend in „Mein Benutzerkonto“ und für den Händler im Backend (als Filtereinstellung und in Bestelldetails) sichtbar.

Jedem Zustand können ein oder mehrere Status-Codes zugewiesen werden. In der Standardinstallation von Magento sind, bis auf eine Ausnahme, jedem Zustand genau ein Status zugewiesen, wobei die internen Bezeichnungen in den meisten Fällen übereinstimmen:

Magento Zustands- und Status-CodesBestellstatus in der Bestellübersicht

Diese Übersicht ist in System > Bestellstatus & Zustand einsehbar. Dort lassen sich zusätzliche Status-Codes definieren und Zuständen zuweisen. Die Liste der Zustände ist vordefiniert und kann nicht bearbeitet werden. Ein Status-Code kann nach dem Anlegen nicht mehr gelöscht werden!

Bestellzustände

Zustand new

Im Zustand new hat die Bestellung den Status Pending (Ausstehend).

Den Zustand new bekommt eine neue Bestellung bei einer Offline-Zahlung wie Vorkasse oder Nachnahme. Der Kunde bekommt dabei eine Bestellbestätigung.

Zustand pending_payment

Im Zustand pending_payment hat die Bestellung den gleichnamigen Status Pending Payment (Ausstehende Zahlung).

Dieser Zustand ist für Abwicklung von Online-Zahlungen vorgesehen (darunter PayPal, MoneyBookers/Skrill, Sofortüberweisung, giropay, Kreditkarte, Lastschrift, Rechnung). Eine neue Bestellung, für die eine Online-Zahlungsmethode gewählt wurde, wird im Zustand pending_payment angelegt. Eine Bestellung im Zustand pending_payment wird im Frontend nicht angezeigt, d. h. sie bleibt in „Mein Benutzerkonto“ für einen registrierten Kunden unsichtbar. Der Kunde bekommt keine Bestellbestätigung.

Im Zustand pending_payment bleibt die Bestellung bis Magento eine Bestätigung über eine erfolgreiche Zahlung erhalten hat. Daraufhin sollte die Rechnung automatisch generiert werden, und die Bestellung in den Zustand processing wechseln.

Eine Zahlungsseite bietet meistens eine Schaltlfläche oder einen Link zum Abbruch der Zahlung. Bricht der Kunde die Zahlung auf diese Weise ab, wird die Bestellung storniert und wechselt in den Zustand canceled. Wenn die Bestellung seit längerem im Zustand pending_payment bleibt, bedeutet es, dass der Kunde das Browserfenster mit der Zahlungsseite geschlossen hat ohne die Zahlung abzuschließen. In diesem Fall kann die Bestellung manuell im Backend storniert werden.

Da der Warenkorb beim Klick auf „Bestellung abschließen“ in eine Bestellung umgewandelt wurde, wird der Warenkorb leer sein, wenn der Kunde zum Shop zurückkehrt.

Wenn der Kunde auf der Zahlungsseite die Zahlung abbricht und die Bestellung damit storniert, befüllen einige wenige Extensions (darunter die Extension von Sofortüberweisung und von MoneyBookers) den Warenkorb mit dem Inhalt der stornierten Bestellung. Meistens ist das jedoch nicht der Fall; PayPal und viele andere Extensions zur Abwicklung von Online-Zahlungen (z. B. Kreditkartenzahlungen, Kauf auf Rechnung) lassen den Kunden bei Abbruch zum Shop mit leerem Warenkorb zurückkehren.

Zustand processing

Im Zustand processing hat die Bestellung den gleichnamigen Status Processing (Verarbeitung).

In diesen Zustand kommt eine Bestellung, wenn eine Rechnung aber noch kein Lieferschein oder ein Lieferschein aber noch keine Rechnung erstellt wurde.

In Magento sind Teilrechnungen und Teillieferungen möglich. Eine Bestellung bleibt in dem Zustand processing, solange sie nicht vollständig berechnet und geliefert wurde.

Zustand complete

Im Zustand complete hat die Bestellung den gleichnamigen Status Complete (Vollständig).

In diesen Zustand kommt eine Bestellung, wenn Rechnung und Lieferschein erstellt wurden. Bei virtuellen Produkten entfällt der Lieferschein.

Bei Verwendung von Teilrechnungen und Teillieferungen kommt die Bestellung in den Zustand complete, nur wenn sie vollständig berechnet und vollständig geliefert wurde.

Zustand closed

Im Zustand closed hat die Bestellung den gleichnamigen Status Closed (Geschlossen).

In den Zuständen processing und complete kann eine Bestellung nicht storniert werden, sondern muss durch Erstellung einer Gutschrift erstattet werden.

Bei einer Teilerstattung bleibt die Bestellung im Zustand complete. Erst bei Erstattung aller Artikel wechselt der Zustand zu closed. Weicht die Gesamtsumme von Gesamterstattung um nur einen Cent ab, bleibt die Bestellung im Zustand complete, wobei der letzte Cent mit einer weiteren Gutschrift erstattet werden kann. Die Wertminderung bei Rückgabe eines gebrauchten Artikels kann über Berichtigungszuschlag abgebildet werden.

Zustand canceled

Im Zustand canceled hat die Bestellung den gleichnamigen Status Canceled (Storniert).

Eine Bestellung kann nur in den Zuständen new und payment_pending storniert und damit in den Zustand canceled überführt werden.

Bei Online-Zahlungen wird die Bestellung beim Abbruch der Zahlung durch den Klick auf die Schaltfläche „Zahlung abbrechen“ o. ä. automatisch storniert. Wenn der Kunde das Browserfenster mit der Zahlungsseite schließt, bleibt die Bestellung im Zustand payment_review und muss im Backend manuell storniert werden.

Zustand holded

Im Zustand holded hat die Bestellung den Status On Hold (Zurückgestellt).

Durch den Klick auf die Schaltlfäche „Zurückstellen“ im Backend wechselt die Bestellung in den Zustand holded. Schaltlfächen zur Abwicklung der Bestellung werden ausgeblendet. Bevor eine Bestellung weiterverarbeitet oder storniert werden kann, muss sie wiederaufgenommen werden. Dabei wechselt der Zustand zu dem ursprünglichen Wert, den die Bestellung vor dem Klick auf „Zurückstellen“ hatte.

Eine Bestellung kann nur in Zuständen vor complete zurückgestellt werden. Eine stornierte oder erstattete Bestellung kann nicht zurückgestellt werden.

Zustand payment_review

Dieser Zustand ist im Diagramm nicht enthalten.

Im Zustand payment_review hat die Bestellung standardmäßig den gleichnamigen Status Payment Review (Zahlungsprüfung).

In den Zustand payment_review wechselt eine Bestellung, wenn der Kunde eine Online-Zahlung autorisiert hat, aber das Geld noch nicht gezahlt wurde. Der Kunde bekommt dabei trotzdem eine Bestellbestätigung, und die Bestellung wird im Frontend in „Mein Benutzerkonto“ sichtbar – im Gegensatz zu dem Zustand pending_payment.

Den Zustand payment_review bekommt eine Bestellung bei Zahlung mit einem frisch eröffneten PayPal-Konto, welches nicht gedeckt ist und für die Abbuchung vom Bankkonto des Kunden noch validiert werden muss. Für die Validierung überweist PayPal zufällige Cent-Beträge auf das Bankkonto des Kunden, die er in seinem PayPal-Konto eingibt, sodass die Bestellung 1-3 Tage in diesem Zustand verweilen kann. Der Händler bekommt in der Regel eine Email vom Zahlungsdienstleister mit mehr Informationen zum Vorgang.

Dem Zustand payment_review ist in der Standardinstallation von Magento als einzigem ein zweiter Status-Code zugewiesen: Suspected Fraud (Betrugsverdacht). Auch in diesem Fall hat der Kunde die Zahlung autorisiert. Die Transaktion wird sowohl auf der Seite von PayPal als auch in Magento nach bestimmten Kriterien analysiert. Kommt beispielsweise die Zahlungsbenachrichtigung von PayPal bei Magento viel zu spät an oder stimmen die Gesambeträge nicht überein (s. Rundungsfehler), bekommt die Bestellung diesen Status-Code. Wenn ein Betrug von PayPal erkannt wurde, bekommt der Händer eine Email mit Zusatzinformationen. Bei Erkennung durch Magento gibt es keine zusätzliche Emails von PayPal. Beachten Sie, dass der Kunde, sofern registriert, seine Bestellung mit diesem Status in dem Kundenkonto sehen kann! Eine Bestellung mit diesem Status kann im Backend nicht weiter verarbeitet werden. Sie muss durch Manipulation des Status und Zustand in der Datenbank freigegeben werden.

Fehlverhalten von Extensions

Viele Extensions zur Abwicklung von Online-Zahlungen beachten die beschriebenen Zusammenhänge nicht. Oft wird der Status unabhängig vom Zustand gesetzt. Oder der Zustand der Bestellung wird z. B. auf processing gesetzt, ohne dass eine Rechnung erstellt wurde. In diesem Fall lassen sich Bestellungen mit Rechnungen und ohne in der Bestellübersicht nicht mehr unterscheiden. Gesamteinnahmen im Dashboard werden verfälscht, da dort nur Rechnungsbeträge berücksichtigt werden. Einige Extensions erlauben in den Einstellungen sogar freie Wahl des Status, den die Bestellung nach erfolgreicher Zahlung erhalten soll. Dies kann bei Bestellabwicklung im Backend zu Problemen führen.

Tipp: Bestellabwicklung wird im Magento-Training für Einsteiger behandelt.

PayPal InvoiceSender: Rechnungen bei mit PayPal bezahlten Bestellungen automatisch versenden

PayPal InvoiceSender auf Magento Connect

Wird eine Bestellung mit PayPal bezahlt, bekommt der Kunde nach Abschluß der Zahlung eine Bestellbestätigung per Email zugesandt. Die Rechnung wird in Magento automatisch generiert, dem Kunden aber nicht zugesendet.

Registrierte Kunden können Ihre Rechnungen im Benutzerkonto einsehen und drucken. Bei Gastbestellungen ist dies allerdings nicht möglich. Nicht registrierte Kunden haben also keinen Zugriff auf die elektronische Rechnung, es sei denn sie wird manuell aus dem Backend versendet.

Mit der Extension PayPal Invoice Sender bekommt der Kunde nach Abschluß der Zahlung zusätzlich zu der Bestellbestätigung auch die Rechnung automatisch zugesendet.

Nach der Installation der Extension ist der automatische Rechnungsversand aktiviert. Im Backend lässt sich der automatische Versand der Rechnung deaktivieren:

PayPal Invoice Sender Extension: Versendet Rechnungen von mit PayPal bezahlten Bestellungen automatisch

Zugriff verweigert auf Cache-Verwaltung in Magento 1.4.x

Zugriff verweigert auf Cache-Verwaltung in Magento 1.4.xMöchte ein Benutzer mit Administrator-Rechten auf die Cache-Verwaltung in Magento 1.4.x zugreifen, bekommt er die Fehlermeldung „Zugriff verweigert“, wenn er nicht zu der Administrator-Benutzergruppe gehört. Hierbei handelt es sich um ein Bug in Magento 1.4.x.

Eine schnelle Lösung, um den Cache zu leeren, besteht in der Löschung aller Dateien und Verzeichnisse im Cache-Verzeichnis /var/cache:

cd /var/cache
rm -rf *

Um den Zugriff auf die Cache-Verwaltung im Backend zu ermöglichen, bedarf es einer Code-Anpassung in /app/code/core/Mage/Adminhtml/controllers/CacheController.php:

protected function _isAllowed()
{
 //    return Mage::getSingleton('admin/session')->isAllowed('cache');
 // Die obere Zeile ist zu ändern in:
       return Mage::getSingleton('admin/session')->isAllowed('system/cache');
}

Die Code-Anpassung darf ausnahmsweise direkt im Core-Verzeichnis erfolgen, da der Bug in der Version 1.5.0 behoben wurde.

Entwicklungsumgebungen

Integrierte Entwicklungsumgebungen (Integrated Development Environment, IDE) sind sehr umfangreiche Werkzeuge für Arbeit an größeren PHP-Projekten. Für kleine Code- und Template-Anpassungen in Magento sind eher Quellcode-Editoren besser geeignet.

Für den Aufbau eines komplett neuen Themes, die Entwicklung eigener Extensions und Analyse umfangreicher PHP-Projekte lohnt sich der Einsatz einer IDE. Eine gute IDE bietet neben Syntax-Hervorhebung und Code-Folding (das Einklappen von Code-Blöcken) einen Klassenbrowser und die Möglichkeit zu der Definition einer Methode zu springen, was Code-Analyse stark vereinfacht.

Eclipse für PHP-Entwickler

Eclipse ist eine Open-Source-IDE, die in Java geschrieben ist und daher für alle Betriebssysteme zur Verfügung steht. Eclipse lässt sich mit Plugins erweitern. Es gibt vorkonfigurierte Varianten für verschiedene Programmiersprachen und Einsatzgebiete.

Ähnlich wie bei Magento, ist bei Eclipse die neueste Version nicht zwangsläufig die beste Wahl, da es oft Kompatibilitätsprobleme mit Plugins gibt. Mit Eclipse „Helios“ for PHP-Developers habe ich gute Erfahrungen gesammelt.

Tipp für Magento-Entwickler: Um Eclipse zu helfen, die Definition einer über Factory-Pattern aufgerufenen Klasse zu finden, übergibt man im Kommentar der Variablen den vollständigen Klassennamen:

  /* @var $helper Mage_Cms_Helper_Data */
  $helper = Mage::helper('cms');
  $processor = $helper->getPageTemplateProcessor();

Durch den Kommentar in der ersten Zeile dieses Beispiels wird man mit Strg+Klick auf getPageTemplateProcessor() zur Definition dieser Methode in Mage_Cms_Helper_Data springen können.

EditBox für Eclipse

Durch Umrahmung und farbige Hervorhebung von Code-Blöcken und deren Schachtelung hilft EditBox Übersicht im Code zu behalten:

Eclipse for PHP-Developers mit EditoBox-Plugin

EditBox ist kompatibel mit Eclipse 3.6.2 „Helios“ for PHP-Developers. Die Installation geschieht durch Kopieren der jar-Datei in das Plugin-Verzeichnis von Eclipse. Mit Zend Eclipse PDT (Eclipse 3.7 „Indigo“ mit PHP Developer Tools) gibt es dagegen Probleme: EditBox wird nicht geladen, obwohl die Voraussetzung (Eclipse ab Version 3.2) erfüllt ist.

In den Optionen Window > Preferences > EditBox können für verschiedene Dateitypen (*.php, *.xml, *.*htm*) eigene Farbschemas festgelegt werden:

Themes für EditBox: PHP, XML, HTML
Download Themes für EditBox: PHP, XML, HTML

Mit Alt+Klick kann ein Block mit einer ebenfalls einstellbaren Farbe markiert werden, hier in Hellgelb.

JetBrains PhpStorm

PhpStorm ist unter den Entwicklungsumgebungen wohl die beste Wahl für einen Webentwickler und speziell für einen Magento-Entwickler. Durch den Bedienkomfort und die dadurch resultierende Zeitersparnis zahlt sich die Lizenzgebühr in Höhe von rund 100 € inkl. 1 Jahr Softwareupdates schnell aus.

Eines der vielen Highlights: Wird in PHP einer Variable SQL- oder HTML-Code als Zeichenkette zugewiesen, erkennt PhpStorm auch innerhalb dieser Zeichenkette die Sprache und setzt eine Syntaxhevorhebung ein und unterstützt die Eingabe mit Vorschlägen und Autovervollständigung, sogar als Inline-CSS innerhalb von HTML innerhalb einer PHP-Zeichenkette:

Darüberhinaus versucht PhpStorm die im Ausgabedokument verwendeten CSS-Klassen zu extrahieren und sie während der Eingabe vorzuschlagen.

Mit dem Plug-In Magicento werden speziell Magento-Entwickler unterstützt: Das Plug-In bietet Extension-Vorlagen und automatische Vervollständigung in XML-Konfigurationsdateien von Magento-Extensions.

Ansonsten bietet PhpStorm vieles von dem, was ein Entwickler von Eclipse gewohnt ist, darunter Sprung zur Definition mit Strg+Klick, Klassenbrowser mit Strg+O, Klassensuche mit Strg+Umschalt+T, Bookmarks, ToDo-Listen.

Als Java-Basierte Software läuft PhpStorm auf allen gängigen Betriebssystemen.

Akzeptanz von Zahlungsmethoden

Die nachfolgende Abfrage analysiert die Akzeptanz von Zahlungsmethoden. Sie zeigt die Anzahl vollständiger Bestellungen und Umsatz pro Zahlungsmethode:

SELECT
    method,
    COUNT(p.parent_id) AS order_count,
    ROUND(SUM(base_grand_total),2) AS method_volume
FROM sales_flat_order_payment p
JOIN sales_flat_order o ON o.entity_id = p.parent_id
AND o.status = 'complete'
GROUP BY method
ORDER BY order_count DESC;

Um die Annahme einer neu eingeführten Zahlungsmethode zu analysieren, ist die nachfolgende Abfrage hilfreich. Sie zeigt Bestellungen pro Kunde und Anzahl verschiedener Zahlungsmethoden, die dabei verwendet wurden; die verwendeten Zahlungsmethoden werden in einer Zusatzspalte aufgelistet:

SELECT
    customer_firstname,
    customer_lastname,
    COUNT(o.entity_id) AS order_count,
    COUNT(DISTINCT p.method) AS method_count,
    GROUP_CONCAT(DISTINCT p.method) AS method_list    
FROM sales_flat_order o
JOIN sales_flat_order_payment p ON o.entity_id = p.parent_id
AND status = 'complete'
-- WHERE YEAR(created_at) = 2012 AND MONTH(created_at) = 1
GROUP BY customer_email
ORDER BY order_count DESC, method_count DESC
LIMIT 100;

Durch auskommentieren der Zeile 10 lässt sich die Abfrage auf einen bestimmten Zeitraum einschränken.

Regionale Verteilung von Kunden und Bestellungen

Die nachfolgende Abfrage untersucht die regionale Verteilung von Kunden und Bestellungen. Regionen werden nach den ersten n Stellen der Postleitzahl zusammengefasst, wobei n in der zweiten Zeile angegeben werden kann:

SELECT
    CONCAT(LEFT(postcode, @n:=3),'*') AS region,
    COUNT(entity_id) AS order_count,
    COUNT(DISTINCT email) AS customer_count,
    GROUP_CONCAT(DISTINCT city) AS city_list
FROM sales_flat_order_address
WHERE address_type = 'shipping'
GROUP BY LEFT(postcode, @n)
ORDER BY customer_count DESC
LIMIT 50;

Die eigentliche regionale Gruppierung findet in Zeile 8 statt. Hier wird die in Zeile 2 gesetzte Benutzervariable n verwendet. Je kleiner n, desto größer die Gruppe. Zur besseren Zuordnung der Region werden in der zusätzlichen Spalte die Städte aufgelistet.

Die Kunden werden anhand der E-Mail-Adresse identifiziert, damit Gastbestellungen mitberücksichtigt werden.

Möchte man die Daten auf einen bestimmten Monat und Jahr beschränken, um beispielsweise die Auswirkungen einer lokalen Werbemaßnahme zu analysieren, kommt man in diesem Fall nicht ohne eine Verbundabfrage weiter:

SELECT
    CONCAT(LEFT(postcode, @n:=2),'*') AS region,
    COUNT(a.entity_id) AS order_count,
    COUNT(DISTINCT email) AS customer_count,
    GROUP_CONCAT(DISTINCT city) AS city_list
FROM sales_flat_order_address a
JOIN sales_flat_order p ON a.parent_id = p.entity_id
  AND status = 'complete'
WHERE address_type = 'shipping'
  AND YEAR(created_at) = 2012
  AND MONTH(created_at) = 1
GROUP BY LEFT(postcode, @n)
ORDER BY customer_count DESC
LIMIT 50;

Die Verbundabfrage verwendet in diesem Fall indizierte Spalten, sodass eine schnelle Verarbeitung und damit kaum Auslastung für den Live-Shop zu erwarten ist.

Um die Abfrage so einfach wie möglich zu halten, wurde auf die Filterung von Bestellungen mit dem Status Vollständig in der ersten Abfrage verzichtet. Da in der zweiten Abfrage zwecks Filterung eines bestimmten Zeitraums die Spalte status zur Verfügung steht, werden zusätzlich nur Bestellungen berücksichtigt, die den Status Vollständig haben.

Anzahl von Bestellungen pro Kunde

Die nachfolgende Abfrage gibt die Anzahl von Bestellungen pro Kunde, wobei die Liste nach dieser Anzahl absteigend sortiert wird:

SELECT
    customer_firstname,
    customer_lastname,
    COUNT(entity_id) AS count,
    GROUP_CONCAT(date(created_at)) AS order_dates
FROM sales_flat_order
WHERE status = 'complete'
GROUP BY customer_email
ORDER BY count DESC
LIMIT 100;

Um sowohl registrierte Kunden als auch Gastbestellungen zu berücksichtigen, werden die Kunden anhand ihrer E-Mail identifiziert und zusammengefasst.

Mit einer Zusatzbedingung lässt sich die Liste auf Bestellungen in einem bestimmten Monat und Jahr beschränken:

 SELECT
    customer_firstname,
    customer_lastname,
    COUNT(entity_id) AS count,
    GROUP_CONCAT(date(created_at)) AS order_dates
FROM sales_flat_order
WHERE status = 'complete'
AND YEAR(created_at) = 2012
AND MONTH(created_at) = 1
GROUP BY customer_email
ORDER BY count DESC
LIMIT 100;

Tabellen in der Magento-Datenbank

Die Datenbank von Magento ist sehr umfangreich. Um mehr Übersicht über die darin vorhandenen Tabellen zu bekommen, habe ich sie in einem Tabellenbaum zusammengefasst:

 

  • admin
    • admin_assert
    • admin_role
    • admin_rule
    • admin_user
  • adminnotification
    • adminnotification_inbox
  • api
    • api_assert
    • api_role
    • api_rule
    • api_session
    • api_user
  • catalog
    • category
      • anc
        • categs
          • index
            • catalog_category_anc_categs_index_idx
            • catalog_category_anc_categs_index_tmp
        • products
          • index
            • catalog_category_anc_products_index_idx
            • catalog_category_anc_products_index_tmp
      • catalog_category_entity
      • catalog_category_product
      • entity
        • catalog_category_entity_datetime
        • catalog_category_entity_decimal
        • catalog_category_entity_int
        • catalog_category_entity_text
        • catalog_category_entity_varchar
      • flat
        • store
          • catalog_category_flat_store_1
          • catalog_category_flat_store_2
          • catalog_category_flat_store_3
      • product
        • catalog_category_product_index
        • index
          • catalog_category_product_index_idx
          • catalog_category_product_index_tmp
          • enbl
            • catalog_category_product_index_enbl_idx
            • catalog_category_product_index_enbl_tmp
    • compare
      • catalog_compare_item
    • eav
      • catalog_eav_attribute
    • product
      • bundle
        • catalog_product_bundle_option
        • catalog_product_bundle_selection
        • option
          • catalog_product_bundle_option_value
        • price
          • catalog_product_bundle_price_index
        • selection
          • catalog_product_bundle_selection_price
        • stock
          • catalog_product_bundle_stock_index
      • catalog_product_entity
      • catalog_product_link
      • catalog_product_option
      • catalog_product_relation
      • catalog_product_website
      • enabled
        • catalog_product_enabled_index
      • entity
        • catalog_product_entity_datetime
        • catalog_product_entity_decimal
        • catalog_product_entity_gallery
        • catalog_product_entity_int
        • catalog_product_entity_text
        • catalog_product_entity_varchar
        • media
          • catalog_product_entity_media_gallery
          • gallery
            • catalog_product_entity_media_gallery_value
        • tier
          • catalog_product_entity_tier_price
      • flat
        • catalog_product_flat_1
        • catalog_product_flat_2
        • catalog_product_flat_3
      • index
        • catalog_product_index_eav
        • catalog_product_index_price
        • catalog_product_index_website
        • eav
          • catalog_product_index_eav_decimal
          • catalog_product_index_eav_idx
          • catalog_product_index_eav_tmp
          • decimal
            • catalog_product_index_eav_decimal_idx
            • catalog_product_index_eav_decimal_tmp
        • price
          • bundle
            • catalog_product_index_price_bundle_idx
            • catalog_product_index_price_bundle_tmp
            • opt
              • catalog_product_index_price_bundle_opt_idx
              • catalog_product_index_price_bundle_opt_tmp
            • sel
              • catalog_product_index_price_bundle_sel_idx
              • catalog_product_index_price_bundle_sel_tmp
          • catalog_product_index_price_idx
          • catalog_product_index_price_tmp
          • cfg
            • opt
              • agr
                • catalog_product_index_price_cfg_opt_agr_idx
                • catalog_product_index_price_cfg_opt_agr_tmp
              • catalog_product_index_price_cfg_opt_idx
              • catalog_product_index_price_cfg_opt_tmp
          • downlod
            • catalog_product_index_price_downlod_idx
            • catalog_product_index_price_downlod_tmp
          • final
            • catalog_product_index_price_final_idx
            • catalog_product_index_price_final_tmp
          • opt
            • agr
              • catalog_product_index_price_opt_agr_idx
              • catalog_product_index_price_opt_agr_tmp
            • catalog_product_index_price_opt_idx
            • catalog_product_index_price_opt_tmp
        • tier
          • catalog_product_index_tier_price
      • link
        • attribute
          • catalog_product_link_attribute_decimal
          • catalog_product_link_attribute_int
          • catalog_product_link_attribute_varchar
        • catalog_product_link_attribute
        • catalog_product_link_type
      • option
        • catalog_product_option_price
        • catalog_product_option_title
        • type
          • catalog_product_option_type_price
          • catalog_product_option_type_title
          • catalog_product_option_type_value
      • super
        • attribute
          • catalog_product_super_attribute_label
          • catalog_product_super_attribute_pricing
        • catalog_product_super_attribute
        • catalog_product_super_link
  • cataloginventory
    • cataloginventory_stock
    • stock
      • cataloginventory_stock_item
      • cataloginventory_stock_status
      • status
        • cataloginventory_stock_status_idx
        • cataloginventory_stock_status_tmp
  • catalogrule
    • affected
      • catalogrule_affected_product
    • catalogrule
    • catalogrule_product
    • group
      • catalogrule_group_website
    • product
      • catalogrule_product_price
  • catalogsearch
    • catalogsearch_fulltext
    • catalogsearch_query
    • catalogsearch_result
  • checkout
    • agreement
      • checkout_agreement_store
    • checkout_agreement
  • cms
    • block
      • cms_block_store
    • cms_block
    • cms_page
    • page
      • cms_page_store
  • core
    • cache
      • core_cache_option
      • core_cache_tag
    • config
      • core_config_data
    • core_cache
    • core_flag
    • core_resource
    • core_session
    • core_store
    • core_translate
    • core_variable
    • core_website
    • email
      • core_email_template
    • layout
      • core_layout_link
      • core_layout_update
    • store
      • core_store_group
    • url
      • core_url_rewrite
    • variable
      • core_variable_value
  • coupon
    • aggregated
      • coupon_aggregated_order
      • coupon_aggregated_updated
    • coupon_aggregated
  • cron
    • cron_schedule
  • customer
    • address
      • customer_address_entity
      • entity
        • customer_address_entity_datetime
        • customer_address_entity_decimal
        • customer_address_entity_int
        • customer_address_entity_text
        • customer_address_entity_varchar
    • customer_entity
    • customer_group
    • eav
      • attribute
        • customer_eav_attribute_website
      • customer_eav_attribute
    • entity
      • customer_entity_datetime
      • customer_entity_decimal
      • customer_entity_int
      • customer_entity_text
      • customer_entity_varchar
    • form
      • customer_form_attribute
  • dataflow
    • batch
      • dataflow_batch_export
      • dataflow_batch_import
    • dataflow_batch
    • dataflow_profile
    • dataflow_session
    • import
      • dataflow_import_data
    • profile
      • dataflow_profile_history
  • design
    • design_change
  • directory
    • country
      • directory_country_format
      • directory_country_region
      • region
        • directory_country_region_name
    • currency
      • directory_currency_rate
    • directory_country
  • downloadable
    • downloadable_link
    • downloadable_sample
    • link
      • downloadable_link_price
      • downloadable_link_purchased
      • downloadable_link_title
      • purchased
        • downloadable_link_purchased_item
    • sample
      • downloadable_sample_title
  • eav
    • attribute
      • eav_attribute_group
      • eav_attribute_label
      • eav_attribute_option
      • eav_attribute_set
      • option
        • eav_attribute_option_value
    • eav_attribute
    • eav_entity
    • entity
      • eav_entity_attribute
      • eav_entity_datetime
      • eav_entity_decimal
      • eav_entity_int
      • eav_entity_store
      • eav_entity_text
      • eav_entity_type
      • eav_entity_varchar
    • form
      • eav_form_element
      • eav_form_fieldset
      • eav_form_type
      • fieldset
        • eav_form_fieldset_label
      • type
        • eav_form_type_entity
  • find
    • feed
      • import
        • find_feed_import_codes
  • gift
    • gift_message
  • googlebase
    • googlebase_attributes
    • googlebase_items
    • googlebase_types
  • googlecheckout
    • googlecheckout_notification
  • googleoptimizer
    • googleoptimizer_code
  • importexport
    • importexport_importdata
  • index
    • index_event
    • index_process
    • process
      • index_process_event
  • log
    • log_customer
    • log_quote
    • log_summary
    • log_url
    • log_visitor
    • summary
      • log_summary_type
    • url
      • log_url_info
    • visitor
      • log_visitor_info
      • log_visitor_online
  • newsletter
    • newsletter_problem
    • newsletter_queue
    • newsletter_subscriber
    • newsletter_template
    • queue
      • newsletter_queue_link
      • store
        • newsletter_queue_store_link
  • paypal
    • payment
      • paypal_payment_transaction
    • paypal_cert
    • settlement
      • paypal_settlement_report
      • report
        • paypal_settlement_report_row
  • persistent
    • persistent_session
  • poll
    • poll
    • poll_answer
    • poll_store
    • poll_vote
  • product
    • alert
      • product_alert_price
      • product_alert_stock
  • rating
    • option
      • rating_option_vote
      • vote
        • rating_option_vote_aggregated
    • rating
    • rating_entity
    • rating_option
    • rating_store
    • rating_title
  • report
    • compared
      • product
        • report_compared_product_index
    • event
      • report_event_types
    • report_event
    • viewed
      • product
        • report_viewed_product_index
  • review
    • entity
      • review_entity_summary
    • review
    • review_detail
    • review_entity
    • review_status
    • review_store
  • sales
    • bestsellers
      • aggregated
        • sales_bestsellers_aggregated_daily
        • sales_bestsellers_aggregated_monthly
        • sales_bestsellers_aggregated_yearly
    • billing
      • agreement
        • sales_billing_agreement_order
      • sales_billing_agreement
    • flat
      • creditmemo
        • sales_flat_creditmemo_comment
        • sales_flat_creditmemo_grid
        • sales_flat_creditmemo_item
      • invoice
        • sales_flat_invoice_comment
        • sales_flat_invoice_grid
        • sales_flat_invoice_item
      • order
        • sales_flat_order_address
        • sales_flat_order_grid
        • sales_flat_order_item
        • sales_flat_order_payment
        • status
          • sales_flat_order_status_history
      • quote
        • address
          • sales_flat_quote_address_item
        • item
          • sales_flat_quote_item_option
        • sales_flat_quote_address
        • sales_flat_quote_item
        • sales_flat_quote_payment
        • shipping
          • sales_flat_quote_shipping_rate
      • sales_flat_creditmemo
      • sales_flat_invoice
      • sales_flat_order
      • sales_flat_quote
      • sales_flat_shipment
      • shipment
        • sales_flat_shipment_comment
        • sales_flat_shipment_grid
        • sales_flat_shipment_item
        • sales_flat_shipment_track
    • invoiced
      • aggregated
        • sales_invoiced_aggregated_order
      • sales_invoiced_aggregated
    • order
      • aggregated
        • sales_order_aggregated_created
        • sales_order_aggregated_updated
      • sales_order_status
      • sales_order_tax
      • status
        • sales_order_status_label
        • sales_order_status_state
      • tax
        • sales_order_tax_item
    • payment
      • sales_payment_transaction
    • recurring
      • profile
        • sales_recurring_profile_order
      • sales_recurring_profile
    • refunded
      • aggregated
        • sales_refunded_aggregated_order
      • sales_refunded_aggregated
    • shipping
      • aggregated
        • sales_shipping_aggregated_order
      • sales_shipping_aggregated
  • salesrule
    • coupon
      • salesrule_coupon_usage
    • product
      • salesrule_product_attribute
    • salesrule
    • salesrule_coupon
    • salesrule_customer
    • salesrule_label
  • sendfriend
    • sendfriend_log
  • shipping
    • shipping_tablerate
  • sitemap
    • sitemap
  • tag
    • tag
    • tag_properties
    • tag_relation
    • tag_summary
  • tax
    • calculation
      • rate
        • tax_calculation_rate_title
      • tax_calculation_rate
      • tax_calculation_rule
    • order
      • aggregated
        • tax_order_aggregated_created
        • tax_order_aggregated_updated
    • tax_calculation
    • tax_class
  • weee
    • weee_discount
    • weee_tax
  • widget
    • instance
      • page
        • widget_instance_page_layout
      • widget_instance_page
    • widget
    • widget_instance
  • wishlist
    • item
      • wishlist_item_option
    • wishlist
    • wishlist_item
  • xmlconnect
    • config
      • xmlconnect_config_data
    • notification
      • xmlconnect_notification_template
    • xmlconnect_application
    • xmlconnect_history
    • xmlconnect_queue

Statische Blöcke in mehrsprachigen Shops optimal verwenden

Ein mehrsprachiger Shop wird oft mit Hilfe von StoreViews umgesetzt, wobei jeder StoreView die Inhalte in der jeweiligen Sprache anzeigt.

Hin und wieder beobachte ich, dass für verschiedene StoreViews auch Blöcke mit verschiedenen Seitenbezeichnern (engl.: Identifier) angelegt werden, etwa myblock_de für den deutschsprachigen und myblock_en für den englischsprachigen StoreView. Beide Blöcke sind dabei in allen StoreViews sichtbar:

Falsche Verwendung von statischen Blocks in mehrsprachigen Magento-Shops

Dabei können in Magento mehrere Blöcke mit dem gleichen Seitenbezeichner angelegt werden (z.B. myblock), sofern sie sich in der Einstellung StoreView unterscheiden (andernfalls wird der Fehler „Ein Blockbezeichner mit den gleichen Einstellungen existiert für den ausgewählten Store bereits.“ gemeldet):

Richtige Verwendung von statischen Blocks in mehrsprachigen Magento-Shops

So kann in verschiedenen StoreViews ein Block mit dem gleichen Seitenbezeichner verwendet werden. In einem mehrsprachigen Shop würde dies die Konsistenzhaltung der Daten erleichtern.

Weiterlesen:

Static Blocks Everywhere: Statische Blöcke in Bestellbedingungen, Produkt- und Kategoriebeschreibung nutzen

Static Blocks Everywhere auf Magento Connect

Statische Blöcke helfen, Inhalte in Magento konsistent zu halten. Mit dem nachfolgenden CMS-Code lassen sich statische Blöcke einbinden:

{{block type="cms/block" block_id="myblock"}}

Im Frontend wird der CMS-Code durch den Inhalt des statischen Blocks mit dem Seitenbezeichner myblock ersetzt. Einrichtung statischer Blöcke ist in Menü CMS > Statische Blöcke möglich. Als Inhalt kann dabei ein einfacher Text oder ein komplexer HTML-Code gespeichert werden.

Magento unterstützt statische Blöcke nur innerhalb von anderen statischen Blöcken und auf Seiten des Content Management Systems. Möchte man einen statischen Block direkt in Kategoriebeschreibung oder in Produktbeschreibung verwenden, wird der CMS-Code nicht interpretiert, d. h. er wird nicht durch den im statischen Block gespeicherten Inhalt ersetzt. Auch können statische Blöcke nicht in Bestellbedingungen verwendet werden.

Mit der kostenfreien Erweiterung Static Blocks Everywhere lassen sich statische Blöcke in Magento überall einsetzen:

  • im Inhalt einer Bestellbedingung
  • und in der Beschriftung der zugehörigen Checkbox,
  • in der Beschreibung
  • und Kurzbeschreibung eines Produkts
  • und in der Beschreibung einer Kategorie.

Der Screenshot zeigt, dass nach der Installation von Static Blocks Everywhere statische Blöcke in der Bestellbedingung und der Beschriftung der zugehörigen Checkbox interpretiert werden:

Static Blocks in Terms and Conditions

Die Vorlage für diese Extension lieferte diese Anleitung, die ich vor längerer Zeit geschrieben hatte.

Siehe auch: Statische Blöcke im Template (PHP-Code) und Layout (XML-Code) verwenden

Im Checkout einen Schritt weniger anzeigen

Viele Schritte im Checkout könnten den Kunden abschrecken und dazu verleiten, z. B. das gleiche Produkt auf einer anderen Plattform zu kaufen, wo er bereits ein Kundenkonto besitzt. Deshalb ist es ratsam, den Checkout zu optimieren. Eine der Strategien besteht in der Reduktion der Schritte, die der Kunde angezeigt bekommt.

Davon ausgehend, dass die Rechnungsanschrift mit der Lieferanschrift übereinstimmt, wird bei den meisten Kunden der Schritt „Versandinformation“ übersprungen. Mit einer einfachen Modifikation ist es möglich, diesen Schritt auszublenden und erst anzuzeigen, wenn der Kunde sich für die Option „An andere Adresse verschicken“ entscheidet.

Um dies zu erzielen, wird in die Datei template\checkout\onepage.phtml ganz unten im Code das folgende Skript eingefügt:

<script type="text/javascript">

    document.body.onload = displayShipping;

    document.getElementById('billing:use_for_shipping_no').onchange = displayShipping;
    document.getElementById('billing:use_for_shipping_yes').onchange = displayShipping; 

    function displayShipping()
    {
        if(document.getElementById('billing:use_for_shipping_no').checked)
        document.getElementById('opc-shipping').style.display = '';
        else
        document.getElementById('opc-shipping').style.display = 'none';
    }
</script>

Wenn Sie diese Datei in Ihrem Theme-Verzeichnis nicht finden, kopieren sie Sie inklusive der Verzeichnisstruktur aus dem Base-Theme (Tipp: Magento-Themes einfacher erstellen und modifizieren).

Der Schritt „Versandinformation“ ist ausgeblendet:

Erst wenn der Kunde sich für die Option „An andere Adresse verschicken“ entscheidet, wird der Zusatzschritt angezeigt:

Eine dynamische Anpassung der Nummerierung wäre aufwändig. Mit CSS können die Nummern einfach ausgeblendet werden. Der nachfolgende Code kann direkt nach dem JavaScript in onepage.phtml eingefügt werden:

<style type="text/css">
  .number { display: none }
</style>

Der Link „Ändern“ im Seitenblock „Versandadresse“ wird nach dieser Anpassung nicht mehr erwartungsgemäß funktionieren, sofern der zugehörige Schritt ausgeblendet ist:

Um dieses Problem zu beheben, kann das JavaScript in der Datei js\varien\accordion.js angepasst werden, welches für das Einblenden einzelner Checkout-Schritte zuständig ist. In der Funktion openSection ist in dem If-Block, nach Zeile 55 der folgende Code einzufügen:

$(this.currentSection).style.display = '';

Nach dieser Modifikation sieht die Funktion openSection wie folgt aus (neue Zeile markiert):

openSection: function(section) {
    var section = $(section);

    // Check allow
    if (this.checkAllow && !Element.hasClassName(section, 'allow')){
        return;
    }

    if(section.id != this.currentSection) {
        this.closeExistingSection();
        this.currentSection = section.id;
        $(this.currentSection).addClassName('active');
        $(this.currentSection).style.display = '';
        var contents = Element.select(section, '.a-item');
        contents[0].show();
        //Effect.SlideDown(contents[0], {duration:.2});

        if (this.disallowAccessToNextSections) {
            var pastCurrentSection = false;
            for (var i=0; i<this.sections.length; i++) {
                if (pastCurrentSection) {
                    Element.removeClassName(this.sections[i], 'allow')
                }
                if (this.sections[i].id==section.id) {
                    pastCurrentSection = true;
                }
            }
        }
    }
},

Ordnungshalber sollte das modifizierte JavaScript im eigenen Skin-Verzeichnis gespeichert werden, beispielsweise in skin\frontend\default\my_theme\js\accordion.js. Der Verweis in onepage.phtml müsste entsprechend angepasst werden:

<script type="text/javascript" src="<?php echo $this->getSkinUrl('js/accordion.js') ?>"></script>

Speicherverbrauch der Magento-Datenbank um mehr als 80% reduzieren

Ungenutzte Tracking-Informationen können über 80% des Speicherplatzes ausmachen, den die Magento-Datenbank belegt.

Üblicherweise wird eine andere Tracking-Software eingesetzt (z. B. Google Analytics), sodass die Tracking-Daten von Magento ungenutzt bleiben und nur die Größe der Datenbank und folglich aller Backups aufblähen.

Speicherverbrauch analysieren

Magento speichert Tracking-Daten in den Tabellen log_url, log_url_info, log_visitor, log_visitor_info. Bei einem gut besuchten Shop belegen diese vier Tabellen stets die ersten Plätze bei der Sortierung nach Datenlänge:

Tabellenstatus im MySQL Administrator

Mit dem passenden SHOW-Befehl lassen sich Informationen über alle Log-Tabellen einschließlich Tracking-Tabellen anzeigen:

SHOW TABLE STATUS LIKE 'log%';

Eine SELECT-Abfrage auf INFORMATION_SCHEMA bietet mehr Möglichkeiten für die Aufbereitung und Sortierung der Ausgabetabelle. Mit dem folgenden Befehl lässt sich die oben gezeigte grafische Ansicht des MySQL Administrators nachbilden:

SELECT
   TABLE_NAME AS 'Tabelenname',
   ENGINE AS 'Engine',
   TABLE_ROWS AS 'Zeilen',
   DATA_LENGTH / 1048576 AS 'Datenlänge',
   INDEX_LENGTH / 1048576 AS 'Indexlänge',
   UPDATE_TIME AS 'Aktualisierungszeit'
FROM
   information_schema.TABLES t
WHERE TABLE_SCHEMA = DATABASE()
   AND TABLE_NAME LIKE 'log%'
ORDER BY DATA_LENGTH DESC;

Mit den nachfolgenden Befehlen lässt sich der prozentuale Speicherverbrauch berechnen. Der erste Befehl speichert dabei Spaltensummen in Variablen, damit die nachfolgenden zwei Befehle auf verlangsamende Unterabfragen verzichten können:

-- 1. Gesamten Speicherverbrauch der aktuellen Datenbank in Variablen speichern und anzeigen
SELECT
  (@data_length := SUM(DATA_LENGTH)) / 1048576 AS data_length,
  (@index_length := SUM(INDEX_LENGTH)) / 1048576 AS index_length,
  (@total_length := SUM(INDEX_LENGTH + DATA_LENGTH)) / 1048576 AS total_length
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE();

-- 2.1. Prozentualen Speicherverbrauch der log-Tabellen
SELECT TABLE_NAME,
  ROUND(DATA_LENGTH / @data_length * 100, 2) AS DATA_LENGTH_RATIO,
  ROUND(INDEX_LENGTH / @index_length * 100, 2) AS INDEX_LENGTH_RATIO,
  ROUND((INDEX_LENGTH + DATA_LENGTH) / @total_length * 100, 2) AS TOTAL_LENGTH_RATIO
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME LIKE 'log%'
ORDER BY TOTAL_LENGTH_RATIO DESC;

-- 2.2. Prozentualen Speicherverbrauch der log-Tabellen zusammengefasst
SELECT
  ROUND(SUM(DATA_LENGTH / @data_length * 100), 2) AS DATA_LENGTH_RATIO,
  ROUND(SUM(INDEX_LENGTH / @index_length * 100), 2) AS INDEX_LENGTH_RATIO,
  ROUND(SUM((INDEX_LENGTH + DATA_LENGTH) / @total_length * 100), 2) AS TOTAL_LENGTH_RATIO
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME LIKE 'log%'
ORDER BY DATA_LENGTH_RATIO DESC;

Beispielausgabe für Abfrage 1 (Anzeige in Megabyte):

+-------------+--------------+--------------+
| data_length | index_length | total_length |
+-------------+--------------+--------------+
|    277.4169 |      54.4785 |     331.8954 |
+-------------+--------------+--------------+
1 row in set (3.48 sec)

Vor dem Ausführen der Abfragen 2.1. und 2.2. muss die Abfrage 1 ausgeführt werden.
Beispielausgabe für Abfrage 2.2 (Anzeige in Prozent):

+-------------------+--------------------+--------------------+
| DATA_LENGTH_RATIO | INDEX_LENGTH_RATIO | TOTAL_LENGTH_RATIO |
+-------------------+--------------------+--------------------+
|             89.27 |              41.64 |              81.45 |
+-------------------+--------------------+--------------------+
1 row in set (0.50 sec)

In diesem Beispiel belegt die Datenbank von Magento 332 MB. Davon nehmen die Daten der Log-Tabellen beträchtliche 81,45% ein!

Tracking in Magento deaktivieren

Eine Deaktivierung des Tracking ist in Magento nicht vorgesehen. Mit einem Wartungsskript könnten die entsprechenden Tabellen regelmäßig geleert werden. Durch den Wechsel der Storage Engine auf BLACKHOLE geht es noch eleganter:

ALTER TABLE log_url ENGINE = BLACKHOLE;
ALTER TABLE log_url_info ENGINE = BLACKHOLE;
ALTER TABLE log_visitor ENGINE = BLACKHOLE;
ALTER TABLE log_visitor_info ENGINE = BLACKHOLE;

Im Code von Magento sind keine Änderungen nötig. Die Anwendungsschicht sendet Tracking-Daten weiterhin an die Datenbank, wo sie von dem „schwarzen Loch“ verschluckt werden. Die Tabellen bleiben leer.

MySQL-Werkzeuge

MySQL Workbench

MySQL Workbench ist die grafische Umgebung von MySQL, verfügbar unter vielen Betriebssystemen, darunter Windows, Linux und Mac OS X. In den drei Bereichen bietet die Workbench bequeme Werkzeuge für Entwurf, Entwicklung und Administration von MySQL-Datenbanken.

Die Workbench kann sich über die gängigen Methoden direkt mit dem MySQL-Server verbinden, aber auch eine verschlüsselte Verbindung über einen SSH-Tunnel aufbauen.

Offizielle Seite der MySQL Workbench mit Download

Die Workbench soll die MySQL GUI Tools ablösen, die nicht mehr weiterentwickelt werden. GUI Tools war eine Sammlung aus grafischen Werkzeugen für MySQL, bestehend u. a. aus dem MySQL Query Browser, MySQL Administrator und MySQL Migration Toolkit. Die Workbench deckt viele wichtige Funktionen der GUI Tools ab.

phpMyAdmin

phpMyAdmin ist eine beliebte Browseroberfläche für die Verwaltung von MySQL-Datenbanken.

Offizielle Seite des phpMyAdmin mit Download

phpMiniAdmin

Wem phpMyAdmin zu umfangreich ist, der sollte phpMiniAdmin kennen. Dieser besteht aus einer einzigen php-Datei und bietet alle wichtigen Funktionen zum Verwalten einer Datenbank und Ausführen eigener SELECT-Anweisungen.

phpMiniAdmin auf SourceForge mit Download

Beispiel für ein optimiertes Magento Checkout

Dies ist ein Beispiel zum Artikel Checkout Optimierung, der die Gestaltung eines Magento Checkouts demonstriert. Die meisten Anpassungen wurden mit CSS realisiert:

  • Klare visuelle Trennung zwischen den Bereichen Neuer Kunde und Anmelden
  • Optionale Telefonnummer und keine Auswahl des Bundeslandes bei der Adressangabe
  • PNG-Icons für Versand- und Zahlungsarten

Magento Checkout: Wie möchten Sie zur Kasse gehen?

Magento Checkout: Rechnungsinformation

Magento Checkout: Versandart

Magento Checkout: Zahlungsinformation

Marktanteile von Magento

Marktanteile von Magento: Zum Update vom 30.05.2014.

Wappalyzer liefert interessante Einblicke in die Entwicklung und Verteilung der Marktanteile von Software im Web. Mit einem Marktanteil von 25% hat Magento das Rennen bereits gewonnen, Stand: 30.05.2012:

Marktanteile von Magento

Datenquelle und Datenqualität

Einige Daten bekommt Wappalyzer über das Open Source Add-On für Firefox und Chrome.

Wappalyzer in FireFox

Das auf dem Server eingesetzte Betriebssystem und einige andere Informationen werden vermutlich aus Meldungen des Webservers ausgelesen über Banner Grabbing oder durch Aufruf nicht vorhandener Unterseiten. Hierbei ist zu beachten, dass ein sicherheitstechnisch gut konfigurierter Webserver die eingesetzte Software nicht nach Außen preisgibt. In bestimmten Softwarekategorien sind deshalb Verzerrungen zu erwarten, da der Anteil nicht erkannter Software nicht angegeben wird.

Die auf einer Webseite eingesetzte Software wird vermutlich anhand von typischen Codemustern, die diese Software ausgibt, erkannt. In den Kategorien der auf dem Webserver laufender Software, wie Online-Shops und Content Management Systeme, sind dementsprechend relativ gute Statistiken zu erwarten, auch wenn Wappalyzer hier fälschlicherweise Cambio (ein Online-Shop) erkannt hat.

Wappalyzer zählt sowohl die Anzahl der Webseiten, die eine Software einsetzen, als auch die Anzahl der Aufrufe (Pageviews). In der Top 5 einer Kategorie ist das Ranking nach Anzahl der Webseiten und Pageviews meistens identisch. Allerdings beschränkt sich die Nutzergemeinschaft des Add-Ons wahrscheinlich auf Webdesigner,  weshalb bei den Pageviews eine starke Verzerrung der tatsächlichen Marktanteile zu erwarten ist. Die Kreisdiagramme werden auf Basis der Anzahl von Webseiten berechnet.

Auch nach Google Trends geht Magento als klarer Sieger hervor, wenn auch in einem anderen Verhältnis als von Wappalyzer berechnet:

Google Trends: Magento und andere e-Commerce-Plattformen

Datenschutz

Bei der Nutzung des Add-Ons ist zu berücksichtigen, dass neben „allgemeiner Informationen“ alle Domainnamen, die Sie in Ihrem Browser aufrufen, an Wappalyzer gesendet werden. In der Datenschutzerklärung von Wappalyzer heißt es, Zitat: „Any time you use the extension to analyze a website, the extension sends basic, limited information including the domain name of the website you have visited and what application was identified on it. This data is retained in Wappalyzer’s server logs and protected according to our general Privacy Policy. At no time this data can be traced back to you personally, since no personally identifiable information is stored.“ Ich hoffe, dass Wappalyzer seiner Datenschutzerklärung treu bleibt und tatsächlich nur die Domainnamen ohne die IP-Adresse übermittelt.

Marktanteile nach Regionen

Derzeitig bietet Wappalyzer keine Aufteilung der Statistik nach Regionen. Entsprechend der Datenschutzerklärung werden IP-Adressen der Add-On-Nutzer nicht gesammelt, sodass die regionale Verteilung der Nutzer nicht ausgewertet werden kann. Der Serverstandort kann jedoch aus der IP einer Webseite ausgelesen werden. Damit wäre die regionale Aufteilung der Marktanteile ohne Verletzung der Privatsphäre möglich und wird hoffentlich demnächst angeboten.

Ähnliche Anbieter

W³Techs fasst Websoftware in nur einer Kategorie zusammen, gibt dafür aber aufschlussreiche Einblicke in Marktanteile von Webtechnologien, darunter Bildformate und Zeichenkodierung.

W³Techs verwendet für die Auswertung die Top 1 Mio. Webseiten in Alexa.com. Im Menü unter „Sites“ kann die eigene Webseite online ausgewertet werden. Auch W³Techs bietet darüber hinaus Browser-Add-Ons.

Update vom Meet Magento 2013 (03.06.2013)

Rund ein Jahr, nach dem ich diesen Artikel geschrieben habe, stagniert das Wachstum: in der Eröffnungspräsentation von Meet Magento 2013 hat Roy Rubin die Zahl 26% vorgestellt.

Als Quelle wurde die Analyse der Top 1 Mio. Webseiten in Alexa.com genannt. Es ist allerdings nicht verwunderlich, dass sich unter den populärsten Webseiten nicht viel ändert.

Fatal error: Call to undefined function curl_setopt() in lib\Varien\Http\Adapter\Curl.php on line …

Dieser Fehler kann beim Aufruf des Magento-Backends nach der Wiederherstellung eines Magento-Shops entstehen.

Beispielfehlermeldung

Fatal error: Call to undefined function curl_setopt() in lib\Varien\Http\Adapter\Curl.php on line 52

Typische Ursache

Die PHP-Erweiterung cURL ist deaktiviert.

Fehlerbehebung in XAMPP unter Windows

In einer XAMPP-Umgebung unter Windows ist die PHP-Erweiterung cURL standardmäßig deaktiviert. Aktivieren Sie diese Erweiterung in der Konfigurationsdatei php.ini durch Entfernen des voranstehenden Semikolons. Diese ist häufig direkt im PHP-Verzeichnis (z. B. xampp\php) zu finden.

Ändern Sie in php.ini die Zeile

;extension=php_curl.dll

in

extension=php_curl.dll

Starten Sie anschließend den Apache-Webserver neu.

Fehlerbehebung auf dem Ubuntu Server

Auf dem Ubuntu Server 10.04 LTS ist standardmäßig kein cURL installiert. Die Installation kann wie folgt durchgeführt werden:

sudo apt-get update
sudo apt-get install curl
sudo apt-get install php5-curl
sudo /etc/init.d/apache2 restart

ERROR 2006 (HY000) at line …: MySQL server has gone away

Dieser Fehler kann bei der Wiederherstellung einer Magento-Datenbank durch Import eines SQL-Skripts entstehen.

Beispielfehlermeldung

ERROR 2006 (HY000) at line 101: MySQL server has gone away

Typische Ursache

Der MySQL-Server empfängt ein Paket, das zu groß ist, und schließt daraufhin sofort die Verbindung. Große Pakete entstehen häufig durch multiple Inserts (Einfügen mehrerer Datensätze in einem INSERT-Statement) und durch BLOB-Datensätze.

Fehlerbehebung

Die maximale Paketgröße ist standardmäßig auf nur 1 MB eingestellt. Erhöhen Sie die maximale Paketgröße auf z. B. 24 MB. Tragen Sie folgende Zeile in die Konfigurationsdatei des MySQL-Servers ein:

[mysqld]
...
max_allowed_packet = 24M
...

Achten Sie darauf, M als Einheit (statt MB) eingetragen zu haben. Starten Sie den MySQL-Server nach dem Bearbeiten der Konfigurationsdatei neu.

Die Konfigurationsdatei my.ini ist in einer XAMPP-Umgebung unter Windows in dem Verzeichnis xampp\mysql\bin zu finden. In einer LAMPP-Umgebung unter Linux befindet sich die Konfigurationsdatei my.cfg im Verzeichnis /opt/lampp/etc oder /etc/mysql.

Sollte diese Einstellung das Problem nicht beheben, ist in der MySQL-Dokumentation ein umfangreicher Artikel diesem Fehler gewidmet.

ERROR 1005 (HY000) at line …: Can’t create table ‚…‘ (errno: 150)

Dieser Fehler kann bei der Wiederherstellung einer Magento-Datenbank durch Import eines SQL-Skripts entstehen.

Beispielfehlermeldung

ERROR 1005 (HY000) at line 209: Can't create table 'magento.api_session' (errno: 150)

Häufige Ursache

In diesem Fall wird der Fehler durch den Fremdschlüssel verursacht, der eine Tabelle referenziert, die erst später im Import-Script angelegt wird.

Fehlerbehebung

Deaktivieren Sie die Prüfung von Fremdschlüsselbedingungen durch Einfügen der folgenden Zeile am Anfang des Import-Skripts:

SET foreign_key_checks = 0;

Die Fremdschlüsselüberprüfung wird nur für die aktuelle Verbindung deaktiviert. Für bestehende und neue Verbindungen zum MySQL-Server bleibt die Prüfung von Fremdschlüsselbedingungen weiterhin aktiviert. Die explizite Aktivierung am Ende des Importskripts ist deshalb nicht erforderlich.

Für Bearbeitung großer SQL-Dateien ist EditPad besonders gut geeignet.

Fatal error: Call to a member function extend() on a non-object in app/code/core/Mage/Core/Model/Mysql4/Config.php on line …

Dieser Fehler kann beim Aufruf des Frontends der Wiederherstellung einer Magento-Datenbank durch Import eines SQL-Skripts entstehen.

Beispielfehlermeldung

Fatal error: Call to a member function extend() on a non-object in app/code/core/Mage/Core/Model/Mysql4/Config.php on line 115

Typische Ursache

Bei einigen Datensätzen (z. B. in die Tabellen core_store und core_website) wird 0 in AUTO_INCREMENT-Spalten eingefügt. Das Einfügen einer 0 (und NULL) in einer solche Spalte erzeugen in MySQL standardmäßig die nächste sequentielle Nummer, sodass die 1 an Stelle der 0 eingetragen wird.

Fehlerbehebung

Aktivieren Sie den passenden Servermodus durch Einfügen der folgenden Zeile am Anfang des Import-Skripts:

SET SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO';

Diese Einstellung gilt nur für die aktuelle Verbindung. Andere und neue Verbindungen zum MySQL-Server werden davon nicht beeinflusst.

Führen Sie anschließend die Wiederherstellung erneut durch.

Bei großen Dateien lässt sich die o. g. Zeile mit dem Linux-Befehl cat anfügen. Hierbei werden zwei Dateien zu einer neuen zusammengeführt:

cat Zusatzzeile.sql Datenbank.sql > Datenbank_mit_Zusatzzeile.sql

Quellcode-Editoren

Quellcode-Editoren sind leichtgewichtige Programme, mit den Quellcode bequem bearbeitet und lokal analysiert werden kann.

Für größere Projekte und umfangreiche Code-Analyse sind Entwicklungsumgebungen (engl. Integrated Development Environment oder kurz IDE) eher geeignet. Eine geeignete Entwicklungsumgebung sollte einen schnellen und übersichtlichen Klassenbrowser und eine Projektverwaltung bieten.

Notepad++

Notepad++ ist ein kostenfreier Editor, der durch seinen Funktionsumfang überzeugt.

Er bietet Syntaxhervorhebung und Code-Folding (das Zusammenklappen von Code-Blöcken). Mit dem Menü „Kodierung“ kann die Zeichenkodierung einer Datei schnell überprüft und geändert werden.

Zur Analyse fremder Quellcodes ist Notepad++ besonders gut geeignet. Er hebt sämtliche Vorkommnisse einer markierten Variablen automatisch hervor:

Notepad++

Durch STRG+Mausrad lässt sich der Code für mehr Übersicht anschließend herauszoomen.

Mit STRG+Leer lässt sich ein Vervollständigungsmenü einblenden, das relativ einfach gestrickt ist, aber dennoch – beispielsweise bei CSS – sehr hilfreich sein kann.

Bei der Suche unterstützt Notepad++ reguläre Ausdrücke. Diese lassen sich aber nicht durch Treffermarkierung wie beim EditPad interaktiv direkt im Editor entwerfen. Auch ist Notepad++ im Gegensatz zum EditPad nicht zum Bearbeiten großer Dateien geeignet.

Unter Linux läuft Editpad++ relativ stabil im Wine.

EditPad

EditPad ist ein Texteditor mit Syntaxhervorhebung und umfangreicher Suchfunktion.

Er versucht nicht, wie andere Editoren, die gesamte Datei in den Arbeitsspeicher zu laden, sondern nur den für die Anzeige relevanten Teil. Mehrere Gigabytes große SQL-Dateien können schnell geöffnet und bearbeitet werden.

Nachtrag vom 06.06.2012: Bearbeitung großer Textdateien im EditPad Pro kann nach neuester Erfahrung zu deren Beschädigung führen. Nach Bearbeitung und Speicherung eines nur 250 MB großen SQL-Skripts hat EditPad Pro ein oder mehrere Zeichen an einer von der Bearbeitung nicht betroffenen Stelle entfernt, sodass das Skript nicht mehr importiert werden konnte.

Mit der Treffermarkierung können reguläre Ausdrücke direkt im Editor schnell entwickelt werden:

EditPad

Mit dem Trefferzähler lässt sich anschließend überprüfen, ob der reguläre Ausdruck auch wirklich alle gewünschten Inhalte erfasst hat.

Unter Linux läuft EditPad relativ stabil im Wine.

EditPad kostet rund 40 €.

Reguläre Ausdrücke

Mit regulären Ausdrücken (Regular Expressions, oder kurz: RegExp) lassen sich anspruchsvolle Suchen und Ersetzungen durchführen und Benutzereingaben einfach überprüfen. Bei der Serverkonfiguration werden Regexp in Zusammenhang mit mod_rewrite verwendet.

Viele häufig gebrauchte reguläre Ausdrücke (z. B. zur Prüfung der eingegebenen E-Mail-Adresse) lassen sich schnell im Web finden. Mit geeigneten Werkzeugen lassen sich eigene RegExp formulieren und einfach testen.

regexpal.com

Mit regexpal.com lassen sich reguläre Ausdrücke direkt im Browser interaktiv entwickeln und testen.

regexpal.com

Es handelt sich um ein JavaScript; es muss keine Software heruntergeladen und installiert werden.

RegexBuddy

RegexBuddy kann beim Entwickeln anspruchsvoller regulärer Ausdrücke (z.B. für mod_rewrite) unfassbar viel Zeit sparen.

Ähnlich wie regexpal.com markiert RegexBuddy Treffer, kann darüberhinaus aber Ersetzungen durchführen.  Der Auswertungsbaum kann die Fehlersuche erleichtern.

RegexBuddy

Zusätzlich bietet RegexBuddy einen GREP-Ersatz für Windows und ermöglicht dateiübergreifendes Suchen und Ersetzen.

RegexBuddy kostet rund 30 €.

PayPal in Magento einrichten

Die Konfiguration von PayPal besteht aus Einstellungen im Backend von Magento und Einstellungen im PayPal-Konto. Diese Anleitung hilft Ihnen, PayPal „Website Payments Standard“ in Magento 1.4/1.5/1.6/1.7 einzurichten.

Notwendige Einstellungen in Magento

Einstellungen von PayPal sind in Magento Administration > System > Konfiguration > Verkäufe > PayPal erreichbar:Website Payments Standard – PayPal wickelt alle Ihre Bestellungen ab und Sie werden bezahlt.

  1. Wählen Sie „Deutschland“ als Händler-Land. Die Liste der PayPal-Lösungen wird daraufhin angepasst.
  2. Geben Sie die E-Mail-Adresse Ihres PayPal Händlerkontos ein. Es ist die E-Mail-Adresse, die Sie zum Einloggen in Ihr PayPal-Konto verwenden. Klicken Sie nach Eingabe mit der linken Maustaste außerhalb des Textfeldes, damit Magento die Eingabe annimmt und die Auswahl der PayPal-Lösung für den nächsten Schritt freigibt.
  3. Wählen Sie die PayPal-Lösung Website Payments Standard.
  4. Vergewissern Sie sich, dass die Voreinstellungen in dem Block Website Payments Standard Einstellungen übereinstimmen:
    • Titel – Es ist egal, was Sie in dieses Feld eintragen. Diese Einstellung wird ignoriert, da an Stelle des Titels bei der Auswahl der Zahlungsmethode dem Kunden das grafische PayPal-Logo angezeigt wird. Bei anderen Zahlungsschnittstellen bestimmt diese Einstellung die Bezeichnung der Zahlungsmethode, die dem Kunden angezeigt wird.
    • Reihenfolge – Sie können dieses Feld frei lassen. Diese Einstellung bestimmt die Position von PayPal in der Auswahlliste der Zahlungsmethoden, die dem Kunden angezeigt wird. Je höher die Zahl, desto niedriger die Position in der Liste.
    • Zahlungsvorgang – Empfohlene Einstellung: Verkauf. Bei dieser Einstellung wird jede Kundenzahlung automatisch akzeptiert. Mit der Option Autorisierung müssen Sie jede Zahlung in Ihrem PayPal-Konto manuell akzeptieren.
    • Zahlung möglich von – Empfohlene Einstellung: Alle erlaubten Länder. Mit dieser Einstellung können Sie PayPal für bestimmte Länder deaktivieren. Ausschlaggebend dabei ist die Rechnungsadresse: Wählt der Kunde ein Land, welches nicht auf der Liste erlaubter Länder steht, wird PayPal in der Auswahl der Zahlungsmethode nicht angezeigt.
    • Sandbox Modus – Empfohlene Einstellung: Nein. PayPal bietet Entwicklern die Möglichkeit, ihre Software mit „Spielgeld“ zu testen. Unter developer.paypal.com können beliebig viele virtuelle Händler- und Kundenkonten eingerichtet werden. Für die Einrichtung von PayPal in Magento ist dieser Aufwand allerdings nicht nötig. Obwohl PayPal die Transaktionsgebühren bei Erstattung nicht vollständig zurück gibt, kann für Tests ein Produkt mit einem niedrigen Preis angelegt werden. So halten sich die durch Tests entstehenden Transaktionsgebühren in Grenzen.
    • Artikel des Warenkorbs übertragen – Empfohlene Einstellung: Ja. Magento überträgt die Liste der Warenkorbartikel an PayPal, sodass der Kunde direkt in seinem PayPal-Konto die Zusammensetzung des Gesamtbetrags nachvollziehen kann. Wählen Sie Nein, wenn Sie nur den Gesamtbetrag der Bestellung übermitteln und PayPal damit weniger über das Kaufverhalten Ihrer Kunden verraten wollen.
    • Debug Modus – Empfohlene Einstellung: Nein. Diese Option kann bei der Fehlersuche nützlich sein. Wird sie aktiviert, schreibt Magento in eine Log-Datei zusätzliche Informationen über PayPal-Transaktionen.
  5. Im Block Individuelle Gestaltung können Sie Einfluss auf das Aussehen der PayPal-Zahlungsseite nehmen. In diesem Block empfehle ich, nur den Namen der Designvorlage zu verwenden, die Sie später im PayPal-Konto einrichten werden. Der Name der Vorlage darf hier frei gewählt werden. Geben Sie in das Feld Seiten Gestaltung z. B. MeinDesign ein.
  6. Speichern Sie Ihre Einstellungen mit der Schaltfläche Konfiguration speichern.

Nach dem Speichern der Einstellungen erscheint PayPal in der Auswahlliste der Zahlungsmethoden:

Zahlungsinformation - Akzeptanz-Markierung  Was ist PayPal? - Sie werden zur PayPal Webseite weitergeleitet wenn Sie die Bestellung abschließen.

Ohne Eingriff in das Template können Sie keinen Einfluss auf das Aussehen der PayPal-Zahlungsoption nehmen. Wird PayPal ausgewählt, erscheint ein Text unterhalb. Wenn Sie diesen Text anpassen wollen, fügen Sie die folgende Zeile der Sprachdatei hinzu:

"You will be redirected to the PayPal website when you place an order.","Sie werden zur PayPal Webseite weitergeleitet wenn Sie die Bestellung abschließen."

Den deutschsprachigen Text nach dem Komma können Sie ändern. Bearbeiten Sie die Datei mit einem einfachen Texteditor, nicht mit Excel. Mit der Sprachdatei ist die Datei translate.csv in Ihrem Theme-Verzeichnis gemeint:

app\design\frontend\default\my_theme\locale\de_DE\translate.csv

Existiert diese Datei nicht, legen Sie sie inklusive der Verzeichnisstruktur an.

Bereits jetzt könnten Sie von Ihren Kunden PayPal-Zahlungen erhalten, diese werden von Magento jedoch nicht registriert. Für die automatische Bestellabwicklung müssen weitere Einstellungen im PayPal-Konto durchgeführt werden.

Notwendige Einstellungen im PayPal-Konto

Nach dem die E-Mail-Adresse für PayPal-Zahlungen eingetragen und die Zahlungsmethode „Website Payments Standard“ aktiviert wurde, können bereits PayPal-Zahlungen empfangen werden. Für die reibungslose Abwicklung und Bestellverwaltung müssen notwendige Einstellungen im PayPal-Konto durchgeführt werden.

Die Konfiguration des PayPal-Kontos für PayPal-Zahlungen in Magento besteht aus folgenden drei Bereichen, sortiert nach Wichtigkeit:

Register: Mein Konto, Menü: Mein Profil, Mehr..., Kategorie im Menü auf der linken Seite: Verkäufer/Händler, Benachrichtigung über Sofortzahlungen: Aktualisieren

Hinweis: Markierungen in diesem Screenshot wurden nachträglich eingearbeitet, sie sind keine Funktion von PayPal.

Einrichtung von Instant Payment Notification

Die Einrichtung von IPN (Instant Payment Notification oder sofortige Zahlungsbestätigung) ist die wichtigste Einstellung. Wenn IPN nicht eingerichtet ist, werden gesendete PayPal-Zahlungen von Magento nicht registriert. Als Konsequenz erscheint eine Bestellung, die mit PayPal bezahlt wurde, nicht im Kundenkonto und bekommt den Status Pending Payment (ausstehende Zahlung). Erst wenn Magento von PayPal eine IPN bekommen hat, wechselt der Bestellstatus auf Processing (Verarbeitung). Der Shop-Betreiber kann dabei sicher sein, dass die Zahlung erfolgreich abgeschlossen wurde, ohne das PayPal-Konto aufrufen zu müssen.

Zum Einrichten der IPN muss diese Funktion zunächst aktiviert werden. Vergewissern Sie sich, dass Sie in Ihrem PayPal-Konto den Reiter Mein Konto aktiviert haben. Im Menü Mein Profil, wählen Sie:

  1. Register: Mein Konto > Menü: Mein Profil > Mehr…
  2. Kategorie im Menü auf der linken Seite: Verkäufer/Händler
  3. Benachrichtigung über Sofortzahlungen: Aktualisieren
  4. Schaltfläche: Einstellungen für sofortige Zahlungsbestätigungen wählen
  5. Die Magento-spezifische Benachrichtigungs-URL eintragen: http://www.meinshop.de/paypal/standard/ipn/
  6. Sofortige Zahlungsbestätigungen erhalten aktivieren
  7. Schaltfläche: Speichern

Die Voraussetzung für den Empfang sofortiger Zahlungsbestätigungen ist die Erreichbarkeit der im Punkt 5 genannten Benachrichtigungs-URL im Web. Eine lokale Testumgebung von Magento wird o. w. keine IPN empfangen können. Auch ein im Web erreichbarer Magento-Shop, der mit einem .htaccess-Passwort geschützt wurde, kann keine IPN empfangen.

Im Folgenden wird die Einrichtung von IPN mit Screenshots veranschaulicht:

  1. Register: Mein Konto > Menü: Mein Profil > Mehr…
  2. Kategorie im Menü auf der linken Seite: Verkäufer/Händler
  3. Benachrichtigung über Sofortzahlungen: AktualisierenMitgliedsname, Meine eBay-ID mit meinem PayPal-Konto verknüpfen, Online verkaufen, PayPal-Buttons, Meine Zahlungsbuttons verwalten, Abrechnungsname, Name meines Geschäfts, wie er auf Kreditkartenabrechnung von Kunden angezeigt wird: SHIRTAUGHAF, Benutzerdefinierte Zahlungsseiten, PayPal-Zahlungsseiten so einrichten, dass sie wie meine eigene Website aussehen, Website-Einstellungen, Kunden nach der Zahlung mit PayPal auf meine Website zurückleiten, API-Zugriff, API-Berechtigungen verwalten, um mein PayPal-Konto in meinen Online-Shop oder Warenkorb zu integrieren, Rechnungsvorlagen, Meine Rechnungen erstellen und verwalten, Zahlungen erhalten und meine Risiken verwalten, Meine automatischen Zahlungen, Automatische Zahlungen verwalten, die ich meinen Kunden anbiete, Benachrichtigungen über Sofortzahlungen, PayPal-Zahlungsbenachrichtigungen in meine Website integrieren, Zahlungen sperren, Zahlungen limitieren, Anweisungen hinzufügen usw., Kundenservice-Mitteilung, Eine personalisierte Mitteilung für Kundenbeschwerden erstellen, Aktualisieren
  4. Schaltfläche: Einstellungen für sofortige Zahlungsbestätigungen wählenDie sofortige Zahlungsbestätigung (IPN) ist eine PayPal-Funktion, die zahlungsbezogene Nachrichten (und andere transaktionsbezogene Ereignisse) direkt von PayPal an die Back-End-Systeme Ihrer Website sendet.Sie können die Nachrichten der letzten 28 Tage anzeigen. Sie können auch ein - von den Back-End-Systemen Ihrer Website nicht empfangene Nachrichten erneut senden - den Nachrichtenerhalt vorübergehend deaktivieren (nützlich, wenn Wartungsarbeiten an Ihren Back-End-Systemen durchgeführt werden) - Die Nachrichten werden bei PayPal erzeugt und gespeichert, bis Sie den Erhalt wieder aktivieren. - Nutzen Sie die Funktion
  5. Die Magento-spezifische Benachrichtigungs-URL eintragen:http://www.meinshop.de/paypal/standard/ipn/
  6. Sofortige Zahlungsbestätigungen erhalten aktivieren
  7. Schaltfläche: SpeichernEinstellungen für sofortige Zahlungsbestätigungen (IPN) bearbeiten - PayPal sendet sofortige Zahlungsbestätigungen an die URL, die Sie unten angeben. - Um sofortige Zahlungsbestätigungen zu erhalten, geben Sie unten die Benachrichtigungs-URL ein, und wählen Sie Sofortige Zahlungsbestätigungen erhalten aus. Um den Erhalt von sofortigen Zahlungsbestätigungen vorübergehend zu deaktivieren, wählen Sie unten Keine sofortigen Zahlungsbestätigungen erhalten aus. Bei PayPal werden weiterhin sofortige Zahlungsbestätigungen erzeugt und gespeichert, bis Sie wieder Sofortige Zahlungsbestätigungen erhalten auswählen oder die Funktion

Einrichtung der Rückleitungs-URL

Als Rückleitungs-URL wird in PayPal die URL bezeichnet, wohin der Kunde nach erfolgreichem Abschluss der Zahlung gelangt. Dies ist die Seite mit der Meldung „Vielen Dank für Ihren Einkauf“, die dem Kunden einen erfolgreichen Abschluss seiner Zahlung signalisiert. Auf dieser Seite wird typischerweise auch der Conversion-Pixel eingebaut, der im Affiliate-Marketing den vermittelten Kauf verfolgt.

Zum Einrichten der Rückleitungs-URL folgen Sie dieser Anleitung:

  1. Register: Mein Konto > Menü: Mein Profil > Mehr…
  2. Kategorie im Menü auf der linken Seite: Verkäufer/Händler
  3. Website-Einstellungen: Aktualisieren
  4. Automatische Rückleitung: aktiviert
  5. Die Magento-spezifische Rückleitungs-URL eintragen:http://www.meinshop.de/paypal/standard/success/
  6. Weitere Einstellungen können unverändert bleiben. Speichern Sie die Einstellungen mit der Schaltfläche ganz unten auf der Webseite.

Register: Mein Konto, Menü: Mein Profil > Mehr…, Kategorie im Menü auf der linken Seite: Verkäufer/Händler – Website-Einstellungen: Aktualisieren“ /></p>
<p><img decoding=

Einrichtung des Designs der Zahlungsseite

Nach dem der Kunde im Checkout auf „Bestellung abschließen“ klickt, wird er auf die externe Zahlungsseite von PayPal weitergeleitet. In die Gestaltung der Zahlungsseite erlaubt PayPal dem Händler sein Logo und Farben seines Corporate Designs einfließen zu lassen, im Wesentlichen bleibt die Seite aber im PayPal-Design.

Die Anzeige des Shop-Logos auf der Zahlungsseite signalisiert dem Kunden die Zugehörigkeit der Seite zum Shop und ist deshalb wichtig.

Die auf der PayPal-Zahlungsseite angezeigten Logo und Farbe lassen sich direkt in Magento einstellen. Davon rate ich ab und empfehle stattdessen, in PayPal eine Vorlage einzurichten und nur den Namen dieser Vorlage in den Magento-Einstellungen zu verwenden. Dies hat zum einen den Vorteil, dass beim Aufruf der Zahlungsseite weniger Daten übertragen werden, zum anderen bietet PayPal eine bequeme Vorschaufunktion, mit der die Zahlungsseite ohne Durchführung eines Testeinkaufs angezeigt werden kann.

Bereiten Sie zwei Grafiken mit Ihrem Logo vor, die Sie in das Verzeichnis media Ihrer Magento-Installation hochladen. Idealerweise sollten die Logos jeweils auf dem weißen Hintergrund platziert sein und folgendermaßen formatiert sein:

  • 190 x 60 Pixel, das Logo ist vertikal und horizontal zentriert
  • 750 x 90 Pixel, das Logo ist linksbündig und vertikal zentriert

Um die Gestaltung der Zahlungsseite einzurichten, gehen Sie wie folgt vor:

  1. Register: Mein Konto > Menü: Mein Profil > Mehr…
  2. Kategorie im Menü auf der linken Seite: Verkäufer/Händler
  3. Benutzerdefinierte Zahlungsseiten: Aktualisieren
  4. Schaltfläche: Hinzufügen
  5. Geben Sie den Namen der Seitenvorlage ein, den Sie bei der Konfiguration von PayPal in Magento im Block Individuelle Gestaltung eingegeben haben, z. B. MeinDesign
  6. Tragen Sie die URL der 190 x 60 Pixel großen Grafik ein, z. B. https://www.meinshop.de/media/logo-pp-190.png
  7. Tragen Sie die URL der 750 x 90 Pixel großen Grafik ein, z. B. https://www.meinshop.de/media/logo-pp-750.png
  8. Lassen Sie sich die beiden Designs der Zahlungsseiten mit der Schaltfläche Vorschau anzeigen. Speichern Sie Ihre Einstellungen anschließend.

Register: Mein Konto, Menü: Mein Profil, Mehr..., Kategorie im Menü auf der linken Seite: Verkäufer/Händler, Benutzerdefinierte Zahlungsseiten: Aktualisieren

Gewähren Sie Ihren Kunden ein umkompliziertes Zahlungserlebnis, indem Sie die Zahlungsseiten von PayPal an das Aussehen und die Funktionalität Ihrer Website anpassen. Weitere Informationen - Die Standardseitenvorlage wird auf alle Ihre Zahlungsseiten angewendet, soweit nicht für eine bestimmte Schaltfläche oder einen bestimmten Link anders angegeben.

Die PayPal-Zahlungsseiten bekommen ein neues Design und werden übersichtlicher - so wird das Bezahlen für Ihre Kunden noch einfacher. - Was bedeutet das für Sie? Sie können natürlich auch die neuen Zahlungsseiten an Ihre Website anpassen. Sie wählen nachfolgend Ihre gewünschte Hintergrundfarbe aus - um den Rest kümmern wir uns. - Übrigens: Das neue Design wird phasenweise angeschaltet. Das bedeutet, manche Ihrer Kunden sehen das alte Design, bis wir die neue Version vollständig freigeschaltet haben.? - Passen Sie die Zahlungsseiten von PayPal an den Stil Ihrer Website an. Weitere Informationen. - Name der Seitenvorlage – Wählen Sie einen Namen mit maximal 30 Zeichen aus, der keine Leerzeichen enthält. - Name der Seitenvorlage: - Logo-/Bild-URL - Wählen Sie ein Bild, das maximal 190 x 60 Pixel groß ist. Das Bild wird oben links angezeigt. Wir empfehlen die Verwendung eines Bildes, das auf einem sicheren (https-)Servergespeichert ist. - Logo-/Bild-URL: - Farbverlauf im Warenkorbbereich - Wählen Sie einen Farbverlauf für den Warenkorbbereich mit HTML-Hex-Code. - Farbverlauf im Warenkorbbereich: - URL für Bild in der Kopfzeile – Geben Sie ein Bild an, das maximal 750 x 90 Pixel groß ist. Größere Bilder werden auf diese Größe zugeschnitten. Das ausgewählte Bild wird oben links auf der Zahlungsseite angezeigt. Wir empfehlen Ihnen, nur dann ein Bild anzugeben, wenn es auf einem sicheren (https-)Server gespeichert ist. - URL für Bild in der Kopfzeile: - Hintergrundfarbe Kopfzeile - Wählen Sie die Hintergrundfarbe für die Kopfzeile mit HTML-Hex-Code. - Hintergrundfarbe Kopfzeile: - Rahmenfarbe Kopfzeile - Wählen Sie die Rahmenfarbe Weitere Informationen

PayPal verwendet zwei Designs für die Zahlungsseite mit den Bezeichnungen „Verbesserte Kaufabwicklung“ (neues Design) und „Bisherige Kaufabwicklung“ (altes Design). Verwenden Sie die Schaltfläche Vorschau, um sich die beiden Designs anzeigen zu lassen.

Sie haben keine Kontrolle darüber, welches Design Ihrem Kunden angezeigt wird. Laut PayPal wird in Zukunft nur das neue Design verwendet. In der Praxis bekommt der eine oder andere Kunde heute immer noch das alte Design zu sehen.

Mit der Bestimmung der Hintergrundfarbe gibt PayPal noch etwas mehr Gestaltungsmöglichkeiten. Ihr Logo auf dem weißen Hintergrund reicht für die Personalisierung der Zahlungsseite aber meistens aus.

Wenn nach dieser Anleitung doch etwas nicht funktioniert, können Sie unseren Magento-Support anfragen.

Magento-Themes einfacher erstellen und modifizieren

In diesem Artikel beschreibe ich ein zeitsparendes Vorgehen zum Arbeiten mit Magento-Themes.

Einführung

Ich gehe ich davon aus, dass Sie mit den Prinzipien der Erstellung eines Magento-Themes vertraut sind, andernfalls wäre Designer’s Guide to Magento ein guter Einstieg.

Wird Magento in den Verzeichnissen des verwendeten Themes nicht fündig, wird die notwendige Datei aus dem Verzeichnis des Base-Themes geladen. Das Base-Theme von Magento ist in app\design\frontend\base\default gespeichert.

Das Verzeichnis des Base-Theme ist unantastbar; Anpassungen am Template sollten auf keinen Fall an Dateien in diesem Verzeichnis durchgeführt werden. Ist eine Anpassung des Templates gewünscht, wird die Datei aus dem eigenen Theme-Verzeichnis bearbeitet. Verwendet Magento eine Datei aus dem Base-Theme, ist sie in das eigene Theme-Verzeichnis inklusive der Ordnerstruktur zu kopieren und erst dann anzupassen.

Um beispielsweise einen Conversion-Pixel auf der Seite „Vielen Dank für Ihren Einkauf“ einzubauen, wird die Datei success.phtml in das eigene Theme-Verzeichnis kopiert und erst dort mit einem statischen Block erweitert, der den Code für den Conversion-Pixel aufnehmen soll:

cp -r app\design\frontend\base\default\template\checkout\success.phtml app\design\frontend\default\my_theme\template\checkout\success.phtml

Bei vielen Template-Anpassungen oder beim Aufbau eines neuen Templates wird diese Prozedur sehr zeitraubend.

Zeitsparendes Arbeiten mit Magento-Themes

Als Abhilfe kann das gesamte Verzeichnis des Base-Theme in das eigene Theme-Verzeichnis kopiert werden (1). Anschließend werden alle Dateien durch rekursive Umbenennung „deaktiviert“ (2). Das lästige Kopieren einzelner Dateien des Base-Theme erübrigt sich; stattdessen wird ausschließlich im eigenen Theme-Verzeichnis gearbeitet. Ist eine Anpassung gewünscht, wird die betroffene Datei im eigenen Theme-Verzeichnis durch Umbenennen „aktiviert“ und danach bearbeitet (4). Vor dem Umbenennen kann eine Kopie der Datei angelegt werden (5), sodass die originale Datei aus dem Base-Theme-Verzeichnis stets zur Hand bleibt.

1. Das Base-Theme wird in das eigene Theme-Verzeichnis my_theme kopiert:

Linux:

shell> cp -r app/design/frontend/base/default app/design/frontend/default/my_theme

Windows:

shell> xcopy /S /E app\design\frontend\base\default app\design\frontend\default\my_theme\

2. Alle Theme-Dateien werden durch rekursive Umbenennung deaktiviert. Hierzu wird jedem Dateinamen  „-disabled“ angehängt:

Linux:

Das aktuelle Verzeichnis ist das Startverzeichnis für die rekursive Umbenennung. Wechseln Sie deshalb vor dem Aufruf erst in das eigene Theme-Verzeichnis. Der folgende Befehl fügt rekursiv jedem Dateinamen „-disabled“ an, sodass „beispiel.phtml“ in „beispiel.phtml-disabled“ umbenannt wird.

Mit dem Parameter -n kann der Befehl zu Testzwecken im Leerlauf ausgeführt werden, Abbruch ist mit STRG+C möglich:

shell> cd app\design\frontend\default\my_theme\
shell> find -type f -exec rename -n 's/$/-disabled/' '{}' ';'

Entfernen Sie den Parameter -n, nach dem die Simulation erfolgreich war:

shell> find -type f -exec rename 's/$/-disabled/' '{}' ';'

Windows:

Für rekursive Umbenennung unter Windows wird der folgende Code in der Stapelverarbeitungsdatei renr.bat im eigenen Theme-Verzeichnis gespeichert:

for /r %%x in (%1) do ren "%%x" %2

Die Datei wird anschließend auf der Kommandozeile ausgeführt. Das aktuelle Verzeichnis ist das Startverzeichnis für die rekursive Umbenennung. Wechseln Sie deshalb vor dem Aufruf erst in das eigene Theme-Verzeichnis.

shell> cd app\design\frontend\default\my_theme\
shell> renr *.phtml *.phtml-disabled
shell> renr *.xml *.xml-disabled

Sie können alle Dateien auch in einer Zeile umbenennen, wobei allerdings auch die renr.bat in renr.bat-disabled umbenannt wird:

shell> renr * *-disabled

Wenn die Datei renr.bat erwartungsgemäß funktioniert, können Sie die Ausgabe der Stapelverarbeitung mit @ECHO OFF ausblenden:

@ECHO OFF
for /r %%x in (%1) do ren "%%x" %2 

3. a) Wenn Sie für das neue Theme als Grundlage das Base-Theme verwenden wollen, wären Sie mit dem Vorbereiten des eigenen Theme-Verzeichnisses fertig. Nun muss noch das Base-Skin-Verzeichnis kopiert werden:

shell> cp -r skin/frontend/base/default/ skin/frontend/default/my_theme/

Unter Windows verwenden Sie den Befehl xcopy (s. Punkt 1).

3. b) Wenn Sie ein anderes Theme als Grundlage für die Gestaltung des neuen Themes verwenden (z. B. modern), kopieren Sie die Dateien dieses Themes in das vorbereitete Theme-Verzeichnis my_theme:

shell> cp -r app/design/frontend/default/modern/ app/design/frontend/default/my_theme/

Verwenden Sie in diesem Fall auch das skin-Verzeichnis des Grundlage-Themes an Stelle des Base-Skin-Verzeichnisses:

shell> cp -r skin/frontend/default/modern/ skin/frontend/default/my_theme/

4. Die anzupassende .phtml-disabled wird in einem Editor geöffnet und unter .phtml gespeichert. Anschließend kann die Datei editiert werden.

5. Um Verwirrung auszuschließen, wird die .phtml-disabled sinngemäß in .phtml-original umbenannt.

So sieht beispielsweise das Verzeichnis app/design/frontend/default/my_theme/template/checkout nach Anpassung der Datei success.phtml aus:

Magento-Theme Anpassung

6. Auch wenn die „deaktivierten“ Dateien im eigenen Theme-Verzeichnis nicht stören, können diese, nach dem die Arbeiten am neuen Theme abgeschlossen wurden, rekursiv entfernt werden.

Tipp: Magento-Training „Magento-Themes erstellen und anpassen“