Tutorial: Magento Extension erstellen

1 | 13249 Aufrufe
Sie können diese Wikiseite nach der Anmeldung auf Webmasterpro bearbeiten. Helfen Sie mit und verbessern Sie "Tutorial: Magento Extension erstellen" mit Ihrem Wissen!

Anzeige Hier werben

Für die E-Commerce Software Magento existiert eine Vielzahl von kostenlosen Extensions (so genannte Community Extensions). Wie man selbst in den Genuss kommt, sein eigenes Modul zu entwickeln, wird mit diesem Tutorial näher erklärt.

Wir versuchen uns daran, ein eigenes Modul für den Versand zu entwickeln (Shipping-Module). Das Ziel dabei ist, dieses für einen Multi-Store tauglich zu machen und eine einfache Konfiguration für das Magento-Backend zu ermöglichen. Wir werden hierbei ein abgewandeltes Table-Rate-Modul erstellen. Dabei müssen wir ins viele interessante Themen einarbeiten, z.B.:

  • in die Magento Modul Struktur
  • MVC-Pattern
  • Magento Backend Konfiguration

Vor dem Start

Ich gehe davon aus, das ihr eine bestehende Magento-Installation, entweder auf einem Entwicklungsserver oder lokal eingerichtet und funktional habt. Dabei ist es egal ob dies eine Magento Community Edition oder Magento Enterprise ist.

Cache deaktivieren

Die erste und wichtigste Aktion die ein Magento Entwickler lernt ist den Cache zu deaktivieren. Das geht unter Admin Panel > System > Cache-Verwaltung > Alle wählen > Aktionen: Deaktivieren > Absenden.

Magento Cache-Verwaltung
Der Magento Cache sollte in der Entwicklung immer deaktiviert sein!

Lässt ihr den Cache während der Entwicklung eingeschaltet, bekommt ihr beim Testen Kopfschmerzen. Ihr werdet eure neue Extension sonst nicht auffinden im System.

Die Magento Ordner Struktur

Der logische Part eines jeden Moduls liegt in einem der drei Unterordner von app/code, diese sind community, core und local. Um Magento mitzuteilen, das wir eine neue Extension installiert haben müssen wir zudem unter app/etc/modules eine XML-Datei anlegen.

Core

In app/code/core liegen, wie der Name schon vermuten lässt, alle Module die von Magento mitgebracht werden. Das beinhaltet unter anderem alle Module für Produkte, Kategorien, Zahlarten, Bestellungen, usw. Diese Module sollten niemals direkt verändert werden. Nach einem Update sind nämlich alle eingebrachten Änderungen weg.

Community

Innerhalb von app/code/community liegen Module von Drittanbietern die nicht von Magento Team selbst gepflegt werden. Diese Extension können frei via Magento Connect bezogen werden. Wenn ihr Module über den Magento Connect Manager installiert, landen diese alle in diesem Verzeichnis (z.B. das deutsche Sprachpaket).

Local

Dieser Ordner (app/code/local) ist nach einer frischen Magento Installation leer. In diesem Ordner sollen Module landen, die selbst entwickelt sind. Das wird unser Spielplatz sein.

Modul Struktur anlegen

Öffnet euren Editor und navigiert zu app/code/local, wir müssen dort einige neue Ordner und Dateien anlegen. Dabei müsst ihr ein bestimmtes Namensschema beachten, dieses besteht aus:

  • Modul Namespace und
  • Modul Name

Modul Namespace

Der erste Ordner den wir erstellen ist unser eigener Namensraum für alle Module die wir erstellen wollen. Der Ordner kann genannt werden wir ihr wollt, die Konvention geht dahin das der Name eure Agentur oder euren eigenen Namen verwendet. Im Tutorial verwenden wir Webmasterpro als Namensraum. Erstellt den Ordner app/code/local/Webmasterpro.

Modul Name

Für das nächste Verzeichnis geben wir unserem Modul einen Aussagekräftigen Namen. Unser Modul soll eine Versandart für Multi-Store Systeme werden. Wir nennen es deshalb Multishipping (app/code/local/WebmasterPro/Multishipping).

Wir sollten nun die folgende Modul-Struktur haben. Die Namen berücksichtigen Groß-/Kleinschreibung.

Modul-Struktur  
Text
1
2
3
4
5
app/
 - code/
  - local/
   - Webmasterpro/
    - Multishipping/

Konfiguration unseres Moduls

Die Konfiguration jeder Magento Extension befindet sich innerhalb einer XML-Datei im Ordner etc. Die Datei sagt Magento wo sich unsere Dateien befinden, auf welche Events wir hören und viele weitere Einstellungen. Wir erstellen also die Datei app/code/local/Webmasterpro/Multishipping/etc/config.xml.

config.xml  
HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0"?>

<!-- Die erste Ebene für die Magento Modul Konfiguration -->
<config>
    <!-- Die modules-Node beinhaltet Basis Information zum Modul -->
    <modules>
        <!-- Die Ebene muss exakt den Namensraum sowie den Modulnamen beinhalten, separiert mit einem Unterstrich -->
        <Webmasterpro_Multishipping>
            <version>1.0.0</version>
        </Webmasterpro_Multishipping>
    </modules>
</config>

Modul aktivieren

Als nächstes aktivieren wir unser Modul im Magento System. Dazu müssen wir unter app/etc/modules eine XML-Datei erstellen, die wieder rum genau unsere Namensstruktur trifft. Mit dieser Datei sagen wir Magento wo unser Modul beheimatet ist und ob es andere Module als Abhängigkeiten verwendet, das wieder rum trifft bei uns nicht zu. Wir erstellen also folgende Datei app/etc/modules/Webmasterpro_Multishipping.xml.

Webmasterpro_Multishipping.xml  
XML
1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<config>
    <modules>
        <Webmasterpro_Multishipping>
            <active>true</active>
            <codePool>local</codePool>
        </Webmasterpro_Multishipping>
    </modules>
</config>

Nun haben wir unser Modul aktiviert, das bisher noch nichts macht. Um zu überprüfen ob Magento dies korrekt erkannt hat, schauen wir unter System > Konfiguration > Erweitert > Erweitert ob unser Modul aufgelistet wurde. Wenn nicht denkt bitte nochmal an den Cache!

Magento Modul Liste
Anzeige ob Magento unser Modul korrekt erkannt hat.

Entwicklung des Moduls

Zuerst definieren wir in unserer config.xml unser Model und geben gleich für den Admin-Bereich einige Standard Werte an.

Vollständige Konfiguration

config.xml  
XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0"?>

<config>
    <modules>
        <Webmasterpro_Multishipping>
            <version>1.0.0</version>
        </Webmasterpro_Multishipping>
    </modules>

    <!-- Konfiguration unseres Modul-Verhaltens -->
    <global>
        <models>
            <!-- Wir geben unserem Modul hier einen eigenen Namensraum -->
            <multishipping>
                <class>Webmasterpro_Multishipping_Model</class>
            </multishipping>
        </models>

        <!-- Brauchen wir um später formatierte Ausgaben zu erzeugen -->
        <blocks>
            <multishipping>
                <class>Webmasterpro_Multishipping_Block</class>
            </multishipping>
        </blocks>

        <!-- für Hilfsfunktionen -->
        <helpers>
            <multishipping>
                <class>Webmasterpro_Multishipping_Helper</class>
            </multishipping>
        </helpers>
    </global>
    
    <default>
        <carriers>
            <multistore_shipping> <!-- Neue Versandart hinzufügen -->
                <active>0</active> <!-- aktiv 0 - nein / 1 - ja -->
                <sallowspecific>0</sallowspecific> <!-- erlaubte Länder auswählbar? -->
                <model>multishipping/carrier_multishipping</model> <!-- dort lebt die Logik unseres Moduls -->
                <name>TableRate Shipping Multistore</name> <!-- Name, wird so im Adminbereich angezeigt -->
                <title>TableRate Shipping Multistore</title> <!-- Titel, zur Ausgabe im Frontend -->
                <specificerrmsg>This shipping method is currently unavailable. If you would like to ship using this shipping method, please contact us.</specificerrmsg> <!-- Fehlernachricht, falls Versandmethode nicht aktiviert ist -->
                <handling_type>F</handling_type> <!-- Für Steuerzonen Behandlung -->
            </multistore_shipping>
        </carriers>
    </default>
</config>

Im folgenden werden die einzelnen Bereiche genauer erklärt.

global

Im Namensraum global definieren wir unser Standardverhalten unseres Moduls. Wo liegen Hilfsklassen, Blöcke, unser Model, eventuell Controller und Observer.

models

Hier sagen wir Magento wo unser Model zu finden ist (Magento setzt auf ein MVC-Pattern). Zudem geben wir unserem Modul hier einen eigenen Namensraum, multishipping. Diesen können wir nun auf alle weiteren XML-Dateien ansetzen.

blocks

In diesem Bereich kommen alle Klassen die zur Anzeige, bzw. des Renderings benötigt werden unter. Wir brauchen das später um im Magento-Backend bestimmte Teilbereiche zu rendern.

helpers

Hier geben wir Magento an, wo Hilfsfunktionen liegen. Auch wenn wir selbst keine benötigen, braucht Magento diese Angabe.

default

In dieser Konfigurationsebene werden Standardangaben zu unserem Modul gemacht. Wir nutzen dabei aus das es bereits Versandmethoden gibt (carriers) und definieren hier unser eigenes, mit Standardwerten vorausgefüllt. Weitere Angaben die im Backend ausgefüllt werden können, werden aber nicht in dieser Konfigurationsdatei vorgenommen, sondern in einer separaten (system.xml).

Admin-Konfiguration

In der system.xml definieren wir Felder die unser Modul im Backend anzeigen soll. Füllen diese natürlich auch wieder mit Standardwerten aus und wichtig, wir setzen für jedes einzelne Feld in welchen Scope dieses erscheinen darf (Global, Website, Store-View). Nachdem wir ein Modul entwickeln, das Multistore tauglich ist, werden wir allen Feldern den Scope Global geben. Damit kann das Feld in jedem anderem Scope verändert werden. Wir legen also die Datei app/code/local/Webmasterpro/Multishipping/etc/system.xml an.

system.xml  
XML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?xml version="1.0"?>
<config>
    <sections>
        <carriers>
            <groups>
                <multistore_shipping translate="label" module="multishipping">
                    <label>TableRate Shipping Multistore</label>
                    <frontend_type>text</frontend_type>
                    <sort_order>20</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <active translate="label">
                            <label>Aktiviert</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <sort_order>10</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </active>
                        <title translate="label">
                            <label>Title</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>20</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </title>
                        <name translate="label">
                            <label>Name</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>30</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </name>
                        <pricetable translate="label">
                            <label>Versandpreise</label>
                            <frontend_model>multishipping/admin_system_config_form_field_pricetable</frontend_model>
                            <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
                            <sort_order>40</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </pricetable>
                        <showmethod translate="label">
                            <label>Versandart zeigen, auch wenn nicht möglich</label>
                            <frontend_type>select</frontend_type>
                            <sort_order>50</sort_order>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </showmethod>
                        <specificerrmsg translate="label">
                            <label>Angezeigte Fehlermeldung</label>
                            <frontend_type>textarea</frontend_type>
                            <sort_order>60</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </specificerrmsg>
                        <sort_order translate="label">
                            <label>Reihenfolge</label>
                            <frontend_type>text</frontend_type>
                            <sort_order>70</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </sort_order>
                    </fields>
                </multistore_shipping>
            </groups>
        </carriers>
    </sections>
</config>

Hier weisen wir Magento an in den Versandeinstellungen eine neue Versandmethode mit den angegeben Feldern aufzunehmen. Zudem definieren wir wie sich die Felder verhalten.

Renderer hinzufügen ("Blocks")

Wir haben in unserer XML-Datei definiert das wir ein pricetable-Feld haben. Dazu müssen wir uns einen eigenen Renderer bauen. Der die Eingaben verarbeitet und ausgibt.

Dazu benötigen wir zwei neue Dateien. Einen für den das pricetable-Feld selbst und einen für die Auswahl des Lands.

app/code/local/Webmasterpro/Multishipping/Block/Admin/System/Config/Form/Field/Pricetable.php  
PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php


class Webmasterpro_Multishipping_Block_Admin_System_Config_Form_Field_Pricetable
    extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract
{
    
    const PRICE_TABLE_PREFIX = "carriers_multishipping_pricetable_";
    
    /** @var Mage_CatalogInventory_Block_Adminhtml_Form_Field_Customergroup */
    protected $_priceRenderer;
    
    /**
     * Retrieve group column renderer
     *
     * @return Mage_CatalogInventory_Block_Adminhtml_Form_Field_Customergroup
     */
    protected function _getPriceRenderer()
    {
        $countryCode = str_replace(
            self::PRICE_TABLE_PREFIX, '', $this->getElement()->getId()
        );
        
        if (!$this->_priceRenderer)
        {
            $this->_priceRenderer = $this->getLayout()->createBlock(
                'multishipping/admin_form_field_country', '', array('country_code' => $countryCode)
            );
        }
        
        return $this->_priceRenderer;
    }
    
    /**
     * Default class constructor
     */
    public function __construct()
    {
        $this->addColumn('country_id', array(
            'label' => Mage::helper('multishipping')->__('Land'),
            'style' => 'width: 150px'
        ));
        
        $this->addColumn('price', array(
            'label' => Mage::helper('multishipping')->__('Versandkosten'),
            'style' => 'width: 100px'
        ));
        
        $this->addColumn('free_shipping_price', array(
            'label' => Mage::helper('multishipping')->__('Versandkostenfrei ab'),
            'style' => 'width: 100px'
        ));
        
        parent::__construct();
        
        $this->setTemplate('multishipping/system/config/form/field/array.phtml');
        $this->_addButtonLabel = Mage::helper('adminhtml')->__('Land hinzufügen');
    }
    
    /**
     * Get the grid and scripts contents
     *
     * @param Varien_Data_Form_Element_Abstract $element
     * @return string
     */
    protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
    {
        $this->setElement($element);
        
        $this->addColumn('country_id', array(
            'label' => Mage::helper('multishipping')->__('Land'),
            'renderer' => $this->_getPriceRenderer()
        ));
        
        return parent::_getElementHtml($element);
    }
    
}

Wir definieren in dieser Datei, das dieses Feld ein serialisiertes Array wird, das aus drei Feldern zusammen gebaut wird:

  • Land
  • Versandkosten
  • Versandkostenfrei ab

Zusätzlich dazu gibt es den Button "Land hinzufügen". D.h. wir können für jedes eigene Land unterschiedliche Versandkosten definieren, und die Grenze für Versandkostenfreie Bestellungen für jedes Land gesondert angeben.

app/code/local/Webmasterpro/Multishipping/Block/Admin/Form/Field/Country.php  
PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php


class Webmasterpro_Multishipping_Block_Admin_Form_Field_Country
    extends Mage_Core_Block_Html_Select
{
    
    /**
     * Customer groups cache
     *
     * @var array
     */
    private $_countries;
    
    /**
     * Retrieve complete country list from magento
     */
    protected function _getCountries($countryCode = null)
    {
        if (is_null($this->_countries))
        {
            $this->_countries = array();
            
            $collection = Mage::getModel('directory/country')->getCollection();
            
            if (!is_null($countryCode))
            {
                $collection->addFieldToFilter('country_id', $countryCode);
            }
            
            foreach ($collection as $country)
            {
                $this->_countries[$country->getId()] = $country->getName();
            }
            
        }
        
        return $this->_countries;
    }
    
    /**
     * Set the name of the country
     */
    public function setInputName($value)
    {
        return $this->setName($value);
    }
    
    /**
     * Render block HTML
     *
     * @return string
     */
    public function _toHtml()
    {
        if (!$this->getOptions())
        {
            foreach ($this->_getCountries() as $countryId => $countryLabel)
                $this->addOption($countryId, $countryLabel);
        }
        
        return parent::_toHtml();
    }
    
}

Hier definieren wir das Rendering für das Land. Wir laden alle verfügbaren Länder, geben diese in ein Select-Element und zum speichern nehmen wir die Country-Id als Wert.

Admin Templates

Um die korrekt gespeicherten Werte zu laden, müssen wir noch das Admin-Template anpassen und ein wenig JavaScript einfügen.

app/design/adminhtml/default/default/template/multishipping/system/config/form/field/array.phtml  
HTML
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<?php


$_htmlId = $this->getHtmlId() ? $this->getHtmlId() : '_' . uniqid();

$_colspan = 2;

if (!$this->_addAfter)
    $_colspan -= 1;

$_colspan = $_colspan > 1 ? 'colspan="' . $_colspan . '"' : '';

?>

<div class="grid" id="grid<?php echo $_htmlId; ?>">
    <table cellpadding="0" cellspacing="0" class="border">
        <tbody>
            <tr class="headings" id="headings<?php echo $_htmlId; ?>">
                <?php foreach ($this->_columns as $columnName => $column):?>
                    <th><?php echo $column['label']; ?></th>
                <?php endforeach;?>
                <th <?php echo $_colspan; ?>></th>
            </tr>
            <tr id="addRow<?php echo $_htmlId; ?>">
                <td colspan="<?php echo count($this->_columns); ?>"></td>
                <td <?php echo $_colspan; ?>>
                    <button style="" onclick="" class="scalable add" type="button" id="addToEndBtn<?php echo $_htmlId; ?>">
                        <span><span><span><?php echo $this->_addButtonLabel; ?></span></span></span>
                    </button>
                </td>
            </tr>
        </tbody>
    </table>
    <input type="hidden" name="<?php echo $this->getElement()->getName(); ?>[__empty]" value="" />
</div>

<div id="empty<?php echo $_htmlId; ?>">
    <button style="" onclick="" class="scalable add" type="button" id="emptyAddBtn<?php echo $_htmlId; ?>">
        <span><span><span><?php echo $this->_addButtonLabel; ?></span></span></span>
    </button>
</div>

<script type="text/javascript">
//<![CDATA[
    // create row creator
    var arrayRow<?php echo $_htmlId; ?> = {
        // define row prototypeJS template
        template : new Template(
            '<tr id="#{_id}">'
                <?php foreach ($this->_columns as $columnName => $column): ?>
                    +'<td>'
                    +'<?php echo $this->_renderCellTemplate($columnName); ?>'
                    +'<\/td>'
                <?php endforeach;?>
                <?php if ($this->_addAfter): ?>
                    +'<td><button onclick="" class="scalable add" type="button" id="addAfterBtn#{_id}"><span><span><span><?php echo Mage::helper('adminhtml')->__('Add after'); ?><\/span><\/span><\/span><\/button><\/td>'
                <?php endif;?>
                +'<td><button onclick="arrayRow<?php echo $_htmlId; ?>.del(\'#{_id}\')" class="scalable delete" type="button"><span><span><span><?php echo Mage::helper('adminhtml')->__('Delete'); ?><\/span><\/span><\/span><\/button><\/td>'
            +'<\/tr>'
        ),
        rowsCount : 0,
        add : function(templateData, insertAfterId)
        {
            // generate default template data
            if ('' == templateData) {
                var d = new Date();
                var templateData = {
                    <?php foreach ($this->_columns as $columnName => $column): ?>
                        <?php echo $columnName ?> : '',
                    <?php endforeach;?>
                    _id : '_' + d.getTime() + '_' + d.getMilliseconds()
                };
            }
            
            // insert before last row
            if ('' == insertAfterId) {
                Element.insert($('addRow<?php echo $_htmlId; ?>'), {
                    before: this.template.evaluate(templateData)
                });
            }
            // insert after specified row
            else {
                Element.insert($(insertAfterId), {
                    after: this.template.evaluate(templateData)
                });
            }
            <?php if ($this->_addAfter):?>
                Event.observe('addAfterBtn' + templateData._id, 'click', this.add.bind(this, '', templateData._id));
            <?php endif;?>
            this.rowsCount += 1;
        },
        del : function(rowId)
        {
            $(rowId).remove();
            
            this.rowsCount -= 1;
            
            if (0 == this.rowsCount) {
                this.showButtonOnly();
            }
        },
        
        showButtonOnly : function()
        {
            $('grid<?php echo $_htmlId; ?>').hide();
            $('empty<?php echo $_htmlId; ?>').show();
        }
    }
    
    // bind add action to "Add" button in last row
    Event.observe('addToEndBtn<?php echo $_htmlId ?>', 'click', arrayRow<?php echo $_htmlId; ?>.add.bind(arrayRow<?php echo $_htmlId; ?>, '', ''));
    
    // add existing rows
    <?php
        $_addAfterId = "headings{$_htmlId}";
        
        foreach ($this->getArrayRows() as $_rowId => $_row) {
            echo "arrayRow{$_htmlId}.add(" . $_row->toJson() . ", '{$_addAfterId}');\n";
            $_addAfterId = $_rowId;
            
            ?>var data = <?php print "{$_row->toJson()}"; ?>;
            country = data.country_id;
            id = data._id;
            
            $$('select[name="groups[multishipping][fields][pricetable][value][' + id + '][country_id]"] option[value=' + country + ']').first().selected = true;
        <?php }
    ?>
    
    // initialize standalone button
    $('empty<?php echo $_htmlId; ?>').hide();
    
    Event.observe('emptyAddBtn<?php echo $_htmlId; ?>', 'click', function () {
        $('grid<?php echo $_htmlId; ?>').show();
        $('empty<?php echo $_htmlId; ?>').hide();
        arrayRow<?php echo $_htmlId; ?>.add('', '');
    });
    
    // if no rows, hide grid and show button only
    <?php if (!$this->getArrayRows()):?>
        arrayRow<?php echo $_htmlId; ?>.showButtonOnly();
    <?php endif;?>
    
    // toggle the grid, if element is disabled (depending on scope)
    <?php if ($this->getElement()->getDisabled()):?>
        toggleValueElements({
            checked:true
        }, $('grid<?php echo $_htmlId; ?>').parentNode);
    <?php endif;?>
    
//]]>
</script>
    

Nun haben wir ein Modul, das im Magento Backend vollständig und beliebig konfiguriert werden kann.

Magento Versandarten Konfiguration
Unser Modul in der Magento Versandarten Konfiguration

Der logische Teil

Bisher haben wir die Versandmethode soweit das wir diese im Backend einrichten können, was fehlt ist der logische Teil der Berechnung. Wichtig ist hier einzig und allein die Funktion collectRates() innerhalb der Klasse Mage_Shipping_Model_Carrier_Abstract. Diese implementieren wir nun.

app/code/local/Webmasterpro/Multishipping/Model/Carrier/Multishipping.php  
PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php


class Webmasterpro_Multishipping_Model_Carrier_MultistoreShipping
    extends Mage_Shipping_Model_Carrier_Abstract
{
    
    /**
     * unique internal shipping method identifier
     *
     * @var string [a-z0-9_]
     */
    protected $_code = 'multistore_shipping';
    
    /**
     * Collect rates for this shipping mehtod based on information in $request
     *
     * @param Mage_Shipping_Model_Rate_Request $request
     * @return Mage_Shipping_Model_Rate_Result
     */
    public function collectRates(Mage_Shipping_Model_Rate_Request $request)
    {
        // skip if not enabled
        if (!Mage::getStoreConfig('carriers/' . $this->_code . '/active'))
            return false;
        
        $result = Mage::getModel('shipping/rate_result');
        
        $priceTable = unserialize(Mage::getStoreConfig('carriers/' . $this->_code . '/pricetable'));
        
        // check country
        if (is_array($priceTable))
        {
            foreach ($priceTable as $key => $price)
            {
                if ($price['country_id'] == $request->getDestCountryId())
                {
                    $method = Mage::getModel('shipping/rate_result_method');
                    
                    $method->setCarrier($this->_code);
                    $method->setCarrierTitle(Mage::getStoreConfig('carriers/' . $this->_code . '/title'));
                    
                    $quote = Mage::getSingleton('checkout/session')->getQuote();
                    $total = $request->getBaseSubtotalInclTax();
                    
                    // check coupon code
                    if ($quote->getCouponCode() !== '' && $quote->getCouponCode() !== false)
                    {
                        $calcDiscountAmount = $request->getPackageValue() - $request->getPackageValueWithDiscount();
                        $total = $total - $calcDiscountAmount;
                    }
                    
                    if ($total >= $price['free_shipping_price'] || $request->getFreeShipping() === true)
                    {
                        $shippingCost = '0.00';
                    }
                    else
                    {
                        $shippingCost = $price['price'];
                    }
                    
                    $method->setMethod('multistore_shipping');
                    $method->setMethodTitle(Mage::getStoreConfig('carriers/' . $this->_code . '/name'));
                    $method->setCost($shippingCost);
                    $method->setPrice($shippingCost);
                    
                    $result->append($method);
                }
            }
        }
        
        return $result;
    }
    
    public function getAllowedMethods()
    {
        return array('multistore_shipping' => $this->getConfigData('name'));
    }
    
}

Der Code prüft für das angegebene Versandland wie hoch die Versandkosten sind (definiert im Adminbereich), oder ob die Versandkostenfrei Grenze erreicht ist und setzt die Versandkosten auf 0.00.

Eigentlich wären wir jetzt fertig mit der Implementierung des Moduls, allerdings verlangt Magento einer Helper-Klasse auch wenn wir diese nicht benötigen. D.h. wir legen eine leere Klasse an.

app/code/local/Webmasterpro/Multishipping/Helper/Data.php  
PHP
1
2
3
4
5
6
<?php

class Webmasterpro_Multishipping_Helper_Data extends Mage_Shipping_Helper_Data
{
    
}

Fazit

Unser Modul ist nun fertig und einsatzbereit. Wir haben in diesem Tutorial gelernt wie der strukturelle Aufbau eines Magento Moduls aussieht. Wie man bereits vorhandene Klassen überschreibt und wie eine Extension mittels XML-Dateien konfiguriert wird.


Wikiseite bearbeiten

Diese Seite kann von jedem registrierten Benutzer bearbeitet werden. Bisher haben 2 Personen an der Seite "Tutorial: Magento Extension erstellen" mitgewirkt.

Sie haben einen Fehler entdeckt oder möchten etwas ergänzen? Dann können Sie nach der Anmeldung "Tutorial: Magento Extension erstellen" hier bearbeiten.

Mitarbeiter
  • Webentwickler bei Team23, Djangonaut und Open-Source-Fan. Mehr Infos: www.gironimo.org
  • Meine Schwerpunkte liegen im Bereich Grafikdesign, SEO und Management. Seit sieben Jahren bin ich als Geschäftsführer der Team23 GbR tätig, die Webdesign in Augsburg anbietet, sowie Webmasterpro.de betreut.