8 Effective Ways to Really Boost Magento Page Speed!

I think there’s no need to write lengthy paragraphs about the disadvantages of a slow website performance, but let me mention just a few issues that I feel important. Apart from the fact that user experience declines seriously while visiting a slow website, Google takes into account page load speeds and ranks lower those pages that perform poorly. In the case of online stores, it can be disastrous since your competitors will be ranked higher.

According to official statistics, even if visitors find your store, every extra second they have to wait will reduce the chance of conversion by 7%. You can measure page speed with external tools that give suggestions for reaching the optimal solution. Such websites include:

Our favourite is New Relic, a software analytics tool with which you can always see the speed performance of your website and many other useful attributes as well. These solutions can help a great deal in detecting slow speeds, however, they do not see the faulty processes within Magento. In the following I will write about how to examine these inner processes and how to detect and fix them.

 

Detection

Default profiler

You can use a default profiler for measuring the speed of internal processes. Thanks to the profiler, you can get an insight into the speed performance and memory usage of the controllers, actions, blocks, observers and other events. To use this, you need to authorize profiling in the System / Configuration / Developer / Debug / Profiler section. If you run speed analytics on a public page, you need to define your computer’s IP address in the System / Configuration / Developer / Developer Client Restriction. Next, you need to remove the comment in the index.php file from the line preceding this:

 Varien_Profiler::enable(); 
The result will look similar to this: Magento Code Profiling In your own modules you can use the profiler option by inserting these lines:

Varien_Profiler::start('unique_profile_identifier');
//... the code lines to be analysed
Varien_Profiler::stop('unique_profile_identifier');

For profiling the SQL queries you need to make your settings in the app/etc/local.xml:

<default_setup>
     <connection>
         <host><![CDATA[localhost]]></host>
         <username><![CDATA[mage_user]]></username>
         <password><![CDATA[mage_password]]></password>
         <dbname><![CDATA[mage_db]]></dbname>
         <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
         <model><![CDATA[mysql4]]></model>
         <type><![CDATA[pdo_mysql]]></type>
         <pdoType><![CDATA[]]></pdoType>
         <active>1</active>
         <profiler>1</profiler>
     </connection>
 </default_setup>

The table below shows what the result of these settings will look like: Magento Code Profiling 2

Aoe Profiler

Let’s be honest, it is quite hard to read and to see how these data are structured. To solve this you can use the Aoe Profiler plugin, which displays the data in a hierarchical structure with the help of small diagrams. These show you clearly those elements that spoil the speed performance of the website.

Aoe Profiler Magento Code

 

However, here at AionHill, we use an even more competent solution, our own module that helps us detect page speed problems more effectively.

 

Introducing AionHill_Profiler

 

Blocks

The blocks are shown in a hieararchical structure indicating the time (seconds) needed for displaying them, whether the given block uses cache, and we can also see the SQL queries that are run while displaying the block.

Magento Code Profiler by AionHill

SQL queries

The module also displays the SQL queries used on the page. Here we also use diagrams, time figures and stack trace figures that show the line and Magento class from which the SQL query started.

Magento SQL Queries

 

Repetitive SQL queries

The module notifies us if there are completely identical SQL queries on the page. The table below shows how many of them are present and how frequently they occur and also how much of the MySQL server’s time was consumed.   Magento SQL Queries Repeats

 

Cycle-structured SQL queries

Finally, we detect the cycle-structured, but not necessarily identical, SQL queries:

Magento Loop SQL Queries

 

Solution proposals

And now let’s see some real-life examples revealing what we can do for tackling the issues that have been detected.

 

Eliminate the cycle-structured SQL queries.

No matter how fast the SQL server is, using its capacity unnecessarily still influences its performance. Download the needed data in one bulk and not in cycles one by one, whenever possible. As examples, I show you two functions that return with the average price based on product identifiers set as parameters. The first method, which is wrong, loads in the products in one cycle one by one and then adds the price to one bulk, from which it calculates the average price out of the cycle, and then returns with it.

/**
 * get Average Price (bad example)
 *
 * @param array $productIds product ids
 *
 * @return float
 */
public function getAveragePriceBadMethod(array $productIds)
{
 $prices = array();

 foreach ($productIds as $productId) {
 $product = Mage::getModel('catalog/product')->load($productId);
 $prices[] = $product->getPrice();
 }

 return array_sum($prices) / count($prices);
}

An example for a fine solution: Instead of making a query for each product separately, we make a query for the whole collection containing them and then we use these items.


/**
 * get Average Price (good example)
 *
 * @param array $productIds product ids
 *
 * @return float
 */
public function getAveragePriceGoodMethod(array $productIds)
{
    if (empty($productIds)) {
        return 0;
    }

    $prices = array();
    $products = Mage::getResourceModel('catalog/product_collection')
        ->addAttributeToSelect('price')
        ->addAttributeToFilter('entity_id', array('in' => $productIds));

    foreach ($products as $product) {
        $prices[] = $product->getPrice();
    }

    return array_sum($prices) / count($prices);
}

Indeed, it is still not the best approach because we need the prices only, so it is not necessary to load the whole collection. When only the values of one field are required, use the following method:


/**
 * get Average Price (good example)
 *
 * @param array $productIds product ids
 *
 * @return float
 */
public function getAveragePrice(array $productIds)
{
    if (empty($productIds)) {
        return 0;
    }

    $products = Mage::getResourceModel('catalog/product_collection')
        ->addAttributeToSelect('price')
        ->addAttributeToFilter('entity_id', array('in' => $productIds));

    $select = $products->getSelect()
        ->reset(Zend_Db_Select::COLUMNS)
        ->columns('price');

    $prices = $products->getConnection()->fetchCol($select);

    return array_sum($prices) / count($prices);
}

It is also a usual problem that a second query is initiated when the product is already in the shopping cart. The quote model ensures that the items related products are already present, so there is no need for subsequent model loads.

/**
 * get Quote Weight (bad example)
 *
 * @return float
 */
public function getQuoteWeightBadExample()
{
    $quoteItems = Mage::getSingleton('checkout/cart')->getQuote()->getAllItems();
    $quoteWeight = 0;

    /** @var Mage_Sales_Model_Quote_Item $quoteItem */
    foreach ($quoteItems as $quoteItem) {
        $product = Mage::getModel('catalog/product')->load($quoteItem->getProductId());
        $quoteWeight += $product->getWeight() * $quoteItem->getQty();
    }

    return $quoteWeight;
}

/**
 * get Quote Weight (good example)
 *
 * @return float
 */
public function getQuoteWeight()
{
    $quoteItems = Mage::getSingleton('checkout/cart')->getQuote()->getAllItems();
    $quoteWeight = 0;

    /** @var Mage_Sales_Model_Quote_Item $quoteItem */
    foreach ($quoteItems as $quoteItem) {
        $quoteWeight += $quoteItem->getProduct()->getWeight() * $quoteItem->getQty();
    }

    return $quoteWeight;
}

Eliminate recurring SQL queries

Of course, there are justifiable cases when we need to repeat the same query, e.g. reloading after modification for checking purposes. But many times there are planning or developing errors in the background. Let’s see what the most common mistakes are. We don’t store the return value of a method that is used several times:


/**
  * get Feature Categories (bad example)
  *
  * @return Mage_Catalog_Model_Resource_Category_Collection
  * @throws Mage_Core_Exception
  */
 public function getFeatureCategoriesBadExample()
 {
     $categories = Mage::getModel('catalog/category')->getCollection()
         ->addAttributeToSelect('*')
         ->addAttributeToFilter('name', array('like' => '%feature%'))
         ->load();
 
     return $categories;
 }

If we use the same method in 10 different places on a single page, then we make 9 unnecessary queries using the MySQL server! So it is wise to store the results in a class variable when calling the method the first time and later use the stored items without using extra resources.


/**
  * Local cache for feature categories
  *
  * @var null|Mage_Catalog_Model_Resource_Category_Collection
  */
 protected $_featureCategories = null;
 
 /**
  * get Feature Categories (good example)
  *
  * @return Mage_Catalog_Model_Resource_Category_Collection
  * @throws Mage_Core_Exception
  */
 public function getFeatureCategories()
 {
     if (!is_null($this->_featureCategories)) {
         return $this->_featureCategories;
     }
 
     $this->_featureCategories = Mage::getModel('catalog/category')->getCollection()
         ->addAttributeToSelect('*')
         ->addAttributeToFilter('name', array('like' => '%feature%'))
         ->load();
 
     return $this->_featureCategories;
 }

Another common mistake is using model instead of singleton. It can cause performance problems right away that a class is present in multiple copies instead of one, but if more complex procedures are run, the situation can get much graver. In the following example you can see an extended shopping cart. I inserted a category collection load in its constructor.


/**
 * Class My_Module_Model_Checkout_Cart
 */
class My_Module_Model_Checkout_Cart extends Mage_Checkout_Model_Cart
{
    /** @var Mage_Catalog_Model_Resource_Category_Collection  */
    protected $_quoteCategories;

    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();

        $categoryIds = array();
        $quoteItems = $this->getQuote()->getAllItems();

        /** @var Mage_Sales_Model_Quote_Item $quoteItem */
        foreach ($quoteItems as $quoteItem) {
            $product = $quoteItem->getProduct();
            $categoryIds = array_merge($categoryIds, $product->getCategoryIds());
        }

        $this->_quoteCategories = Mage::getModel('catalog/category')->getCollection()
            ->addAttributeToSelect('*')
            ->addAttributeToFilter('entity_id', array('in' => array_unique($categoryIds)))
            ->load();
    }
}

It can work fine if we handle this extended class properly.


// bad example 
$productIds = Mage::getModel('my_module/checkout_cart')->getProductIds();
$itemsQty = Mage::getModel('my_module/checkout_cart')->getItemsQty();

// good example
$productIds = Mage::getSingleton('my_module/checkout_cart')->getProductIds();
$itemsQty = Mage::getSingleton('my_module/checkout_cart')->getItemsQty();

 

In the above example, wrongly, the class is present in more copies and thus the category query in the constructor will run in each case. The situation is the same if there are resource-demanding processes with different methods. Here, even if we use one class variable for caching, like in the previous example, the time consuming code lines are executed repeatedly since we have stored the previous calculations in another copy of the class. In the example below, which gives the correct solution, the object is present in one copy only and therefore there won’t be any unnecessary calculations. If, for some reason, you cannot use singleton, you can also use Magento Helpers, which are singleton classes, or Mage::registry for storing temporary data. These are very simple practices, but if you do not pay enough attention to them, the number of SQL queries may grow significantly.

 

Fixing long runtime SQL queries

Creating appropriate table indexes

Many times it well may be that the corresponding fields of a given table are not indexed. Here caution is needed because the more indexes you use, the longer the writing time will be, but searches and ordering will be considerably faster. It is very important to define the structure of the table and the indexes optimally. You can add indexes to the tables with the help of the installer integrated in the module.


$installer = $this;

$installer->startSetup();

$tableName = $installer->getTable('my_module/model');

if ($installer->getConnection()->isTableExists($tableName)) {
    $table = $installer->getConnection();

    try {
        $table->addIndex(
            $installer->getIdxName(
                'my_module/model',
                array(
                    'column1',
                    'column2',
                ),
                Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX
            ),
            array(
                'column1',
                'column2',
            ),
            array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX)
        );
    } catch (Exception $e) {
        Mage::logException($e);
    }
}

$installer->endSetup();

 

Extending indexing of the product flat tables

When there are many products, queries executed from product flat tables may be slower if you use filtering or ordering which is not field-indexed by Magento. You cannot index flat tables using the installer since Magento discards and re-creates these during indexing. However, you can modify the default indexes of the flat table with an observer. To make it work, you need to add an observer to the catalog_product_add_indexes event.


<events>
    <catalog_product_flat_prepare_indexes>
        <observers>
            <my_module_catalog_product_flat_prepare_indexes>
                <type>singleton</type>
                <class>my_module/observer</class>
                <method>catalogProductFlatPrepareIndexes</method>
            </my_module_catalog_product_flat_prepare_indexes>
        </observers>
    </catalog_product_flat_prepare_indexes>
</events>

/**
 * Add indexes to product flat table
 *
 * @param Varien_Event_Observer $observer observer
 *
 * @return void
 */
public function catalogProductFlatPrepareIndexes(Varien_Event_Observer $observer)
{
    /** @var Varien_Object $indexesObject */
    $indexesObject = $observer->getIndexes();
    /** @var array $indexes */
    $indexes = $indexesObject->getIndexes();

    $indexes['IDX_MY_ATTRIBUTE'] = array(
        'type' => Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX,
        'fields' => array('my_attribute')
    );

    $indexesObject->setIndexes($indexes);
}

The method above is always run when Magento re-creates the flat table due to the re-indexing process.

 

Eliminating resource-demanding SQL joins

In some cases, a slow-speed query cannot be fixed with using indexes only because we connect several large tables and therefore, inevitably, the MySQL server has to deal with huge amounts of data. Let’s suppose we would like to execute an ordering on the product list page based on inventory volume and rating. In this case we apply the following method:


$collection->joinField(
    'quantity',
    'cataloginventory/stock_item',
    'qty',
    'product_id=entity_id',
    '{{table}}.stock_id=1',
    'left'
);

$collection->joinField(
    'rating_summary',
    'review_entity_summary',
    'rating_summary',
    'entity_pk_value=entity_id',
    array(
        'entity_type' => 1,
        'store_id' => Mage::app()->getStore()->getId()
    ),
    'left'
);

$collection->setOrder($attribute, $direction);

Depending on the number of products and ratings, immense amounts of data can stack up and structuring these can take up a considerable amount of time. A great number of simple methods can be used in terms of My SQL queries. Now I’d like to mention that join is not always needed, only in those cases when we’d really use it.


if ($attribute == 'quantity') {
    $collection->joinField(
        'quantity',
        'cataloginventory/stock_item',
        'qty',
        'product_id=entity_id',
        '{{table}}.stock_id=1',
        'left'
    );
}

if ($attribute == 'rating_summary') {
    $collection->joinField(
        'rating_summary',
        'review_entity_summary',
        'rating_summary',
        'entity_pk_value=entity_id',
        array(
            'entity_type' => 1,
            'store_id' => Mage::app()->getStore()->getId()
        ),
        'left'
    );
}

$collection->setOrder($attribute, $direction);

With this simple trick we prevented connecting two large tables to the product collection. Now, connecting takes place only in the case of such tables that are truly needed.

 

Performance improvement of Magento Blocks

Whenever possible, it is recommended to use caching of Magento blocks. You can segment these cache data based on user groups and can also combine more segmentations. .


/**
 * construct
 *
 * @return void
 */
protected function _construct()
{
    $this->addData(
        array(
            'cache_lifetime' => 3600,
            'cache_key'      => 'MY_MODULE_' . $this->getExampleModel()->getId(),
            'cache_tags'     => array(My_Module_Model_Example::CACHE_TAG)
        )
    );
}

It’s worth using the so-called object cache for those methods that are called several times and it is not always needed to run the codes within them.


/**
 * get Category Collection
 *
 * @return Mage_Catalog_Model_Resource_Category_Collection|mixed
 * @throws Mage_Core_Exception
 */
public function getCategoryCollection()
{
    if ($this->hasData('category_collection')) {
        return $this->getData('category_collection');
    }

    $collection = Mage::getModel('catalog/category')->getCollection()
        ->addAttributeToSelect('*')
        ->addAttributeToFilter('parent_id', array('eq' => Mage::app()->getStore()->getRootCategoryId()));

    $this->setData('category_collection', $collection);
    return $collection;
}

Other useful development suggestions for better performance

Simple SQL queries

If you want to collect identifiers from a collection, it is better to solve this without a cycle:


// bad example
$ids = array();

$products = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToFilter('sku', array('like' => 'test-%'));

foreach ($products as $product) {
    $ids[] = $product->getId();
}

// good example
$ids = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToFilter('sku', array('like' => 'test-%'))
    ->getAllIds();

The getAllIds method is included in every Magento collection. If it is not the identifiers that you need, but another field, and that one only, then you can apply the following solution:


// bad example
$result = array();

$products = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToSelect('my_attribute')
    ->addAttributeToFilter('sku', array('like' => 'test-%'));

foreach ($products as $product) {
    $result[] = $product->getData('my_attribute');
}

// good example
$collection = Mage::getResourceModel('catalog/product_collection')
    ->addAttributeToSelect('test')
    ->addAttributeToFilter('sku', array('like' => 'test-%'));

$select = $collection->getSelect()
    ->reset(Zend_Db_Select::COLUMNS)
    ->columns('test')
    ->group('test');

$result =  $collection->getConnection()->fetchCol($select);

If you just want to check if a value exists in the table:


// bad example
$firstItem = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToFilter('hello', array('gt' => 3))
    ->getFirstItem();

$hasData = $firstItem->getId() != null;

// good example
$size = Mage::getResourceModel('catalog/product_collection')
    ->addAttributeToFilter('hello', array('gt' => 3))
    ->getSize();
$hasData = $size > 0;

Simplify whenever possible

Again, simple things, but they can help a lot with shortening runtimes and having shorter codes also makes life easier. For example, if you need only the identifier of the logged-in user:


// less effective
$customerId = Mage::getSingleton('customer/session')->getCustomer()->getId();
// a little shorter
$customerId = Mage::getSingleton('customer/session')->getCustomerId();

Similarly, the products in the shopping cart and their identifiers are handled as follows:


$quoteItems = Mage::getSingleton('checkout/cart')->getQuote()->getAllItems();

foreach ($quoteItems as $item) {
    // when only product ID is needed
    // it's a little longer
    $productId = $item->getProduct()->getId();

    // more effective
    $productId = $item->getProductId();


    // if the product is needed
    // this is a really bad solution
    $product = Mage::getModel('catalog/product')->load($item->getProductId());

    // this is the right solution
    $product = $item->getProduct();
}

Summary

We have seen some useful tips in terms of improving the load speed of your Magento website and by far we haven’t shown all of these. Please don’t forget that your visitors are potential customers and that they can be lost easily if they don’t find your website user-friendly. No matter how wonderful the design or the layout is, a slow website can destroy the originally positive user experience completely. Nowadays it is a must to handle this issue properly.

 

 

Ultimate Magento Admin Interface Customization Guide

Would you like to know how it is possible to set up a separate admin menu and add a grid/form to it? And what if you could organize your customers into a separately administered category structure so that you could offer custom deals to each of the customer categories? These needs can all be addressed within the Magento ecommerce framework by adding customized admin menus, grids and forms.

In this article we will describe:

  • How to create a new Magento admin menu
  • How to add a grid to the menu
  • How to create forms and inputs within them
  • How tabs work in the forms

 

There are two approaches to this challenge:

  1. A new admin menu should be set up for category listing and editing, and then adding a new tab to the customer editing where we can assign the relevant categories to the given customer.
  2. We add a customer list to the category editing where we can assign the customers to the given category. In the final solution we describe this second option because here we can handle these settings in one single location.

Planned steps of implementation:

  1. Planning of database items (database modifications are not described in this article)
  2. Creating new modules a (Customercategory)
    • Könyvtár struktúra létrehozása
    • magento customer category directory structure
    • Creating the necessary model, resource model, helper installers
    • App/etc/modules/Aion_Customercategory.xml
    • Creating the config.xml in the module’s etc library
    • Creating controllers
    • Creating Blocks
    • Creating Layout
    • Creating language files (csv)

Creating the module

Within Magento, when creating a new module, first we need to create a library (Customercategory) – where we want to place the module –, in the selected pool (local in our case), under namespace (aion). At the same time we create [module name].xml under app/etc/modules.


<?xml version="1.0"?>
 <config>
     <modules>
         <!-- namespace_modulename -->
         <Aion_Customercategory>
             <active>true</active>
             <!-- pool -->
             <codePool>local</codePool>
         </Aion_Customercategory>
     </modules>
 </config>

Library structure of the module:

  • Block – forms, grids and similar classes to be shown will be placed here
  • controllers – this contains controllers
  • etc – location of xml files to be used (config.xml, adminhtml.xml, …)
  • Helper – location of helper files
  • Model – location of model files
  • sql – location of database installer/update files

We need to set the basic actions in the config.xml of the module’s etc library to make our module functional:


<?xml version="1.0"?>
 <config>
     <modules>
         <Aion_Customercategory>
             <version>0.1.0</version>
         </Aion_Customercategory>
     </modules>
     <global>
         <models>
             <aion_customercategory>
                 <class>Aion_Customercategory_Model</class>
                 <resourceModel>aion_customercategory_resource</resourceModel>
             </aion_customercategory>
             <aion_customercategory_resource>
                 <class>Aion_Customercategory_Model_Resource</class>
                 <entities>
                     <category>
                         <table>aion_customercategory_category</table>
                     </category>
                     <customer>
                         <table>aion_customercategory_customer</table>
                     </customer>
                 </entities>
             </aion_customercategory_resource>
         </models>
         <blocks>
             <aion_customercategory>
                 <class>Aion_Customercategory_Block</class>
             </aion_customercategory>
         </blocks>
         <helpers>
             <aion_customercategory>
                 <class>Aion_Customercategory_Helper</class>
             </aion_customercategory>
         </helpers>
         <resources>
             <aion_customercategory_setup>
                 <setup>
                     <class>Mage_Core_Model_Resource_Setup</class>
                     <module>Aion_Customercategory</module>
                 </setup>
             </aion_customercategory_setup>
         </resources>
     </global>
 </config>

After finishing with the basic tasks, we need to create a menu in the admin panel to handle the different items. In our case we create this in the Customer main menu so we insert the following into the config.xml:

 </global>

     <adminhtml>
         <menu>
             <customer>
                 <children>
                     <aion_customercategory>
                         <title>Customer Categories</title>
                         <sort_order>9999</sort_order>
                         <action>adminhtml/customercategory/index</action>
                     </aion_customercategory>
                 </children>
             </customer>
         </menu>
     </adminhtml>
 </config>

We can see that we need to set the XML structure in a way that we want to define a menu (<menu>) in the admin (<adminhtml>), where we create a sub menu (<children>) under Customers (<customer>) and where we give the following settings: :

  • title – title of the menu
  • sort order – in which line should the menu be located
  • action – which method of which controller should be run when clicking on the menu

Now if we take a look at the admin panel, we can see the following (it is very important to empty the Magento cache after every modification in xml, so that fresh data are reloaded):

Magento Admin Grid Customization

Now we need to script the controller to address the call: We need to create a class/method pair in the xml as an action under the controllers library (every ending of every controller is Controller and all the methods need to have endings of Action). Since it will be an admin controller, it should be derived from the Mage_Adminhtml_Controller_Action parent class. controllers/Adminhtml/CustomercategoryController.php

class Aion_Customercategory_Adminhtml_CustomercategoryController
    extends Mage_Adminhtml_Controller_Action
 {
     public function indexAction()
     {
         die('ok');
     }
 }


Next we need to “tell” Magento to use our controllers by inserting the following data in the config.xml:

</adminhtml>
<admin>  
    <routers> 
        <adminhtml> 
            <args>  
                <modules> 
                    <Aion_Customercategory before="Mage_Adminhtml">Aion_Customercategory_Adminhtml</Aion_Customercategory>  
                </modules>     
            </args>    
        </adminhtml> 
    </routers>
</admin>

So it should handle the paths we defined before handling the Magento router. The after item is also an option (in our case it is irrelevant). However, if we want to use a system url, then before is the suitable property, so that our controller/action pair is run before default (after the changes were made, we need to empty the cache so that Magento can read the modifications). Now our method can be run, but we need to define the layout in order to set what form it should take after rendering. This should also be given in xml format including the following steps.

  • We need to define in the config.xml, under the adminhtml tag, those layout.xml files that we want to use:

...
</menu>
     <layout>
         <updates>
             <aion_customercategory>
                 <file>aion/customercategory.xml</file>
             </aion_customercategory>
         </updates>
     </layout>
 </adminhtml>
...

  • We need to load the layout in the indexAction and then render it out

class Aion_Customercategory_Adminhtml_CustomercategoryController
    extends Mage_Adminhtml_Controller_Action
 {
     public function indexAction()
     {
         $this->loadLayout();
         $this->renderLayout();
     }
 }

  • We need to create these under app/design/adminhtml/default/default/layout (which means that the describing file of our module’s layout xml will be /app/design/adminhtml/default/default/layout/aion/customercategory.xml).
    
    

<?xml version="1.0"?>
 <layout version="0.1.0">
     <adminhtml_customercategory_index>
         <reference name="menu">
             <action method="setActive">
                 <menupath>customer/customercategory</menupath>
             </action>
         </reference>
         <reference name="content">
             <block
                type="aion_customercategory/adminhtml_category"
                name="category_index"
            />
         </reference>
     </adminhtml_customercategory_index>
 </layout>

In the above example we define the layout of the adminhtml/customercategory/index (<adminhtml_customercategory_index>)path where we set that the selected menu customer/customercategory and its content will be an adminhtml/category block created by us.

Creating an index block

Naturally, when a user clicks on our menu, he or she would like to see a list with the items he or she selected, as well as would like to edit / create / delete items. In the above layout we set that our block will be the adminhtml_category, thus we need to create the app/code/local/Aion/Customercategory/Block/Adminhtml/Category.php, which manages the content given in the layout:

class Aion_Customercategory_Block_Adminhtml_Category

extends Mage_Adminhtml_Block_Widget_Grid_Container
{

public function __construct()
{
$this->_blockGroup = 'aion_customercategory';
$this->_controller = 'adminhtml_category';

parent::__construct();

$this->_headerText = $this->__('Customer Categories');
$this->_addButtonLabel = $this->__('Add New');

}
}

We can see that our class is derived from Mage_Adminhtml_Block_Widget_Grid_Container because here we want to display a grid later on. Within the constructor we need to set the _blockGroup and the _controller by which we tell Magento to create our grid block from Aion_Customercategory_Block_Adminhtml_Category_Grid. When the parent class renders out the layout, the following function is run:  

$this
->getLayout()
->createBlock(
$this->_blockGroup.'/' . $this->_controller . '_grid',
$this->_controller . '.grid'
)

Grid block

We need to create the grid class set above under app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Grid.php:

class Aion_Customercategory_Block_Adminhtml_Category_Grid
extends Mage_Adminhtml_Block_Widget_Grid
 {
public function __construct()
 {
 parent::__construct();
 }
protected function _prepareCollection()
 {
 return parent::_prepareCollection();
 }
protected function _prepareColumns()
 {
 return parent::_prepareColumns();
 }
 }

Our class derives from Mage_Adminhtml_Block_Widget_Grid, however we need to overwrite 3 methods so that it can function in our own module:

  • __construct – We need to set certain basic settings when creating instance
  • _prepareCollection – Defines where to call collection from
  • _prepareColumns – Defines what type of columns should be in the grid

The class, comprising our solutions, looks like this:

class Aion_Customercategory_Block_Adminhtml_Category_Grid extends Mage_Adminhtml_Block_Widget_Grid
 {
public function __construct()
 {
 parent::__construct();
 $this->setId('category_id');
 $this->setDefaultSort('category_id');
 $this->setDefaultDir('asc');
 $this->setSaveParametersInSession(true);
 }
protected function _prepareCollection()
 {
 $collection = Mage::getModel('aion_customercategory/category')
->getCollection();
 $this->setCollection($collection);
 return parent::_prepareCollection();
 }
protected function _prepareColumns()
 {
 $this->addColumn(
 'category_id',
 array(
 'header' => $this->__('ID'),
 'align'  => 'right',
 'width'  => '50px',
 'type'   => 'number',
 'index'  => 'category_id',
 )
 );
$this->addColumn(
 'name',
 array(
 'header' => $this->__('Name'),
 'index'  => 'name',
 )
 );
$this->addColumn(
 'active',
 array(
 'header'   => $this->__('Active'),
 'index'    => 'active',
 'type'     => 'options',
 'options'  => array(
 1 => Mage::helper('adminhtml')->__('Yes'),
 0 => Mage::helper('adminhtml')->__('No')
 ),
 )
 );
return parent::_prepareColumns();
 }
 }

After implementing this code, the following appears:

Magento Admin Grid Modification

We can see that we have a grid which needs to be completed.

Creating a form

Along with fulfilling the planning/implementing criteria, it is recommended to manage the “creating new” and “edit existing” forms with a common class. Thus the following modifications should be made in the controller:

class Aion_Customercategory_Adminhtml_CustomercategoryController
extends Mage_Adminhtml_Controller_Action
 {
...
 public function editAction()
 {
 $id    = $this->getRequest()->getParam('id');
 $model = Mage::getModel('aion_customercategory/category');
if ($id) {
 $model->load($id);
 if (!$model->getId()) {
 $this->_getSession()->addError(
 $this->__('This Category no longer exists.')
 );
 $this->_redirect('*/*/');
 return;
 }
 }
$data = $this->_getSession()->getFormData(true);
 if (!empty($data)) {
 $model->setData($data);
 }
Mage::register('current_model', $model);
$this
 ->loadLayout()
 ->renderLayout();
 }
 public function newAction()
 {
 $this->_forward('edit');
 }
...
 }

So, when creating new, it should redirect to Edit and is validated there. If there is an ID parameter, it loads the necessary data. Just like it was needed in case of the indexAction, here too it should be defined in the layout xml how the page is built up:

<?xml version="1.0"?>
 <layout version="0.1.0">
...
<adminhtml_customercategory_edit>
 <update handle="editor"/>
 <reference name="menu">
 <action method="setActive">
 <menupath>customer/customercategory</menupath>
 </action>
 </reference>
 <reference name="content">
 <block
type="aion_customercategory/adminhtml_category_edit"
name="category_edit"
/>
 </reference>
 <reference name="left">
 <block
type="aion_customercategory/adminhtml_category_edit_tabs"
name="category_tabs"
/>
 </reference>
 </adminhtml_customercategory_edit>
...
 </layout>

In the edit panel the content will be the category editor (reference name=”content”), on the left hand side it will be the tabs (reference name=”left”). In order to make something appear on the page, these two blocks should be created. The first block is adminhtml_category_edit, which we need to define under app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit.php:

class Aion_Customercategory_Block_Adminhtml_Category_Edit
extends Mage_Adminhtml_Block_Widget_Form_Container
 {
 protected function _getHelper()
 {
 return Mage::helper('aion_customercategory');
 }
public function __construct()
 {
 parent::__construct();
 $this->_blockGroup = 'aion_customercategory';
 $this->_controller = 'adminhtml_category';
 $this->_mode       = 'edit';
 $this->_updateButton(
'save',
'label',
$this->_getHelper()->__('Save Category')
);
 $this->_addButton(
'saveandcontinue',
array(
 'label'   => $this->_getHelper()->__(
'Save and Continue Edit'
),
 'onclick' => 'saveAndContinueEdit()',
 'class'   => 'save',
 ), -100);
$this->_formScripts[] = "
 function saveAndContinueEdit(){
 editForm.submit($('edit_form').action+'back/edit/');
 }
 ";
 }
 }

This is a Mage_Adminhtml_Block_Widget_Form_Container, and the form itself, based on the _blockGroup, _controller, _mode, is loaded from the class Aion_Customercategory_Adminhtml_Category_Edit_Form. We create this class in the app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Form.php file:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Form
extends Mage_Adminhtml_Block_Widget_Form
 {
 protected function _prepareForm()
 {
 $form = new Varien_Data_Form(array(
 'id'      => 'edit_form',
 'method'  => 'post',
 'enctype' => 'multipart/form-data',
 'action'  => $this->getUrl('*/*/save'),
 )
 );
 $form->setUseContainer(true);
 $this->setForm($form);
 return parent::_prepareForm();
 }
 }

We have an empty form, now we need to create the class managing the tabs. We set the adminhtml_category_edit_tabs for the block in the layout.xml, therefore the name of the class should be Aion_Customercategory_Block_Adminhtml_Category_Edit_Tabs. Thus the file should be app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Tabs.php:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Tabs
extends Mage_Adminhtml_Block_Widget_Tabs
 {
 public function __construct()
 {
 parent::__construct();
 $this->setId('category_tabs');
 $this->setDestElementId('edit_form');
 $this->setTitle(
Mage::helper('aion_customercategory')->__('Details')
);
 }
protected function _beforeToHtml()
 {
 $this->addTab(
 'category_section',
 array(
 'label' => Mage::helper('aion_customercategory')
->__('Category'),
 'title' => Mage::helper('aion_customercategory')
->__('Category'),
 'content' => $this->getLayout()
->createBlock(
'aion_customercategory/adminhtml_category_edit_category_form'
)->toHtml(),
 )
 );
 return parent::_beforeToHtml();
 }
 }

We can see that it can be set with the __consturctor what the identification will be, into which forms items will be loaded or what titles are seen above the tabs. We set the tabs dynamically in the _beforeToHtml, so the number of addTab() present here will be the number on the list as well. Now we need to create the adminhtml_category_edit_category_form block, which denotes the Aion_Customercategory_Block_Adminhtml_Category_Edit_Category_Form class, which will be in the app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Category/Form.php file:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Category_Form
extends Mage_Adminhtml_Block_Widget_Form
 {
 protected function _getModel()
 {
 return Mage::registry('current_model');
 }
protected function _getHelper()
 {
 return Mage::helper('aion_customercategory');
 }
protected function _prepareForm()
 {
 $model = $this->_getModel();
 $form  = new Varien_Data_Form(array(
 'id'     => 'edit_form',
 'action' => $this->getUrl('*/*/save'),
 'method' => 'post'
 ));
$fieldset = $form->addFieldset('base_fieldset', array(
 'legend' => $this->_getHelper()->__('Category Information'),
 'class'  => 'fieldset-wide',
 ));
if ($model && $model->getId()) {
 $modelPk = $model->getResource()->getIdFieldName();
 $fieldset->addField($modelPk, 'hidden', array(
 'name' => $modelPk,
 ));
 }
$fieldset->addField('name', 'text', array(
 'name'     => 'name', 'required' => true,
 'label'    => $this->_getHelper()->__('Name'),
 'title'    => $this->_getHelper()->__('Name'),
 )
 );
$fieldset->addField('active', 'select', array(
 'name'     => 'active', 'required' => true,
 'label'    => $this->_getHelper()->__('Active'),
 'title'    => $this->_getHelper()->__('Active'),
 'options'  => array(
 '1' => Mage::helper('adminhtml')->__('Yes'),
 '0' => Mage::helper('adminhtml')->__('No'),
 ),
 )
 );
if ($model) {
 $form->setValues($model->getData());
 }
 $this->setForm($form);
return parent::_prepareForm();
 }
 }

Besides the auxiliary methods we add the necessary fields to the panel in the _prepareForm() function. We then see a panel if we take a look at the page:

Magento Custom Admin Form Development

Thanks to this, we have created the panel of “creating new” and “edit item”, but we also need to save the data entered in the form. For this we need to define a saveAction() method in the controller (CustomercategoryController):

...
public function saveAction()
 {
 $redirectBack = $this->getRequest()->getParam('back', false);
 if ($data = $this->getRequest()->getPost()) {
 $id    = $this->getRequest()->getParam('id');
 $model = Mage::getModel('aion_customercategory/category');
 if ($id) {
 $model->load($id);
 if (!$model->getId()) {
 $this->_getSession()->addError(
 $this->__('This Category no longer exists.')
 );
 $this->_redirect('*/*/index');
 return;
 }
 }
try {
 $model->addData($data);
 $this->_getSession()->setFormData($data);
 $model->save();
 $this->_getSession()->setFormData(false);
 $this->_getSession()->addSuccess(
 $this->__('The Category has been saved.')
 );
 } catch (Mage_Core_Exception $e) {
 $this->_getSession()->addError($e->getMessage());
 $redirectBack = true;
 } catch (Exception $e) {
 $this->_getSession()->addError(
$this->__('Unable to save the Category.')
);
 $redirectBack = true;
 Mage::logException($e);
 }
if ($redirectBack) {
 $this->_redirect('*/*/edit', array('id' => $model->getId()));
 return;
 }
 }
 $this->_redirect('*/*/index');
 }
...

After saving, we find the items in the list that we entered:

Magento custom admin form save data

The general grid functions are already operational because of the class derived from Magento:

  • filtering
  • ordering
  • paging

We need to set what url should be activated if we click on a certain line. We do this in the Aion_Customercategory_Block_Adminhtml_Category_Grid class with the help of the following method:

public function getRowUrl($row)
 {
 return $this->getUrl('*/*/edit', array('id' => $row->getId()));
 }

In order to accomplish full functionality, deleting is also necessary, which can be reached from the editing form. After a confirmation pop-up it automatically redirects to the deleteAction() function. Therefore we need to implement that as well in the controller:

public function deleteAction()
 {
 if ($id = $this->getRequest()->getParam('id')) {
 try {
 $model = Mage::getModel('aion_customercategory/category');
 $model->load($id);
 if (!$model->getId()) {
 Mage::throwException($this->__('Unable to find a Category to delete.'));
 }
 $model->delete();
 $this->_getSession()->addSuccess(
 $this->__('The Category has been deleted.')
 );
 $this->_redirect('*/*/index');
 return;
 } catch (Mage_Core_Exception $e) {
 $this->_getSession()->addError($e->getMessage());
 } catch (Exception $e) {
 $this->_getSession()->addError(
 $this->__('An error occurred while deleting Category data. Please review log and try again.')
 );
 Mage::logException($e);
 }
 $this->_redirect('*/*/edit', array('id' => $id));
 return;
 }
 $this->_getSession()->addError(
 $this->__('Unable to find a Category to delete.')
 );
 $this->_redirect('*/*/index');
 }

Thanks to this, we can edit the categories. If we want to change the structure, we simply need to remove or add fields in the form by deleting or using the addField() method respectively. In order to be able to add customers to the categories in the planning phase, the ajax tab solution is recommended. A new tab should be added in the Aion_Customercategory_Block_Adminhtml_Category_Edit_Tabs class, which loads in the customer grid in an “ajax-based” way.  

protected function _beforeToHtml()
 {
 ...
 $this->addTab('customers', array(
 'label'     => $this->__('Customers'),
 'title'     => $this->__('Customers'),
 'url'       => $this->getUrl(
'*/*/customerstab',
array('_current' => true)
),
 'class'     => 'ajax'
 ));
return parent::_beforeToHtml();
 }

It can be seen under the url key that our controller’s customerstabAction method is called, in which we set the necessary data:

public function customerstabAction()
 {
 $saved_customer_ids = array();
 //Your load logic
 $this
 ->loadLayout()
 ->getLayout()
 ->getBlock('category.customer.tab')
 ->setSelectedCustomers($saved_customer_ids);
$this->renderLayout();
 }

It is important, however, that, as with all admin items, this too needs a descriptive block about what it should include:

...
<adminhtml_customercategory_customerstab>
 <block type="core/text_list" name="root" output="toHtml">
 <block type="aion_customercategory/adminhtml_category_edit_customer_grid" name="category.customer.tab"/>
</block>
 </adminhtml_customercategory_customerstab>
...

We need to create the block necessary, which is the following in our example: adminhtml_category_edit_customer_grid -> Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid -> app/code/local/Aion/Customercategory/Block/Adminhtml/Category/Edit/Customer/Grid.php As he have already executed this with our previous grid, the __construct(), _prepareCollection(), _prepareColumns() methods should be set here as well:

class Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid extends Mage_Adminhtml_Block_Widget_Grid
 {
 public function __construct()
 {
 parent::__construct();
 $this->setId('customerGrid');
 $this->setUseAjax(true);
 $this->setSaveParametersInSession(true);
 }
protected function _prepareCollection()
 {
 $collection = Mage::getResourceModel('customer/customer_collection')
 ->addNameToSelect();
$this->setCollection($collection);
 return parent::_prepareCollection();
 }
protected function _prepareColumns()
 {
 $this->addColumn('selected_customers', array(
 'header'    => $this->__('Select'),
 'type'      => 'checkbox',
 'index'     => 'entity_id',
 'align'     => 'center',
 'field_name'=> 'selected_customers[]',
 'values'    => $this->getSelectedCustomers(),
 ));
$this->addColumn('customer_name', array(
 'header'    => $this->__('Name'),
 'index'     => 'name',
 'align'     => 'left',
 ));
$this->addColumn('email', array(
 'header'    => $this->__('E-mail'),
 'index'     => 'email',
 'align'     => 'left',
 ));
return parent::_prepareColumns();
 }
 }

If the constructor contains setUseAjax(true), then it will read the grid in an “ajax-based” way, however, the gridUrl should also be given in this class:

public function getGridUrl()
 {
 return $this->getUrl('*/*/customersgrid', array('_current' => true));
 }

This, logically, is a controller/action call, therefore this should be managed in the controller as well:

public function customersgridAction()
 {
 $this
 ->loadLayout()
 ->getLayout()
 ->getBlock('category.customer.tab')
 ->setSelectedCustomers(
$this->getRequest()->getPost('customers', null)
);
$this->renderLayout();
 }

In order to render out this “ajax-based” page without any errors, we need to give the following data in the layout.xml:

...
<adminhtml_customercategory_customersgrid>
<block type="core/text_list" name="root" output="toHtml">
<block type="aion_customercategory/adminhtml_category_edit_customer_grid" name="category.customer.tab"/>
</block>
</adminhtml_customercategory_customersgrid>
...

When we have finished with this, we can see our “ajax-based” customer grid under a tab:

Magento Admin Customer Grid

The next step is to store the selected customer entities in the given category, which means we need to put the IDs in a hidden input, so that these would be stored after saving. This is necessary because if we checked only the checkboxes selected in the grid, then after paging or filtering, the selected checkbox may not appear anymore and thus we could not include it as a part of the saving process. Thankfully, Magento provides a solution to this problem. We need to complement our tab with a new block (widget_grid_serializer), which manages this, so we only have to make some major settings and modifications in our code:

...
<adminhtml_customercategory_customerstab>
<block type="core/text_list" name="root" output="toHtml">
<block type="aion_customercategory/adminhtml_category_edit_customer_grid" name="category.customer.tab"/>
<block type="adminhtml/widget_grid_serializer" name="category.customer.serializer">
<action method="initSerializerBlock">
<grid_block_name>category.customer.tab</grid_block_name>
<data_callback>getSelectedCustomerIds</data_callback>
<hidden_input_name>customer_ids</hidden_input_name>
<reload_param_name>customers</reload_param_name>
</action>
</block>
    </block>
</adminhtml_customercategory_customerstab>
...

It can be seen that it calls the initSerializerBlock() method, which has the following parameters:

  • grid_block_name – defines wich grid the serialization applies to
  • data_callback – defines which created method returns the already selected IDs
  • hidden_input_name – defines the name of the input (we will refer with this name to the specific html tag later in the post)
  • reload_param_name – defines the parameter that contains the already stored items

Now we need to create the getSelectedCustomerIds() function in the Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid class, which returns the identifiers set in the category:

public function getSelectedCustomerIds()
 {
 $returnArray = array();
 //Your getter logic
 return $returnArray;
 }

With this, loading has been effectuated, now the saving process should be completed with the storing of the selected identifiers. It is important that the data in the input are serialized so we need to decrypt to be able to store them. It should be carried out within CustomercategoryController saveAction():

public function saveAction()
 {
 $redirectBack = $this->getRequest()->getParam('back', false);
 if ($data = $this->getRequest()->getPost()) {
 ...
if ($customersIds = $this->getRequest()->getParam('customer_ids', null)) {
 $customersIds = Mage::helper('adminhtml/js')
->decodeGridSerializedInput($customersIds);
 //Your save logic
 }
if ($redirectBack) {
 $this->_redirect('*/*/edit', array('id' => $model->getId()));
 return;
 }
 }
 ...
}

Finally, there is one more thing needed to have a fully functional panel. This is the function with wich the user can filter the customers in the list that are already selected as well as those that are not.

Magento Admin Customer Grid Filter

This can be solved by adding a method to the Aion_Customercategory_Block_Adminhtml_Category_Edit_Customer_Grid , which we can set when adding a column, indicating what the filter function should be:

$this->addColumn('selected_customers', array(
 ...
'filter_condition_callback' => array($this, '_selectedCustomerFilter'),
 ));

I.e. filtering should be done by the _selectedCustomerFilter() in the selected_customers column:

protected function _selectedCustomerFilter($collection, $column)
 {
 $filter = $this->getRequest()->getParam('filter', null);
 $filter_data = Mage::helper('adminhtml')->prepareFilterString($filter);
 if (!$filter_data || !isset($filter_data['selected_customers'])) {
 return $this;
 }
$selectedCustomers = $this->getSelectedCustomers();
 if (!$selectedCustomers || !is_array($selectedCustomers)) {
 return $this;
 }
if ($filter_data['selected_customers'] == '1') {
 $operator = 'IN';
 } else {
 $operator = 'NOT IN';
 }
 $collection->getSelect()->where('e.entity_id ' . $operator . ' (' . implode(',', $selectedCustomers) . ')');
return $this;
 }

 

Summary

This example has shown us that we can easily satisfy such client needs when a new menu, a new form, a new grid or dynamic tabs should be created. We can also use existing collections or custom database models. Magento provides a tremendous amount of help throughout the process, it holds many core classes that we only need to either implement or complement. If you think this article can be useful to other readers as well, please share it. You can also write your comments below. If you have any queries, please don’t hesitate to comment.

 

Why Magento is The Best eCommerce Platform for Enterprise Level Companies?

An excellent software, beyond making it possible to customize the page design according to your expectations, should also offer the options of configure your online “shop window”, search features as well as all the processes in detail.

Otherwise it could not comply with various requirements, even within one single organization because these needs may change considerably over time.

Therefore it is really something if a particular ecommerce system is regarded by firms of different sizes and experts from different industries as best choice. Well, Magento Commerce happens to be like that.

Its major characteristics are expandability, scalability, and flexibility.

We are talking about such an ecommerce platform that truly features a framework system that offers you the freedom to shape it completely according to your own needs ― on condition that you are willing to assign an expert to do those tasks that seem rather complicated for you.

We now show you why this ecommerce platform is the ideal choice and to whom it can generate the most benefits.

 

why magento - advantages

 

“Everyone uses it”

 

Yes, we know that this argument may lead to false conclusions. It well may be that something is agreed on by the majority while it is not necessarily the best option.

But now we are talking about ecommerce and business results.

Magento is the second most popular ecommerce platform: it has a market share of 17%, among the Top 100k sites (Magento CE and Magento Enterprise combined, July 2017).

 

tips Global presence: But it is not only “mass” online stores that prefer Magento… Magento is also used by global brands like Ford, Samsung, or Olympus. These companies were definitely not looking for the cheapest or simplest solutions for their ecommerce systems. When a platform is favoured by such big names, it is a guarantee for a secure commercial system, which is capable of selling your products effectively online.

 

Professional technological support and huge knowledge base

 

The IT developers of Magento eCommerce, thanks to the help of volunteers, have been able to create a platform that truly serves the unique needs of users.

The flexibility and openness of the system is basically the result of this philosophy. A couple of years ago Magento was acquired by eBay.

This did not come as a disadvantage: it stayed to be an open source platform, while it also enjoyed the massive amount of expertise of the huge ecommerce company.

Magento Commerce then got independent of eBay, but its extremely strong and loyal developer community, which had evolved well before its acquisition by eBay, is still passionately working on its development.

See more on Magento Commerce’s history here: Magento Commerce: History and Features of The Most Popular eCommerce Platform

 

The Leatherman of ecommerce

The system has a remarkably rich administrative area, which gives you the possibility to modify everything according to your needs – such as product features and categories or content targeted at different customer groups. Magento is capable of handling many things, it is a robust system.

 

magento multi functions ecommerce

 

Although sometimes it is simple to deal with, in other cases it can be quite complicated. For example, you can set that different customer groups (say, retailers and wholesalers) see different prices on the pages of your online store. This kind of customization is one of the greatest advantages of Magento.

You can create an online store that not only shows the products to the shoppers, but can also direct them towards conversion.

The discount system has great features with which you can learn how to manage the discount offers even without the help of a programmer. Should you have special requirements, experts can develop whatever you think of since the software is open-source.

It is also possible to organize the categories into a tree structure and with the help of extensions you can make the process even more effortless, like using drag&drop actions.

 

Huge array of product information

 

It is no surprise that Magento is so popular: it is the most widely used ecommerce platform (according to 2015 statistics) in the world with a nearly 30% market share. Serving such a big chunk of the market successfully would be impossible without a massive amount of flexibility.

You can define basically any product feature you like.

You can set and manage the attributes without compromise in the admin panel. Basically, you can adjust the entire system to your products.

The discount system is equally flexible. For instance, you can give a discount (e.g. a percentage of the price or free shipping) to a particular shopper if he or she puts two products from the same category into the shopping cart. You can define a whole bunch of combinations, you have loads of opportunities to do whatever you like.

 

Impressive, responsive design

 

Needless to say that you can tailor the look of your Magento store according to your expectations. It simply could not be any other way, since a standard template would not add very much to your brand value.

It is not a problem if you are not an expert in web design, for Magento offers thousands of freely customizable templates. Installation, however, is recommended to be performed by a specialist.

Responsive design is even more important. There are many solutions on the market that have “forgotten” to adapt to the most recent market needs. Since 2014, more and more people use the internet on their tablets and smartphones, and an increasing proportion of purchases are done on mobile devices.

An online store can compete in such an environment successfully if it has a responsive layout, which means that it offers an interface to its shoppers, which is easy to handle on all platforms from tablets and smartphones to PCs and netbooks.

 

tips Tip: Responsiveness is also an advantage in terms of SEO. Google’s algorithm analyses each website if it is mobile-friendly or not and gives a lot of weight to this issue when showing search results. So if you have an online store which looks fantastic on desktop, but “falls apart” on a mobile device, you can say goodbye to your potential shoppers wishing to buy from you on their mobiles.

 

Magento eCommerce CMS platform

 

Supports SEO

 

Magento features a range of default options to create an SEO-friendly website. You can generate SEO-friendly URLs and sitemaps (e.g. yourdomainname.com/productname), can define the meta data and so on. Searches within your e-store is also crucial, which is also a major strength of Magento.

Customers make multiple, narrow-down searches, which is good for user experience, but less beneficial for SEO because this makes the system generate new product lists or new pages after the searches have been made, but can be regarded as duplicate content by Google.

This drawback can be handled by custom development. The developers of Magento have always kept in mind that Magento websites should be easily found and stay strong in terms of SEO.

 

tips Tip: It probably has the most advanced SEO toolkit among ecommerce platforms, but technical SEO settings, like Robots.txt. should be applied as well. Here you can exclude duplicate pages and irrelevant pages (shopping cart, user account etc.) from Google searches, which can be very valuable.

 

Magento is secure

 

Safety is not an option, it is a must in an environment where hackers try to crack databases every minute. We hear news almost every day mentioning data breaches, so in case of an ecommerce store it is essential to have a hack-proof system.

Luckily, Magento is always tested by a vast number of developers.

The whole system is constantly watched by experts (e.g. ethical hackers) searching for defects and eliminating vulnerability by uploading patches.

But don’t worry, you don’t have to hunt down updates all the time. Magento always calls your attention when an update is available so you will know about fresh developments right away. (You had better ask an expert to carry out the update process, especially when you already have some custom developments in your store.)

All in all, the system is not perfect. No system is. But with Magento it is just amazing how fast its dedicated community can reveal the errors and after making the safety updates, you can avoid any future problems before they can kick in.

 

Data!

 

Even if you only have a little experience in online marketing, you very likely know that this business is based on data.

No matter what kind of a website you run, if you want to earn money with it, you need to build databases and after examining them, draw the appropriate conclusions.

You have to know how many visitors you have, how they find you, how much time they spend on your pages etc.

There are a lot of factors you should keep an eye on so that you can optimize your website according to user behaviour and preferences.

You can integrate one of the most useful data collection systems, Google Universal Analytics, into your Magento website.

An even better solution can be implementing Enhanced Ecommerce, specially developed for ecommerce systems. Applying this extension for Analytics needs some programming, but it provides much more detailed data.

Thus you can collect a huge amount of useful data about your shoppers, such as what they like or how they behave when browsing in your store. You also have to watch and control the system. You should know how your website performs, how it works, what may cause failures.

Thanks to the tools of Magento support, you will receive comprehensive reports from which experts can easily find the reasons for malfunctioning.

If you have the possibility, it is worth to request an automated testing system service from your developer partner, which may raise problem solving to a whole new level.

 

Complete ecommerce toolkit

 

  • Shopping Cart: Magento can comfortably lead the customers through the whole shopping process. It can be either used by registered users or guests and can also offer multiple payment options.
  • User Account: Shoppers can quickly access their purchase history, check their orders or save products that they want to buy later. In addition, they can store information so that next time they can check out faster. Such information can be delivery address or invoicing address which the users can switch with only one click if they wish to.
  • Management: You can manage transactions in the admin panel, start and complete the shipping process or resolve complaints. A top list can be downloaded about the most searched products (exact terms of what your customers typed in the search field), about the most popular products, or you can see if a particular shopper put some items in his or her shopping cart, but did not order them.
  • Product Management: You can import and export thousands of products and modify their attributes at the same time, you can upload images, set custom pricing conditions, and determine what to show and how to show them in your “shop window”.
  • Marketing: You can run promotions, create unique sales deals, offer free shipping or other special bargains. You also have opportunities for up-selling and cross-selling, you can preset opinions about or comparisons between products, display lists of recently viewed items, and also make it possible for shoppers to share your product pages easily and quickly with each other on social networking sites.
  • Multiple websites: Magento is capable of handling more, an almost unlimited number (!) of websites simultaneously. You can run multiple iterations at the same time with only one user account so managing all these is much simpler. What does it mean more precisely? Say, you have a central online store with a dozen brands. You also have separate e-stores for each brand, all independent from the main website, where you can define different prices and discount offers―and you can manage all this in one single admin area.

 

What about Magento 2?

 

Is Magento 2 so much better than Magento 1? Is it worth migrating over to Magento 2 if you have a Magento 1 store?

We have covered these questions in our article Magento 1 vs. Magento 2.

To give you a short answer, we can tell you that Magento 2

  • provides better performance and speed
  • offers more advanced user experience
  • more features and possibilities
  • has a simpler checkout process
  • has a more advanced search engine (Elasticsearch)
  • makes extension installations and updates easier
  • has an advanced dashboard
  • offers a simpler product upload process
  • is compatible with more technologies (e.g. HTML5, CSS3)

 

magento 2 admin panel

 

In short: Magento has got even better!

 

However, at the moment, you have to pay a premium for that. It’s because Magento 2 development takes more time and also many developers are just catching up with Magento 2 development challenges. We have elaborated a separate post about Magento pricing for 2017, which you’ll surely find useful.

 

tips Try Magento Demo Stores:

Check Magento 1 Demo Store here

Check Magento 2 Demo Store here

Access data:

User name: admin

Password: admin123456

 

What are your options?

 

At the moment, two versions of Magento are available: Magento Open Source (formerly branded as Community Edition) and Magento Commerce (formerly branded as Enterprise Edition).

Magento Opnen Source features two important characteristics of modern online platforms: free and open-source.

 

why magento commerce

 

 

With Open Source you will not get dedicated technical support, but you can always count on the community as a lot of beginners and experts talk about Magento on numerous forums.

If you are considering implementing a more sophisticated enterprise online store, then Magento Commerce (Enterprise Cloud Edition) can be the one for you. It is a system that is capable of managing vast websites efficiently with advanced technical solutions so that users can always have a positive experience.

In spite of the robustness and complexity, Magento provides you with a fast and very reliable platform.

Speed is a great advantage of both editions. In the case of Magento Open Source, you can enhance speed with custom developments and thus improve user experience further.

Your options are manifold. You can browse among lots of third-party extensions and services, many of which are free, to broaden the potential of your system.

 

Why Magento Commerce (Enterprise Cloud Edition) may be the best choice in 2017 for your online business?

 

Magento Enterprise Cloud Edition was introduced in April 2016 as a Platform-as-a-Service (PaaS), based on Magento 2.

It is basically Magento Enterprise Edition running on Amazon Web Services (AWS), which guarantees merchants advanced hosting services for better performance and security.

Magento Commerce is ideal for larger online stores that

  • have a lot of products (thousands or tens of thousands)
  • manage large databases
  • need to integrate external systems (e.g. invoicing, CRM, shipping)
  • need smart and fast scalability
  • need high availability globally
  • need automated patching (security updates)
  • want to guarantee fast page speed to their users

 

Mark Lavelle, CEO of Magento, summarizes the main benefits as follows:

"Magento Enterprise Cloud Edition defines the post-SaaS commerce platform era. It’s a first-of-its-kind in-market solution and a major differentiator in the broader Magento product portfolio.

With this new platform running on AWS, we ensure that our merchants have the agility to respond to a rapidly changing environment, can continuously deploy innovations, easily scale to meet unexpected demand and don’t have to worry about the day-to-day management of infrastructure."Mark Lavelle

 

All in all, it is a flexible open source, fully customizable system that is capable of managing even the largest, most complex ecommerce websites efficiently with advanced technical solutions so that users can always have a positive experience.

Magento Enterprise Cloud Edition offers a fine combination of modern cloud computing and Magento’s rich heritage and unmatched experience in the ecommerce industry.

 

Summary

 

Magento is not tailored to your needs. It is a system that you can tailor to make it perfect for you. It has a great array of functions, which is vital in today’s ecommerce environment.

It is also an extremely flexible framework system in which you can do basically anything to get the maximum out of your online store.

This is the very reason it is worth a try.

Once you start using it, you will soon realize that you have access to such a huge pool of possibilities that you will not find in any other ecommerce platform.

It is true, however, that you will need to pay development costs to have this kind of richness.

On the other hand, for a business which has only some vague ideas about its future, not even sure about what it would sell exactly on the internet, it would be better to choose a simpler and free solution, such as WooCommerce.

You need to understand that implementing and running Magento cost a considerable amount of capital which is worth investing only when you pursue a voluminous ecommerce activity.

Magento best suits those who have a clear objective and know precisely what and how they want to sell.

For example, they already have brick and mortar stores, have a lot of experience in selling, not like the ones that are just starting and it is still a question if they will have customers at all.

An online store’s success is determined by what kind of user experience is offered to its visitors. This experience is provided in the way how the system responds to the activity of the user.

 

 

You can influence it greatly if you optimize and personalize the website. The more personal the offer and the more convenient the shopping process, the better the shopper opinion in the end.

Magento has the most features among ecommerce systems which can serve that kind of optimization with its functions, extensions and custom developments.

 

What to keep in mind?

 

Let us give you some hints for reaching a higher level of user experience. It is not our own random list, it was outlined by Forbes with the help of ecommerce veterans.

  • What is the first impression of the visitor after landing on the page?
  • What does the user see when he/she starts typing in the search field?
  • What kind of email is sent to the user after registering, after changing the password, after signing up or ordering?
  • How do you thank the user for his/her registration, purchase, sign-up?
  • What happens when a user lands on a product page where the product is temporarily not available for purchase?
  • What happens if the user searches for a product which does not exist?
  • What does the user see if he/she has given an invalid email address during the registration process?
  • How much information does the user have to provide to be able to close the purchase?
  • In what ways does your store earn the trust of your shoppers?
  • How do you let the customer know that you will be able to deliver on time?

When you start using Magento, make sure you thoroughly think these issues over. Your possibilities are rich enough to guarantee that your visitors will gain a pleasant user experience.

 

… 3, 2, 1, Launched!

We proudly announce that AionHill has started its new mission. After outlining the strategic direction, training and expanding our troops, we are ready to stand up to the challenges with full force. Our goal is crystal clear: to give the best to our clients. We’re dauntless.

After years of hard work we can proudly say:

in Magento nothing is impossible for us!

The core of our army is made up of our Magento developer team. We don’t employ mercenaries, don’t outsource work, everything is done on our own board. This means maximum efficiency and safety. Not only to us but to our clients as well. In order to lead Magento projects to success, we provide a full circle of services.

Services From hosting solutions and code audit to page speed optimization, we cover every aspect necessary to the flawless development and operation of an online store. Our rescue team embraces the projects in crisis and leads them to victory. We leave no one behind. Thanks to our support service, our ecommerce partners can always be sure that if they release an emergency call, we will fight down the blockages. We’re constantly watching the field.

We follow the ecommerce trends and developments and as everything is in constant motion, we won’t stop moving either. We adapt, learn, and also develop breakthrough solutions. We inform and advise. We write regularly in our ecommerce blog about topics that give useful information and tips to the entire Magento community.

We will publish about methods of increasing conversion, Magento development, online marketing, ecommerce store design, user experience, ecommerce innovations and a lot of other valuable content. It would be a lie if we said we know perfectly well what our future holds. But we definitely see that we have set off in the right direction.