Magento 2

Magento hat die Entwicklungsversion des 2.0 Branch in einem öffentlichen SVN Repository auf dem Server bereitgestellt. Um einen Blick auf die Entwicklung zu werfen kann man sich den Code einfach unter der URL http://mage2.magentocommerce.com/svn/public/ auschecken und einmal reinschauen. Ein Blick unter die Haube lohnt sich. Wer Angst hatte, dass sich mit Version 2 alles ändert, der kann an dieser Stelle beruhigt sein. Es ist nicht alles neu Designt worden. Man fühlt sich direkt heimisch und findet seine Klassen. Die Controller, Models und Blocks sind noch an der gleichen Stelle abgelegt. Alles was allerdings mit der Darstellung zu tun hat, findet man nun direkt im Modul. Kein lästiges scrollen mehr in der IDE!!!

Design/Templates

Die Templates findet man jetzt im Verzeichnis “view” innerhalb des Moduls. Dort gibt es dann die Trennung nach “frontend” und “adminhtml”. Javascripts die zum Modul gehören kann man nun in das Verzeichnis “skin” ebenfalls innerhalb des Moduls ablegen. Ein Zugriff kann z.B. über das Template in der .phtml Datei geschehen.

Beispiel aus der onepage.phtml:

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

Was auffällt ist, dass die Modul-Kürzel abgeschafft wurden. Jetzt wird immer der volle Modulname verwendet. Dies sorgt für weniger Irritation. Die Layout XML Dateien findet man nun auch im Modul im Verzeichnis “view”. Die hier abgelegte Datei ist jetzt das Standard-Layout. Weggefallen ist dafür der sogenannte “Base-Theme” da nun alle mit einer Extension ausgelieferten Dateien direkt im Verzeichnis view liegen. Wer in seinem Theme eine layout.xml Datei überschreiben möchte kann dies aber immer noch tun. Am Theme “iphone” kann man sich das ganze auch gut anschauen. Es wird jetzt einfach ein Verzeichnis mit dem Modulnamen z.B. Mage_Checkout angelegt. Dort kann dann eine eigene layout.xml hinterlegt werden die mit der originalen Datei gemerged wird. Interessant ist in diesem Fall der Artikel im Magento 2 Wiki der das neue angepasste XML mergen erklärt. [Wiki: Configuration File Merging]https://wiki.magento.com/display/MAGE2DOC/Configuration+File+Merging Neu ist jetzt auch, dass Themes mit Metainformationen ausgetattet werden können. Dazu dient die Datei “theme.xml” innerhalb des Theme Verzeichnisses. Darin können Mindestanforderungen definiert werden und dem Theme ein richtiger Namen gegeben werden. Das wird wahrscheinlich für den in Magento 2.0 eingeführten Layout-Manager benötigt.

<design>
    <package code="default">
        <title>Default</title>
        <theme version="2.0.0.0" code="default">
            <title>Default</title>
            <requirements>
                <magento_version from="2.0.0.0-dev1" to="*"/>
            </requirements>
        </theme>
    </package>
</design>

Autoloader

Das Erstellen von neuen Magento Klassen geschieht auch in Version 2 weiterhin über Factory Methoden die das überschreiben von Models, Blöcken und Helpern ermöglichen. Aber auch hier fallen die “Modulkürzel” weg. Es wird stattdessen der gesamte Klassenname hinterlegt, was die Entwicklung und Suche ebenfalls vereinfacht. Ich könnte mir hier auch gut vorstellen, dass die Autoloader-Performance deutlich gestiegen ist. Apropos Autoloader… Dieser ist jetzt als Klasse Magento_Loader im Verzeichnis “lib” zu finden. Der Loader kann über eine sogenannte Class-Map arbeiten die für die notwendige Performance sorgt. Damit geht man bei Magento ähnliche Wege wie die Jungs von der Zend Framework 2 Entwicklung. Auch dort kann man jetzt mit einen Class-Map Autoloader arbeiten. Eine Erklärung wie so etwas funktioniert findet ihr hier: 

http://zfcookbook.org/post/show/id/post-klassen_laden_im_zf2_mit_dem_classmapautoloader

// Beispiel wir jetzt Kategorien geladen werden.

$category = createObject('Mage_Catalog_Model_Category')
            ->setStoreId(Mage::app()->getStore()->getId())
            ->load($categoryId);

Setup Scripte

Die Setup-Scripte wurden nun ebenfalls voneinander getrennt. Setup-Script für Daten sind jetzt im Verzeichnis “data” des Moduls zu finden. Die SQL-Setup-Scripte sind weiterhin im Verzeichnis “sql” aufzubewahren. Hier hat sich soweit nicht viel geändert.

Beispiel SQL Setup:

<?php

//....

$installer = $this;
/* @var $installer Mage_Catalog_Model_Resource_Setup */

$installer->startSetup();

/**
* Create table 'catalog_product_entity'
*/
$table = $installer->getConnection()
    ->newTable($installer->getTable('catalog_product_entity'))
    ->addColumn('entity_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
        'identity'  => true,
        'unsigned'  => true,
        'nullable'  => false,
        'primary'   => true,
        ), 'Entity ID')
    ->addColumn('entity_type_id', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array(
        'unsigned'  => true,
        'nullable'  => false,
        'default'   => '0',
        ), 'Entity Type ID')
    ->addColumn('attribute_set_id', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array(
        'unsigned'  => true,
        'nullable'  => false,
        'default'   => '0',
        ), 'Attribute Set ID')
    ->addColumn('type_id', Varien_Db_Ddl_Table::TYPE_TEXT, 32, array(
        'nullable'  => false,
        'default'   => Mage_Catalog_Model_Product_Type::DEFAULT_TYPE,
        ), 'Type ID')
    ->addColumn('sku', Varien_Db_Ddl_Table::TYPE_TEXT, 64, array(
        ), 'SKU')
    ->addColumn('has_options', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array(
        'nullable'  => false,
        'default'   => '0',
        ), 'Has Options')
    ->addColumn('required_options', Varien_Db_Ddl_Table::TYPE_SMALLINT, null, array(
        'unsigned'  => true,
        'nullable'  => false,
        'default'   => '0',
        ), 'Required Options')
    ->addColumn('created_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
        ), 'Creation Time')
    ->addColumn('updated_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
        ), 'Update Time')
    ->addIndex($installer->getIdxName('catalog_product_entity', array('entity_type_id')),
        array('entity_type_id'))
    ->addIndex($installer->getIdxName('catalog_product_entity', array('attribute_set_id')),
        array('attribute_set_id'))
    ->addIndex($installer->getIdxName('catalog_product_entity', array('sku')),
        array('sku'))
    ->addForeignKey($installer->getFkName('catalog_product_entity', 'attribute_set_id', 'eav_attribute_set', 'attribute_set_id'),
        'attribute_set_id', $installer->getTable('eav_attribute_set'), 'attribute_set_id',
        Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
    ->addForeignKey($installer->getFkName('catalog_product_entity', 'entity_type_id', 'eav_entity_type', 'entity_type_id'),
        'entity_type_id', $installer->getTable('eav_entity_type'), 'entity_type_id',
        Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
    ->setComment('Catalog Product Table');
$installer->getConnection()->createTable($table);

Beispiel Daten Setup:

<?php

//....

/* @var $installer Mage_Catalog_Model_Resource_Resource_Setup */
$installer = $this;

// Create Root Catalog Node
createObject('Mage_Catalog_Model_Category')
    ->load(1)
    ->setId(1)
    ->setStoreId(0)
    ->setPath(1)
    ->setLevel(0)
    ->setPosition(0)
    ->setChildrenCount(0)
    ->setName('Root Catalog')
    ->setInitialSetupFlag(true)
    ->save();

/* @var $category Mage_Catalog_Model_Category */
$category = createObject('Mage_Catalog_Model_Category');

$category->setStoreId(0)
    ->setName('Default Category')
    ->setDisplayMode('PRODUCTS')
    ->setAttributeSetId($category->getDefaultAttributeSetId())
    ->setIsActive(1)
    ->setPath('1')
    ->setInitialSetupFlag(true)
    ->save();

Qualitätssicherung / Testing

Ein großes Manko der 1.X Magento Versionen ist die Schwierigkeit das System gescheit zu testen ohne einige Core-Klassen anzupassen. So ist es z.B. sehr schwer einzelne Controller zu testen. Ebenso sind die vielen Singletons im System eher hinderlich beim testen da man einiges nicht mehr so ohne weiteres für den nächsten Test-Case aufräumen kann. Auch hier wurde Abhilfe geschaffen!!! Das ist für mich die größte Änderung an Magento 2. Das Vertrauen in die Software und die Stabilität sollten durch diese Maßnahme enorm steigen. Die Tests findet man im Verzeichnis “dev” der Installation. Es ist alles sauber aufgetrennt. Es gibt Integration Tests und Tests die auf Unsauberkeiten, Dubletten, Code-Standard etc. im Code prüfen. Alles was von der Community bemängelt wurde wird nun sauber umgesetzt und auch im SVN bereitgestellt. Ich bin echt begeistert dies nun zu sehen. Im Test-Framework steckt eine Menge Arbeit der Magento Entwickler. Die Tests basieren auf PHPUnit, was den meisten Entwicklern sowieso bekannt ist. Damit sauber getestet werden kann haben die Magento Entwickler einige Listener für PHPUnit angelegt die neue Annotationen im Docblock auswerten können. Damit ist es möglich z.B für den Test-Case die Magento Store-Config zu setzen ohne, dass diese direkt im core_config_data gespeichert wird. Nach der Testausführung ist wieder alles beim alten. Ebenso kann man Test-Fixtures anlegen. Also z.B. ein paar Dummy-Produkte in einem Script zusammenbauen lassen. Diese Fixtures können dann ebenfalls zum Testen über den DocBlock herangezogen werden. Echt eine Super Sache!

Beispiel eines Controller Tests der früher fast unmöglich war:

<?php

/**
* Auszug aus der ProductContorllerTest.php unter
* dev/tests/integration/testsuite/Mage/Catalog/controllers/
*/

    //...

    /**
    * @magentoDataFixture Mage/Catalog/controllers/_files/products.php
    */
    public function testViewAction()
    {
        $this->dispatch('catalog/product/view/id/1');

        /** @var $currentProduct Mage_Catalog_Model_Product */
        $currentProduct = Mage::registry('current_product');
        $this->assertInstanceOf('Mage_Catalog_Model_Product', $currentProduct);
        $this->assertEquals(1, $currentProduct->getId());

        $lastViewedProductId = Mage::getSingleton('catalog/session')->getLastViewedProductId();
        $this->assertEquals(1, $lastViewedProductId);

        /* Layout updates */
        $handles = Mage::app()->getLayout()->getUpdate()->getHandles();
        $this->assertContains('PRODUCT_TYPE_simple', $handles);
        $this->assertContains('PRODUCT_1', $handles);

        $responseBody = $this->getResponse()->getBody();
        /* Product info */
        $this->assertContains('Simple Product 1 Name', $responseBody);
        $this->assertContains('Simple Product 1 Full Description', $responseBody);
        $this->assertContains('Simple Product 1 Short Description', $responseBody);
        /* Stock info */
        $this->assertContains('$1,234.56', $responseBody);
        $this->assertContains('In stock', $responseBody);
        $this->assertContains('Add to Cart', $responseBody);
        /* Meta info */
        $this->assertContains('<title>Simple Product 1 Meta Title</title>', $responseBody);
        $this->assertContains('<meta name="keywords" content="Simple Product 1 Meta Keyword" />', $responseBody);
        $this->assertContains('<meta name="description" content="Simple Product 1 Meta Description" />', $responseBody);
    }

    //...

Beispiel mit angepasster Store-Config:

<?php

/**
* Auszug aus der Datei AreaTest.php im Verzeichnis dev/tests/integration/testsuite/Mage/Core/Model/App
* Im Test wird doe Store-Config über einen DocBlock definiert.
*/

//...

    /**
    * @magentoConfigFixture current_store design/theme/full_name default/default/blank
    * @magentoAppIsolation enabled
    */
    public function testDetectDesignStoreConfig()
    {
        $this->_model->detectDesign();
        $this->assertEquals('default/default/blank', Mage::getDesign()->getAreaParameters());
    }

//...

Zwischenfazit

Aus meiner Sicht scheint sich bisher der Einstieg von Ebay echt bei der QS niederzuschlagen. Alles was bisher bemängelt wurde wird angegangen. Da es sich bei der jetzigen Version nur um eine frühe Alpha handelt darf man abwarten was noch passieren wird. Ich freue mich auf jeden Fall auf die neue Magento Version.