Magento 1 Categories, with and without Flat Tables

Sometimes Magento leaves you wondering, “WHY!?!?!?!?!?”

 

If flat categories are enabled, and you want all the children of a parent category, do this:

$children = Mage::getModel('catalog/category')->load($theCategoryIdHere)->getChildrenCategories();

If they are not enabled, do this:

$children = Mage::getModel('catalog/category')->getCategories($theCategoryIdHere);

How do you know if they are enabled?

if (Mage::helper('catalog/category_flat')->isEnabled()) {}

Also, getChildrenCategories() on the flat category list returns the category objects in the same order you find them in the admin category manager. However,

Mage::getModel('catalog/category')->load($theCategoryIdHere)->getChildren();

returns a comma separated string of child category ids in numerical order.

Magento 1 Data Upgrade Script: Set All Simple Products to Manage Stock

<?php
/* @var $installer Mage_Core_Model_Resource_Setup */
$installer = $this;
$installer->startSetup();

$collection = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToSelect('*')
    ->addAttributeToFilter('type_id', 'simple');

foreach ($collection as $product) {
    $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product->getId());
    $stockItem->setData('manage_stock', 1);
    $stockItem->setData('use_config_manage_stock', 1);
    $stockItem->save();
}

$installer->endSetup();

?>

php-fpm, nginx, bad hoodoo magic, and the dreaded 502 gateway error on magento sites

When segmentation faults collide ….

If anything disrupts nginx’s ability to talk to php-fpm, you’ll see the 502 gateway error. Sometimes, that’s because the server guys did something. Sometimes, that’s because the dev guys did something. Sometimes, it’s really, really, really hard to tell.

If your host assures you it’s your code, and they are positive they have not updated anything on the server (php version, nginx version, anything at all), and more importantly, if the 502 gateway is intermittent and always worse under load, you need to consider your code. It may function, but function so poorly it kills the server.

On Magento sites, look for long running queries. You can stack trace by enabling the zend logger in lib/Varien/Db/Adapter/Pdo/Mysql.php.

One of the biggest coding mistakes in Magento customizations is inefficient queries. Running SQL queries inside a loop, for example, can really put a drain on your server. A long running query can consume so much memory it crashes php-fpm which times out the response from nginx and gives you a 502 gateway error, even though your server is running just fine as far as you can see.

Running SQL queries is a very expensive operation, and doing it in a loop tends to make it even worse. Instead of doing that we should use data collections to load models and then process the items in the collection.

Instead of:

foreach ($this->getProductIds() as $productId){
    $product = Mage::getModel('catalog/product')->load($productId);
    $this->processProduct($product);
}

Do this:

$collection = Mage:getResourceModel('catalog/product_collection')
    ->addFieldsToFilter('entity_id', array($this->getProductIds()))
    ->addAttributeToSelect(array('name'));

foreach ($collection as $product){
    $this->processProduct($product);
}

Especially be on the lookout for queries run through an adapter. Method fetchAll() used to fetch and iterate over larger result sets will lead to a very long execution time (again, with the memory thing and the time out thing and the 502 gateway thing). The better solution is to fetch the results row by row using the fetch() method.

Assuming you declared/initialized your adapter already, instead of this:

$rowSet = $adapter->fetchAll($select);
foreach ($rowSet as $row) {
    //process row
}

Do this:

$query = $adapter()->query($select);

while ($row = $query->fetch()) {
    //process row
}

Q: A custom frontend controller will extend which one of the following classes?

All Action Controller need Mage_Core_Controller_Front_Action as an ancestor.

The best example of this in action is a simple HelloWorld module that is configured with routes and has a Create Action Controller for the routes.

Directory Structure:

app/code/local/nodwell/Helloworld/Block
app/code/local/nodwell/Helloworld/controllers
app/code/local/nodwell/Helloworld/etc
app/code/local/nodwell/Helloworld/Helper
app/code/local/nodwell/Helloworld/Model
app/code/local/nodwell/Helloworld/sql

We need a configuration file (PATH: app/code/local/nodwell/Helloworld/etc/config.xml):


<config>    
    <modules>
        <nodwell_Helloworld>
            <version>1.0.0</version>
        </nodwell_Helloworld>
    </modules>
</config>

and a modules file to activate our module (PATH: app/etc/modules/nodwell_Helloworld.xml):


<config>
    <modules>
        <nodwell_Helloworld>
            <active>true</active>
            <codePool>local</codePool>
        </nodwell_Helloworld>
    </modules>
</config>

Now, the module exists, and we can begin to add the code to make it do stuff. We need to configure a route in the config.xml. The route turns a URL into an Action Controller and a method. In our case, it will act on URLs that start with /helloworld, as in http://magento.nodwell.net/helloworld/*. So, add a frontend routers section to the config.xml:


<config>    
    <modules>
        <nodwell_Helloworld>
            <version>1.0.0</version>
        </nodwell_Helloworld>
    </modules>
    <frontend>
        <routers>
            <helloworld>
                <use>standard</use>
                <args>
                    <module>nodwell_Helloworld</module>
                    <frontName>helloworld</frontName>
                </args>
            </helloworld>
        </routers>  
    </frontend>
</config>
 

Now, we create the actual Action Controller (PATH: app/code/local/nodwell/Helloworld/controllers/IndexController.php):

class nodwell_Helloworld_IndexController extends Mage_Core_Controller_Front_Action {        
    public function indexAction() {
        echo 'Hello Index!';
    }
}

Code Generation in Magento 2

When and Why Does Code Get Created in /var/generation?

Overview

Code generation can be triggered in two ways:

1. On the fly – when the system tries to autoload a class, if it doesn’t find it, it generates it. (slower)

delete the MAGENTO_ROOT/var/generation directory

2. Command-line – process goes through the system, inspects the code, and generates the necessary classes it might need. (speedier)

run the magento setup:di:compile command from terminal on web server

Code Generation did not exist in Magento 1. In Magento 2, code generation is deployed to support a number of core concepts.

The system generates several class types – the 3 most important are Factories, Proxies, and Plugins.

Factories

Factories are used to instantiate objects that cannot be injected automatically, objects that it doesn’t make sense to create with a generic mechanism, such as those which contain data from the database.

  1. Developer declares a dependency on factory in constructor ($productFactory)
  2. Object manager injects this dependency
  3. Developer can access create() method (the only method in factory object) to create as many Product instances as he wants.

The purpose of factories is to delegate object instantiation to object managers.

Proxies

Magento 2 uses the object manager to create all the dependencies and allows only one type of dependency injection: constructor injection. Thus, you cannot instantiate an object without passing all the dependencies as they are required. Proxies allow you to pass in optional dependencies.

Developer never touches PHP files to use proxy, only inside the di.xml

Whenever any proxy method is called for the first time, the original instance gets created with all its dependencies. The purpose of a proxy is to delay creation of an instance (and its dependencies) until the very first usage.

Plugins (Interceptors)

Plugins are the primary customization mechanisms for Magento 2 which replace class rewrites. Plugins allow you to hook in and do something before, after or around any public method of the application.

  1. Developer writes a plugin class depending on the requirements
  2. Developer registers a plugin in di.xml

The system generates the interceptor class. The generator tool will generate only the methods that you rewrite.