Magento 2.0 Development Version - A First Look

Magento 2

Magento has made the development version of the 2.0 branch available in a public SVN repository on its server. To take a look at development, you can simply check out the code at http://mage2.magentocommerce.com/svn/public/ and have a look around. A look under the hood is worthwhile. Anyone who was afraid that everything would change with version 2 can relax at this point. Not everything has been redesigned. You immediately feel at home and find your classes. Controllers, models, and blocks are still stored in the same places. However, everything related to presentation is now found directly in the module. No more annoying scrolling in the IDE!!!

Design/Templates

Templates are now found in the "view" directory inside the module. There they are separated into "frontend" and "adminhtml". JavaScript files belonging to the module can now be placed in the "skin" directory, also inside the module. They can be accessed through the template in the .phtml file, for example.

Example from onepage.phtml:

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

What stands out is that module aliases have been abolished. The full module name is now always used. This makes things less confusing. Layout XML files are now also found in the module's "view" directory. The file stored here is now the default layout. The so-called "base theme" has been removed, since all files shipped with an extension now reside directly in the view directory. If you want to override a layout.xml file in your theme, you can still do so. The "iphone" theme is a good place to see how this works. You now simply create a directory with the module name, e.g. Mage_Checkout. You can then put your own layout.xml there, which is merged with the original file. An interesting read in this context is the article in the Magento 2 wiki explaining the new, adjusted XML merging. [Wiki: Configuration File Merging]https://wiki.magento.com/display/MAGE2DOC/Configuration+File+Merging Another new feature is that themes can now have metadata. The "theme.xml" file inside the theme directory serves this purpose. It lets you define minimum requirements and give the theme a proper name. This will probably be needed for the layout manager introduced in Magento 2.0.

<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

In version 2, new Magento classes are still created through factory methods that allow models, blocks, and helpers to be overridden. But here, too, the "module aliases" are gone. Instead, the full class name is specified, which also simplifies development and searching. I can also well imagine that autoloader performance has increased significantly. Speaking of the autoloader... it is now found as the Magento_Loader class in the "lib" directory. The loader can work with a so-called class map to provide the necessary performance. Magento is thus taking a similar approach to the guys developing Zend Framework 2. There, too, you can now work with a class-map autoloader. You can find an explanation of how this works here:

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 Scripts

Setup scripts have now also been separated from one another. Data setup scripts are now found in the module's "data" directory. SQL setup scripts still belong in the "sql" directory. Not much has changed here otherwise.

SQL setup example:

<?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);

Data setup example:

<?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();

Quality Assurance / Testing

A major shortcoming of Magento 1.X versions is how difficult it is to test the system properly without modifying some core classes. For example, testing individual controllers is very difficult. Likewise, the many singletons in the system tend to get in the way of testing, because it is no longer straightforward to clean certain things up for the next test case. This has been addressed, too!!! For me, this is the biggest change in Magento 2. This measure should greatly increase confidence in the software and its stability. The tests are found in the installation's "dev" directory. Everything is neatly separated. There are integration tests and tests that check for messy code, duplicates, coding standards, etc. Everything the community has criticized is now being implemented properly and made available in SVN as well. I am really excited to see this now. A lot of work by the Magento developers has gone into the test framework. The tests are based on PHPUnit, which most developers are familiar with anyway. To enable proper testing, the Magento developers have created several listeners for PHPUnit that can evaluate new annotations in DocBlocks. This makes it possible, for example, to set Magento's store config for a test case without saving it directly in core_config_data. After the test runs, everything is back to the way it was. You can also create test fixtures. For example, you can have a script assemble a few dummy products. These fixtures can then also be used for testing through the DocBlock. Really a great thing!

Example of a controller test that used to be almost impossible:

<?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);
    }

    //...

Example with adjusted 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());
    }

//...

Interim Conclusion

From my perspective, Ebay's involvement really seems to be making a difference in QA so far. Everything that has been criticized is being tackled. Since the current version is only an early alpha, we can wait and see what else happens. In any case, I am looking forward to the new Magento version.