Magento Blog für Entwickler

Magento ist das führende Open-Source Shop-System. In unserem Blog diskutieren wir aktuelle Herausforderungen im E-Commerce, geben Tipps und zeigen Probleme auf. Hiermit möchten wir unseren kleinen Teil zum Magento Ökosystem beitragen. Viel Spaß beim Lesen :-)

Türchen 20: Hilfreiches rund um Warenkorbpreisregeln

Dez 20, 2014   //   von   //   Adventskalender, Magento  //  Keine Kommentare

Mit den Warenkorb-Preisregeln steht einem in Magento out-of-the-box ein mächtiges Tool zur Rabattierung auf Warenkorb-Ebene zur Verfügung. Jedoch haben Kunden immer wieder Anforderungen an z.B. zusätzlichen Bedingungen oder Automatisierungen. Auch gibt es z.B. bei der Implementierung der Validierung auf Kategorie-Ebene
Stolperfallen, die einem die tägliche Arbeit erschwären kann. Dieser Beitrag zeigt an praktischen Beispielen wie man GiftCards generell aus der Rabatt-Berechnung ausschließt, ein neues Warenkrorb-Attribute für Bedingungen zur Verfügung stellt und validiert und ein mögliches Problem bei der Kategorie-Validierung auf Artikelebene bei konfigurierbaren Produkten umschifft.

1. GiftCards aus der Rabattberechnung generell ausschließen

GiftCards sollten grundsätzlich aus der Berechnung von Zeilenrabatten ausgeschlossen werden. Diese können wir zum einen für jede Regel manuell über z.B. die SKU steuern oder aber GiftCards global per Observer von der Einbeziehung auschließen. In einem lokalen Modul Namens Lokal_SalesRule definieren wir in unserer Observer-Klasse die Methode excludeGiftCard welche auf den Observer “sales_quote_collect_totals_before” lauscht. Hier durchlaufen wir alle Quote-Items und setzen für alle GiftCards-Items die Eigenschaft “no_discount”.

<?php
/**
 * Do not process giftcard in sales rules
 *
 * Listens to:
 * - sales_quote_collect_totals_before
 *
 * @param Varien_Event_Observer $observer
 * @return void
 */
public function excludeGiftCard(Varien_Event_Observer $observer)
{
    $quote = $observer->getEvent()->getQuote();
    /* @var $quote Mage_Sales_Model_Quote */

    foreach ($quote->getAllItems() AS $item) {
        if ($item->getParentItemId()) {
            continue;
        }
        /* @var $item Mage_Sales_Model_Quote_Item */
        if ($item->getProductType() == Enterprise_GiftCard_Model_Catalog_Product_Type_Giftcard::TYPE_GIFTCARD) {
            $item->setNoDiscount(1);
        }
    }
}

Die collect-Methode des Quote-Discount-Models prozessiert nur Items durch den SalesRule-Validator wenn für dieses die “no_discount”-Eigenschaft false ist. Somit werden global alle GiftCards aus der Regelausführung/-prüfung ausgeschlossen. Es können über diese Methode programmatisch weitere beliebige Items anhand von Eigenschaften ausgeschlossen werden.

2. Eigenes Warenkorb-Attribut für Bedingung: z.B. Eigenes Subtotal ohne GiftCards

Manch einer möchte beispielsweise eine Regel nur angewendet bekommen wenn die Zwischensumme des Warenkorbs ohne eingerechnete GiftCards einen gewissen Wert überschreitet. Um das zu ermöglichen, müssen wir zum einen ein neues Address-Condition-Attribute definieren und zum anderen das Attribute an den Quote-Addresses zur Verfügung stellen. Beides können wir über einen Observer implementieren. Um ein neues Address- bzw. Quote-Attribute als Bedingung definieren zu können erweiteren wir unseren Observer mit nachfolgender Methode und lassen diese im Event “salesrule_rule_condition_combine” ausführen. Die neu zu validierende Eigenschaft lautet in unserem Fall “subtotal_incltax_without_giftcards”.

<?php
/**
 * Add new condition for address / quote
 *
 * Listens to:
 * - salesrule_rule_condition_combine
 *
 * @param Varien_Event_Observer $observer
 * @return void
 */
public function addAddressCondition(Varien_Event_Observer $observer)
{
    $additional = $observer->getAdditional();
    $conditions = $additional->getConditions();

    if (!is_array($conditions)) {
        $conditions = array();
    }
    $conditions[] = array(
        'label' => 'Additional Cart Conditions',
        'value' => array(
            array('label' => Mage::helper('lokal_salesrule')->__('Subtotal WITHOUT GiftCards'),
                'value' => 'lokal_salesrule/rule_condition_address|subtotal_incltax_without_giftcards',)
        )
    );
    $additional->setConditions($conditions);
}

Jetzt können wir im Admin-Interface eine neue Bedingung auswählen. (siehe Screenshot)

auswahl

Zusätzlich benötigen wir jetzt noch das entsprechende Rule Condition Model. Die neu definierte Eigenschaft (“subtotal_incltax_without_giftcards”) wird dort für virtuelle Warenkörbe auf der Rechungsadresse und bei Standard-Warenkörben auf der Versandadresse als Bedingung validiert.

<?php
class Lokal_SalesRule_Model_Rule_Condition_Address extends Mage_Rule_Model_Condition_Abstract
{
    public function loadAttributeOptions()
    {
        $attributes = array(
            'subtotal_incltax_without_giftcards' => Mage::helper('lokal_salesrule')->__('Subtotal WITHOUT GiftCards'),
        );

        $this->setAttributeOption($attributes);

        return $this;
    }

    public function getAttributeElement()
    {
        $element = parent::getAttributeElement();
        $element->setShowAsText(true);
        return $element;
    }

    public function getInputType()
    {
        switch ($this->getAttribute()) {
            case 'subtotal_incltax_without_giftcards':
            return 'numeric';

        }
        return 'string';
    }

    public function getValueElementType()
    {
        return 'text';
    }

    public function getValueSelectOptions()
    {
        if (!$this->hasData('value_select_options')) {
            switch ($this->getAttribute()) {
                default:
                    $options = array();
            }
            $this->setData('value_select_options', $options);
        }
        return $this->getData('value_select_options');
    }

    /**
     * Validate Address Rule Condition
     *
     * @param Varien_Object $object
     * @return bool
     */
    public function validate(Varien_Object $object)
    {
        $address = $object;
        if (!$address instanceof Mage_Sales_Model_Quote_Address) {
            if ($object->getQuote()->isVirtual()) {
                $address = $object->getQuote()->getBillingAddress();
            }
            else {
                $address = $object->getQuote()->getShippingAddress();
            }
        }
        return parent::validate($address);
    }
}

Der “subtotal_incltax_without_giftcards” Wert muss jetzt noch per Observer für die beiden Adress-Typen zur Verfügung gestellt werden. Hierfür hört die nachfolgende Methode auf den Event “sales_quote_collect_totals_before” wo der Wert berechnet und für die einzelnen Modelle gesetzt wird.

<?php
/**
 * Calculate subtotal without giftcard amounts
 *
 * Listens to:
 * - sales_quote_collect_totals_before
 *
 * @param Varien_Event_Observer $observer
 * @return void
 */
public function calcSubtotalWithoutGiftCards(Varien_Event_Observer $observer)
{
    $quote = $observer->getEvent()->getQuote();
    /* @var $quote Mage_Sales_Model_Quote */

    $gcRowsTotal = 0;
    /* item is marked as deleted, so we have two items left, including card */
    foreach ($quote->getAllItems() AS $item) {
        if ($item->getParentItemId()) {
            continue;
        }
        /* @var $item Mage_Sales_Model_Quote_Item */
        if($item->getProductType() == Enterprise_GiftCard_Model_Catalog_Product_Type_Giftcard::TYPE_GIFTCARD){
            $gcRowsTotal += $item->getRowTotalInclTax();
        }
    }
    $quote->getShippingAddress()->setData('subtotal_incltax_without_giftcards',  $quote->getShippingAddress()->getSubtotalInclTax() - $gcRowsTotal);
    $quote->getBillingAddress()->setData('subtotal_incltax_without_giftcards',  $quote->getBillingAddress()->getSubtotalInclTax() - $gcRowsTotal);
}

Jetzt können wir unsere neue Warenkorb-Bedingung einsetzen. Es kann die Bedingung konfiguriert werden, das zu validierende Objekt hat die entsprechend benötigte Eigenschaft erhalten und die Validierung ist implementiert.

3. Regelbedingung für Kategorien auf Artikel-Ebene bei Configurable-Products

Je nach Art der Kategorisierung von konfigurierbaren Produkten kann es bei der Validierung von Bedingungen auf Kategorien zur Problemen mit der Regelvalidierung kommen. Mage_SalesRule_Model_Rule_Condition_Product::validate (siehe nachfolgenden Code) validiert das konfigurierbare Produkt eines Quote-Items. Ist diese unwahr wird die selbe Bedingung auf das Produkt des ersten Child-Items (ein Simple) validiert. Da normalerweise Simples nicht katgorisiert werden müssen (Stichwort: Sichtbarkeit nirgendwo) und in Version >= EE 1.14.0.0 die zu validierenden Kategorien aus Mage_Catalog_Model_Product::getCategoryIds() gelesen werden, wird die Bedingung dann ggf. bei einem unkategorisierten Simple wahr. Somit würde die Regel ungewollt angewendet werden.

Mage_SalesRule_Model_Rule_Condition_Product::validate

<?php
/**
 * Validate Product Rule Condition
 *
 * @param Varien_Object $object
 *
 * @return bool
 */
public function validate(Varien_Object $object)
{
    /** @var Mage_Catalog_Model_Product $product */
    $product = $object->getProduct();
    if (!($product instanceof Mage_Catalog_Model_Product)) {
        $product = Mage::getModel('catalog/product')->load($object->getProductId());
    }

    $product
        ->setQuoteItemQty($object->getQty())
        ->setQuoteItemPrice($object->getPrice()) // possible bug: need to use $object->getBasePrice()
        ->setQuoteItemRowTotal($object->getBaseRowTotal());

    $valid = parent::validate($product);
    if (!$valid && $product->getTypeId() == Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
        $children = $object->getChildren();
        $valid = $children && $this->validate($children[0]);
    }

    return $valid;
}

An einem Beispiel konkretisiert:
Eine Warenkorb-Regel soll nicht für Produkte aus einer bestimmten Kategorie X angewendet werden. Wir definieren unter “Regel nur auf Artikel im Warenkorb mit den…” “Kategorie enthält nicht X”. Somit sind Produkte aus der Kategorie X ausgeschlossen. In unserem Shop sind nur die konfigurierbaren Produkte kategorisiert. Wird nun ein Produkt/Item validiert, welches in die Kategorie X verknüpft ist, ist für dieses Item die Validierung fehlgeschlagen, also unwahr. Jetzt wird das Produkt des Child-Items validiert auf die Kategorie X validiert. Da dieses jedoch nicht kategorisiert ist wird die Validierung wahr, es ist ja nicht in Kategorie X.

Hinweis:
Version bis EE 1.14.0.0 validieren auf die Kategorie-Id’s aus dem Category-Product-Index (Mage_Catalog_Model_Product::getAvailableInCategories())
Version ab EE 1.14.0.0 validieren auf Mage_Catalog_Model_Product::getCategoryIds() (ohne Index, macht die Regeldefinition komplizierter)

Man könnte dieses Problematik jetzt zum einen damit umgehen, dass wir zusätzlich alle Simple-Products kategorisieren, was wir jedoch nicht wollen. Wir behelfen uns lieber wieder mit einem Observer über welchen wir das Produkt des Child-Items vor der Abarbeitung der Totals (vorallem Discount) durch eine Vererbung der Parent-Item-Kategorisierung anreichern. Das nachfolgende Beispiel funktioniert z.B. für Versionen >= EE 1.14.0.0.

Wir lauschen auch hier wieder auf “sales_quote_collect_totals_before” und reichern das Child-Item-Product mit den Kategorien aus seinem Parent-Item-Product an.

<?php
/**
 * Inherit categories
 *
 * Listens to:
 * - sales_quote_collect_totals_before
 *
 * @param Varien_Event_Observer $observer
 * @return void
 */
public function inheritCategories(Varien_Event_Observer $observer)
{
    $quote = $observer->getEvent()->getQuote();
    /* @var $quote Mage_Sales_Model_Quote */

    /* item is marked as deleted, so we have two items left, including card */
    foreach ($quote->getAllItems() AS $item) {
        /** add parent product categories to item */
        if($item->getParentItem()){
            $categoryIds = Mage::helper('catalog/product')->getProduct($item->getParentItem()->getProductId(), null)->getCategoryIds();
            $item->setProduct($item->getProduct()->setCategoryIds($categoryIds));
        }
    }
}

Türchen 19: Kickstart your Magento Dev System with Vagrant

Dez 19, 2014   //   von   //   Adventskalender, Magento  //  Keine Kommentare

Vagrant is a tool that allows portable virtual development environments, using virtual machines. If multiple team members work on different devices on a project, a unified environment can be ensured. It only has to be set up once and can be reproduced as often as needed.

But it is also interesting to single developers who work on multiple projects. Often the different production systems have different PHP versions installed or they need specific extensions and system configurations. Especially with PHP where much behaviour depends on global configuration, you often get errors due to different systems.

With Vagrant you can define the whole environment for each project and also put this definition under version control. Ideally this resembles the production system as close as possible. Also, your own system is less cluttered up with software that you needed for some past project.

Although Vagrant manages virtual machines, it is not a VM provider itself, it works with different providers, such as VirtualBox, VMware and even AWS.

Vagrant also works with many different provisioning tools, i.e. tools for automated system setup, like Puppet, but also comes with very simple built-in provisioners to copy files and run shell scripts. You can combine all of those.

Weiterlesen >>

Türchen 18: Magento Frontend mit Bootstrap SCSS

Dez 18, 2014   //   von   //   Adventskalender, Magento  //  1 Kommentar

In diesem Artikel schauen wir gemeinsam, wie ein Magento Theme auf Bootstrap aufgebaut werden kann und warum es gar nicht so kompliziert ist wie vermutet. Helfen werden uns dabei SCSS, zum Erstellen der Stylesheets und Gulp zum Kompilieren der SCSS und Bootstrap Dateien. Ziel ist es herauszufinden wie Bootstrap mit möglich wenig Klassen-Überschreibungen einfach und effektiv eingesetzt werden kann.

Weiterlesen >>

Türchen 17: Lasttests mit „Siege“

Dez 17, 2014   //   von   //   Adventskalender, Magento  //  1 Kommentar

Performance oder Last-Tests sind nicht unwichtig, wenn man einen Shop fertig stellt und nicht weiß, wie er mit dem möglicherweise zu erwartenden Besucheraufkommen zurecht kommt. Siege ist ein Kommandozeilen-Werkzeug zur Durchführung von Lasttests mit einer frei wählbaren Anzahl an URLs und Benutzern…

Weiterlesen >>

Türchen 16: Extending the magento RESTfull Api (Fast Simple Import)

Dez 16, 2014   //   von   //   Adventskalender, Magento  //  Keine Kommentare

The Magento REST-Api was introduced with CE 1.5 and many of us hoped that some of the flaws of the SOAP-Api would be fixed or could be circumvented. Both Apis have their pros and cons and one of the biggest con for me with the SOAP style was its speed.
Some time ago we decided to rewrite our complete import/export interface to become faster and reduce the pure amount of data that is updated on the shop side. Before the rewrite we used a pure model-based import/export and as you all know: this is super slow so i decided to try something else.

Weiterlesen >>

Türchen 15: MySQL 5.6 für Magento Entwickler

Dez 15, 2014   //   von   //   Adventskalender, Magento  //  1 Kommentar

Seit der im November 2014 veröffentlichten Magento Community Edition 1.9.1.0 bzw. Enterprise Edition 1.14.1.0 bietet Magento die Unterstützung von MySQL 5.6 an.

In den Release Notes der beiden Magento Versionen wird der Support folgendermaßen angekündigt: „Magento Community (Enterprise) Edition erhöht die Performance und Sicherheit durch den Support für MySQL 5.6 und PHP 5.5. Mit MySQL 5.6 profitiert man von einer verbesserten Seitengeschwindigkeit und Skalierbarkeit, weniger Speicherverbrauch des Datenbankservers und erweiterten Debugging-Tools.“ In der täglichen Arbeit sind Magento-Entwickler meist sehr auf ihren Programmcode bzw. dessen Handling fokussiert. Ein sehr spannender, wichtiger und sehr grundlegender Bestandteil eines Magento-Shops kommt dabei manchmal zu kurz: Die Datenbank. Welche coolen Features von MySQL, und im Speziellen von MySQL 5.6, für die Magento-Entwicklung nützlich und interessant sind, gibt’s in diesem Blogpost zu entdecken.

Weiterlesen >>

Türchen 14: A possible solution for Dropshipping in Magento

Dez 14, 2014   //   von   //   Adventskalender, Magento  //  2 Kommentare

Many Magento online shops face the challenge of sending thousands of physical goods to clients worldwide. Especially the number of suppliers and packages and the wide range of shipping partners increase the complexity of the delivery process. Merchants have several possibilities to solve this challenge: build up a new warehouse and connect external fulfillment service providers, which of course can be very cost intense and complex. As an alternative the outsourcing of logistics became more and more interesting for smaller companies.

Weiterlesen >>

Türchen 13: Magento ImportExport: Xmlimport

Dez 13, 2014   //   von   //   Adventskalender, Magento  //  2 Kommentare

The Magento standard import, ImportExport, was a big improvement in terms of speed and reliability of product import. Unfortunately, it only accepts a complicated CSV-format. This import format is hard to understand and looks like somebody tried to transform XML into CSV. So we created a module that transforms it back to XML and called this module XmlImport. It was built on top of FastSimpleImport and has been tested intensively for simple products in multiple stores. Other cases have also been tested and seem to work. The module is available on GitHub: https://github.com/code4business/xmlimport and as a ZIP-File in this blog page: http://www.code4business.de/xmlimport

Weiterlesen >>

Seiten:1234567...39»