The Top 10 Magento 2 Concepts You Need to Know

Are you a Magento developer who has yet to make the dive into Magento 2? Have no fear, because, by the end of this article, you’re sure to be a bonafide Magento 2 expert!

Well … perhaps not. Learning Magento is a hefty task, after all, and this remains true for version 2 of the platform. A few hundred words aren’t really going to get you up and running, obviously. However, if you’re like me, not knowing where to start may be the single most intimidating thing about diving in. So the real objective of this article is to give you an overview of the most significant differences from Magento 1 that you’ll encounter.

As you get your feet wet in the new platform, you may be pleasantly surprised to discover how similar the line-by-line code you’ll write really is. So a little insight into a few major architectural concepts will go a long way toward smoothing your transition to Magento 2 guru.

And before we really get started, if you don’t know already, Magento 2 has a growing collection of documentation topics for getting into the finer details.

Module Structure

If you’re not yet familiar with Composer, the resoundingly popular PHP dependency manager, now is the time to become so, since the software is integral to the structure of Magento 2. Instead of a single core codebase, Magento is now a collection of individual components installed via Composer. This applies to third-party libraries and Magento core modules/themes (i.e., packages) alike. Package dependencies have been formalized such that any particular “version” of Magento can really be expressed in terms of the versions of these components and their dependencies.

If you’re a developer of distributable Magento extensions, you’ll be creating Composer packages as well. On a broader note, though, the kind of encapsulation required for such self-contained components is facilitated by a change in Magento’s structure that is sure to make life easier whether your code is a marketplace extension or just a local customization: All files related to a module now reside in a single directory. Whereas in Magento 1 PHP files, layout XML, templates and static assets were scattered throughout different locations, now all files for a single feature live together. A module’s top-level directory contains the familiar Block, Model, etc. sub-directories (or really any sub-directories in which you wish to place your classes), along with a view sub-directory. Within view/adminhtml or view/frontend are layout and template directories, and a web directory that contains the module’s static assets like JavaScript and CSS.

The pub Directory and Code Generation

The concept of static asset files living in numerous locations, and the questions this raises, are the perfect segue into the next important topic. Automated code generation and deployment play a huge role in Magento 2, and these can be partially understood with a look at the pub directory. While it’s possible to use your Magento installation’s root directory as your web root in development environments, the required web root in production is the new pub directory that lives apart from all application code and contains only what Magento puts there (including the main application entry point, media, and static view files). Since site code now lives outside this designated web root, access control is more rigid and predictable, and the new deployment/compilation process makes the previously mentioned module encapsulation possible:

  • Static view file deployment materializes all necessary static assets from their source in application code into the web-accessible pub directory. This deployment process happens automatically in development mode but requires an explicit action in production. This automatic deployment of assets is what allows all JS/CSS code to live in its appropriate module but still be web-accessible in a centralized location.
  • Code generation doesn’t involve the pub directory, but it’s closely related to the concept of deployment. Certain types of PHP classes are now automatically generated by Magento and facilitate more granular and unobtrusive customization than has been possible in the past.

Dependency Injection

Dependency injection in Magento is a huge topic, and so I’ll cover it only in the broadest terms here. Magento 1 had its factory methods that facilitated class overrides, like Mage::getModel and Mage::helper. This is replaced in Magento 2 by a more structured approach whereby all external types are injected as parameters in a class’s constructor as follows:

Example.php

class Example
{
	protected $request;
	protected $customerAccountManagement;
	public function __construct(
		MagentoFrameworkAppRequestHttp $request,
		MagentoCustomerApiAccountManagementInterface $customerAccountManagement
	) {
		$this->request = $request;
		$this->customerAccountManagement = $customerAccountManagement;
	}
}

Types referenced this way are automatically instantiated and passed in by Magento’s Object Manager. This facilitates the use of real class names and PHP namespaces instead of littering code with proprietary factory methods, it makes for more organized code by centralizing references to dependencies, and it allows a more flexible approach to overriding classes. Notice in the code above that one dependency references only an interface, not a full class. Interfaces are a big point of emphasis in Magento 2, and dependency preferences defined in a special config file – di.xml – specify the best default implementation of such an interface. This DI configuration is what third-party modules use to change the preference for a default interface or full class, either globally (the equivalent of Magento 1’s class rewrites) or for specific contexts (made possible by the centralization in constructors). External classes obtained via constructor injection are shared instances (i.e., a “singleton” pattern) and are called “injectables.”

Plug-ins

As previously mentioned, Magento 1 class rewrites find their analog in the ability to specify dependency injection preferences. I also mentioned that more unobtrusive techniques for customization are made possible thanks to M2’s code generation, and plug-ins are the most significant example of this. Plug-ins allow you to alter or extend targeted methods instead of needing to replace entire classes.

Like preferences, plug-ins are defined in di.xml files, and they have specific “before,” “after” and “around” implementations, respectively allowing you to alter the input of an original method, operate further on its output, or surround it with your own logic. Best of all, multiple plug-ins can be defined on a single method, vastly improving compatibility between extensions that operate on the same area of the system. M2’s code generation and the Object Manager take care of running method calls through the right paces and handing them off to plug-ins as necessary. Below is a native example of the definition and implementation of a plug-in:

di.xml

<type name="MagentoStoreModelResourceModelGroup">
    <plugin name="categoryStoreGroupAroundSave" type="MagentoCatalogModelIndexerCategoryProductPluginStoreGroup">
</type>


---

StoreGroup.php

class StoreGroup
{
    public function aroundSave(
        MagentoFrameworkModelResourceModelDbAbstractDb $subject,
        Closure $proceed,
        MagentoFrameworkModelAbstractModel $group
    ) {
        $needInvalidating = $this->validate($group);
        $objectResource = $proceed($group);
        if ($needInvalidating) {
            $this->indexerRegistry->get(MagentoCatalogModelIndexerCategoryProduct::INDEXER_ID)->invalidate();
        }

        return $objectResource;
    }
}

Factories

On the subject of dependency injection, you may have noticed a significant omission. “Injectables,” as shared instance objects, are really the equivalent of M1’s Mage::getSingleton factory technique. So where is the equivalent of the Mage::getModel factory method for obtaining unique object instances (that is, “non-injectables” in M2 parlance)? Such objects are instantiated using injectable classes that are dedicated to the purpose: factories. Observe the following use of a factory class to instantiate a model:

Example.php

class Example
{
    protected $customerFactory;
    public function __construct(
        MagentoCustomerModelCustomerFactory $customerFactory
    )
    {
        $this->customerFactory = $customerFactory;
    }
    
    public function getCustomer($email)
    {
        return $this->customerFactory->create()->loadByEmail($email);
    }
}

Your first reaction to the above might be to think that defining and maintaining one class just to handle instantiating another seems cumbersome. In this respect, the benefits of M2’s code generation come to the rescue again, because factory classes actually don’t need to be defined at all. They are automatically created by Magento whenever a reference to such a factory is encountered. The class-specific approach means that you can explicitly define them, however, thereby handily customizing the factory technique for any particular class if necessary.

The Service Layer

Like dependency injection, the service layer is worth a whole series of articles on its own (see the core documentation on service contracts and design patterns), but the basic concept can be grasped with a little explanation. Consider that one of the major challenges for customization in Magento 1 was that code almost certainly relied on referencing specific implementation details of core Magento classes and that major changes to this core code (such as alterations of method parameters or logic) could render custom code inoperable. The M2 service layer alleviates this problem by introducing a layer in between the main business logic of a module and any external code referencing that module’s components. “Service contracts” take the form of interfaces that rigidly define entities and the prescribed ways for external code to interact with them; these interfaces and the classes that implement them from the service layer.

As an example, let’s consider the core Customer entity. MagentoCustomerModelCustomer is the model for a customer entity, and this class has corresponding resource and collection models that serve the same purpose as their M1 counterparts. Rather than reference these classes directly, however, external modules should utilize a sort of traffic controller class called MagentoCustomerModelResourceModelCustomerRepository to obtain and manipulate instances of MagentoCustomerModelDataCustomer. Both classes implement service contract interfaces, and while they use the typical data and collection models under the hood, the methods exposed in the interfaces make this implementation invisible and explicitly define the details external code can rely on. The logic of models and collections may change to any degree in the future, but the details of the service contracts will remain the same, preserving the compatibility of custom modules.

Service contract interfaces are located in the API sub-directory of a module. If you’re producing distributable code, you are strongly advised to create service contracts and classes for your own entities to maintain future compatibility with others’ code.

Page Cache

In Magento 1, the Full Page Cache was a performance enhancing feature available exclusively in Enterprise, and accommodating it in your custom developed features was not exactly the most trivial development topic. In M2, full page caching is a feature of both Community and Enterprise, living in the module Magento_PageCache, and its implications on developers are far more streamlined.

The central challenge of full page caching is those areas of a web page whose content cannot be universal for all visitors and page views. M1 focused on assembling content for such areas as part of the rendering of final HTML output, while M2 takes a different – and increasingly popular – approach. Firstly, page blocks that should be de-coupled from the main cache content are more solidly defined as “private data”: content that is dependent on a visitor’s session information. Secondly, such blocks are anonymized for all visitors in the initial rendering of the page, with the private data instead loaded via an Ajax request after the main page content is delivered. Thus, the main body of a page is cached and delivered as swiftly as possible, with less essential and visitor-specific content materializing asynchronously.

In terms of the implications for developers, accommodating the page cache is almost insanely simple as compared with M1. Marking a block as private data is as simple as setting a single property, and Magento takes care of the rest:

ExampleBlock.php

class ExampleBlock
{
    public function __construct()
    {
        parent::__construct();
        $this->_isScopePrivate = true;
    }
}

CSS Compilation

Since the responsive overhaul to its theme, CSS compilation in the form of SASS/Compass has been a part of Magento 1. The tool of choice in Magento 2 is LESS, which brings much the same capability, including CSS variables, mix-ins for repeatable styles, multi-file organization with includes, and much more.

Covering the specifics of LESS in M2 is outside the scope of this article, but an important concept to grasp is how the M2 developer deals with CSS compilation (or, to be more accurate, does not deal with it). In M1’s responsive theme, compilation was a process completely invisible to the Magento application; the developer was responsible for compiling source code into final CSS files that were then explicitly included via layout as has always been done. In M2, the CSS compilation magic is done at the application level as part of the aforementioned static file deployment process. Simply make sure LESS source files are included in the appropriate locations, and Magento will take care of the rest. (It’s worth noting that it’s still advantageous to handle compilation differently during development, but the point is that the process is heavily integrated into the application core.)

Any underscore-prefixed source files placed in the view/{area}/web/css/source directory of a module or the web/css/source directory of a theme will be compiled into the main styles, with only a single instance of a unique name being loaded site-wide, depending upon a priority order (thus allowing for override of specific partials). On top of this, the magically named _module.less and _widgets.less (for modules) and _extend.less (for themes) allow styles to be cumulative instead of directly overriding the identically named partials from other sources.

JavaScript Inclusion and RequireJS

Magento 2 has overhauled its technique for including JavaScript from a traditional approach of explicitly requiring each JS file with tags to an “include-as-needed” method primarily utilizing RequireJS. (See this page for info on RequireJS in Magento.) RequireJS is a robust tool with its own documentation, but the important thing to understand is that it allows for encapsulating JavaScript within enclosures that declare dependencies, which RequireJS then manages the loading of. This ensures that JavaScript components get loaded only when needed, and in the necessary order.

The process could almost be considered a JS version of dependency injection, and there are several ways to do it in Magento. Here’s a simple example, which requires the component “mage/url” as a dependency of the code within the enclosure, passing in the class returned by that file as the parameter of the function:

example.phtml

<script>
    require([
        'mage/url'
    ], function(url) {
        return url.setBaseUrl('http://www.mydifferentdomain.com');
    })
</script>

The other two ways introduced by Magento deal with immediately scoping calls for jQuery plug-ins to specific selectors and can be studied on the Magento documentation page. The precise definition for the location of named components is set up in a per-module file called requirejs-config.js, and like dependency injection, this configuration allows for easy extension and override of components by superseding their definition with your own.

JavaScript Components

A JavaScript-heavy developer will be well advised to become familiar not only with the techniques for including JS code, but with some of the powerfully systematized approaches Magento takes to its own JavaScript as well. Firstly, it’s worth noting that M2 makes heavy use of jQuery widgets, both from the jQuery UI library and its own customized widgets. When you’re developing JS-heavy interfaces, it will be well worth the effort to utilize this pattern in your own code as well.

A more complex topic is the JS class uiComponent (distinct from M2’s UI Components library) and the way layout, JS and static templates can work together. In this paradigm, JS components that extend uiComponent can be initialized, and configuration passed to them, via layout XML. Among this configuration can be the definition of a static template path used to render the component, which is loaded via Ajax without the involvement of PHP, and which contains Knockout syntax to dynamically access the properties and methods exposed by the JS component. Getting a handle on this particular pattern will take some study of the Magento core usage and the capabilities of Knockout, but here is the barest snippet of an example:

default.xml

<item name="minicart_content" xsi_type="array">
    <item name="component" xsi_type="string">Magento_Checkout/js/view/minicart</item>
    <item name="config" xsi_type="array">
        <item name="template" xsi_type="string">Magento_Checkout/minicart/content</item>
    </item>
</item>

How the above fits into the layout structure and how its parent block initializes such components is beyond what I can cover in such limited space. However, know that the above defines the path to the JS file that represents this component (minicart.js, containing a class that extends uiComponent) and a static template file loaded straight from the server (content.html), which contains Knockout code referencing logic from minicart.js.

There are a plethora of other smaller architectural changes in Magento 2 that could be mentioned, and you’ll become well familiar with them as your small ventures into the new platform become larger ones. For the developer looking for a place to start, however, the above topics represent some of the best ways you can spend your Magento 2 training time. Start to get a handle on these concepts, and you’re sure to start seeing M2 as a warm friend rather than an ominous stranger.

Now Available on Magento Connect: AddShoppers

Classy Llama always looks for the best way increase sharing and track social ROI on Magento sites. That’s why we’ve partnered with AddShoppers to provide a hassle-free way to add the AddShoppers social sharing and analytics platform to your site quickly and easily.

AddShoppers is an open sharing platform built for social commerce. They increase sharing by rewarding and incentivizing social actions. Meanwhile, their social analytics measure ROI and provide deep insights, for free. Retailers can identify their most social products and influencers (and sort by Klout!). For example, a retailer can see a Facebook “Like” drove 8 clicks and two of those people ordered for a total of $125.49.

All you need to do to get started is:

Get your free account at AddShoppers.
Install the AddShoppers Extension from Magento Connect.
Enable the module and customize as desired.
For more information, you can connect with AddShoppers via the web at AddShoppers.com or on twitter at @addshoppers.

Optimizing Your E-Commerce Conversion Rate: Analyzing and Improving Traffic Quality

Variable #1:  Traffic Quality

If you sell pencil sharpeners, and most of your traffic is redirected from a Youtube video that shows a guy balancing a pencil sharpener on his nose, it’s not very likely that the visitors to your site are interested in purchasing pencil sharpeners.  Your bounce rate will likely be sky-high.  I recently saw a massive increase in traffic to one of our client’s sites, increasing their monthly traffic volume by around 30%, but they only made one sale from all of the traffic.  It was a major design gallery hit that caused the traffic.  People were going to the site to look at the design, not buy stuff, so of course, the conversion rate for that traffic source was 0%.  In these cases, the traffic quality would greatly decrease the conversion outcome, despite the quality of your Value Presentation and the Ease of Purchase.  The Value Presentation and Ease of Purchase variables would have to pick up a lot of slack to absorb the poor traffic quality and generate conversions.

If on the other hand, your traffic quality is very high, it can absorb weakness in your Value Presentation and Ease of Purchase.  Specialty stores are a great example of this.  They offer a very narrow product that you can only get from them.  Their site can have a really bad design and no marketing continuity or promotional sensitivity, and it can be really difficult to purchase the product, but they still convert visits to purchases because the traffic to their site is looking for what they’re offering.  This situation can be broken down via a few Value/Cost-based criteria:

1.  Perceive they immediately need/want what you’re offering

Many of the people who arrive on a specialty site will most likely perceive that they need/want what is offered.  Otherwise, they wouldn’t have ended up on the site.  They either searched for the specific specialty item on a search engine or were referred by a friend or other site that clearly defines the specialty.  Hopefully, any traffic that arrives and immediately leaves wasn’t traffic that required resource allocation to generate.

If a visitor does not perceive that they need it immediately when they arrive on your site, you must either persuade them that they do need/want “it” immediately if you wish to convert the visit into a sale.  That may not be the right goal, though.  Oftentimes, the right goal is to build loyalty without asking for the “final” sale by asking for intermediate commitments, like signing up for a newsletter, following you on Twitter, ordering a catalog, or any other intermediate step that generates a larger degree of intimacy with that visitor.  How quickly you seek to convert a visitor to a purchase depends largely on the nature of your offer.  If you are selling novelty T-shirts, converting the sale on the first visit is likely the right pursuit.  If, however, you are selling vintage wine, it is likely not advisable to convert on the first visit.  Quality traffic flow analysis can bring clarity to this kind of question though.

Key Point: Do not base your decisions on what seems reasonable or intuitive.  Look at the data and react to what is actually happening.

Key Point: “What is my bounce rate?” is not the right question.  “What is the bounce rate of my quality traffic?” is the right question.  In other words, are the people you want to be on your site staying?  That’s all that matters.  Who cares about the designers who are just browsing for design inspiration.  If they bounce, it doesn’t matter.  Do you think we freaked out when the general bounce rate spiked after the major design gallery hit?  Of course not.  That is to be expected.  Asking the right questions is the fundamental key to conversion success (oh, and the key to success with everything else, too).

2.  Perceive it to be worth more than they perceive it costs them.

There are two variables here:  Perceived Value and Perceived Cost.  To help the visitor to pass this Gauntlet milestone, you must affect one or both of these factors.  There is no other option.

Perceived Value

Value is a tricky thing.  No two people perceive the same thing as equally valuable.  In fact, I could wax philosophical here and pose the question, “Does true value even exist?” but I will refrain.  Whether or not there is a true value to anything, people perceive it differently.  Our team believes in trying to correctly assess the true value of what we offer and represent that value well.  That encourages our team to constantly improve the value of what we offer, which we appreciate.

There are two factors of perceived value:  Pre-Existing Evaluation and Interaction Effect.  The pre-existing evaluation is relevant to the Quality Traffic variable; the interaction effect is relevant to the Value Presentation variable.

1.  Pre-Existing Evaluation is how the visitor evaluates the product or service prior to interacting with your marketing media.  Except in cases where the individual has never heard of your product or service, everyone will have a Pre-Existing Evaluation.  This is not something you can affect.  Ideally, you want to attract visitors who have a very high pre-existing evaluation of your product or service.  High pre-existing evaluation has a positive effect on Traffic Quality, but it’s not the only sub-variable of Traffic Quality.  Most people have a very high evaluation of a Lamborghini, but that doesn’t mean Lamborghini should try to attract “most people” to their show room.  We’ll discuss the other sub-variables shortly.

2.  Interaction Effect is the effect the interaction with your offering, specifically your e-commerce site, in this case, has on your visitors’ pre-existing evaluation.  The only factor in the Interaction Effect that is relevant to Traffic Quality is how Impressionable the traffic is, which makes them more receptive to positively changing their evaluation of your product/service.  We will discuss the Interaction Effect in greater detail when we discuss the second variable, Value Presentation.

Perceived Cost

The first mistake people make when considering Cost is to assume that it’s simply the price.  It’s not.  First, the same price will be perceived differently in how costly it is depending on the perspective.  A multi-billionaire perceives a private jet to be a very low cost; a millionaire would perceive it to be extremely costly.  When thinking about Perceived Cost, think about it in terms of Perceived Sacrifice, as that is what the person feels. How much a person feels they will experience sacrifice as a result of making a particular decision is a different way of saying Perceived Cost.

Many people perceive less sacrifice in purchasing a $3,000 ring for $100 per month for five years than paying $3,000 cash immediately, even though the first option is twice as costly in terms of cash than the second, and yet it is considered less costly.  Clearly, Perceived Cost and price are not interchangeable.

Money isn’t even the only factor that plays a part in Cost Perception.  Time, stress, focus, and forms of sacrifice are equally important to consider.  Convenience is becoming more important in the purchase of goods and services because a higher value is being placed on time, focus, and stress avoidance.  People value time more, so using less of it means they feel less sacrifice.  This is especially relevant to the third variable, so I will bite my tongue for the time being.

To conclude this section on Perceived Value and Cost, consider that in order to pass this Conversion Gauntlet obstacle, the visitor must perceive more value than the cost in the acquisition of your good and/or service.  Consider all the factors that affect Perceived Value and Perceived Cost.  Most people just look at the price.  Don’t be sucked into that ignorance.  It’s not about price.  It’s about Value and Sacrifice (Cost).

There is tons more to say about the relationship between perceived value and cost.  I could literally write an entire book about it.  But I will be patient and elaborate on it later.

3.  Be able to complete the transaction process without falling below the “Adequate Value Threshold.”

The “Adequate Value Threshold” is that point at which the Perceived Value falls below the Perceived Cost or the Perceived Cost rises above the Perceived Value.  I will be writing a blog post on this idea alone as it is an essential piece of theory that every business person and marketer needs to be able to visualize graphically.

Two important questions relevant to Traffic Quality:

1.  Do they have the technical skill to complete the process?

Your visitors need to be able to use the internet and complete a transaction online in order to purchase from you.  If their technical skills are lacking or if your process is difficult, their attempts at completing the process may produce a great deal of stress or take a lot of time and focus, both of which increases their Perceived (and actual) Cost to complete the transaction.  This can push the Perceived Cost above their Value Threshold, which is the point at which a person says “No.”  Generally, the later this occurs during the interaction, the more likely it will cause the visitor to exit the interaction with a negative outcome.  Visitors don’t typically expect to see adequate value in a product or service early on in the interaction, but once they cross the value rises above the cost or the cost dips below the value, a cross back over typically greatly reduces the likelihood of a positive outcome.

2.  Do they perceive significant risk in giving their credit card and other personal information to a business online?

Security is so important to people because it represents the absence of risk.  A risk is a sacrifice, and therefore a cost.  Reduce risk and you reduce the cost to the visitor.  The less your traffic consider it risky to give personal information, the higher quality traffic it is as it relates to conversion rate specifically because fewer visitors will abandon the process due to the perceived cost of insecurity/risk.

4.  Have the resources to absorb the cost right now.

I clearly have chosen specific verbiage here to represent a more accurate truth.  I could have just said, “They gotta have enough money to pay the price,” but, to belabor the point, it’s not just about money.  Do they have the time, money, and emotional and mental strength to absorb the costs of time, money, stress, and focus that is required to complete the purchase?

Convenience has become a major factor more recently because people are tending to value their time more.  Consider what your target audience values (make a list!) and be sensitive to those values.  If you can’t connect with what your audience values most, how in the world are you going to communicate any value to them?

Conclusion:

Traffic Quality is the nature and condition of people when they arrive on your site.  There are a vast array of preset values that will affect how they respond to your Value Presentation.  Your Value Presentation is not right for everyone.  In fact, it’s probably not right for the grand majority of the market.  Make sure you’re attracting the right Traffic for your Value Presentation.  Sometimes, though, it’s easier to change your Value Presentation to conform to the Traffic Quality you have.  In the next section, I will discuss how Your Value Presentation can affect the two criteria for conversion and what factors you need to consider in optimizing it to better deliver value to your Traffic.

Coming Soon…  Article #3 in the Revenue Optimization Series:  Value Presentation.  I don’t know about you, but I feel like I’m in marketing heaven already.

Appendix A

Wrong Question #1:  What is a good conversion rate?

There is no global “good” conversion rate.  It really depends on your particular circumstances.  Consider this.  If you have a product that requires a large amount of consideration and research before a purchase decision is made, you may have visitors that visit your site 20 times before purchasing.  Since conversion rate is calculated by purchase orders divided by a total number of visits (# of Orders / # of Visits = Conversion Rate), your maximum conversion rate is only 5%.  So if half of the unique visitors that are coming to your site end up purchasing, you are achieving a 2.5% conversion rate.  This is over-simplification, but you get the picture.  Determine your target conversion rate based on your offering and your audience.  Don’t listen to gurus who herald their ability to produce xx% conversion rates and that you should be generating that conversion rate as well.  If their traffic is exclusively club members and their product is mandatory membership cards, their claims become less impressive.  In fact, aiming too high on your conversion rate can generate bad decisions.  By trying to get your visitors to push the button too quickly, you may miss out on opportunities to cross-sell or upsell and you very likely will get to a point when you stop serving their best interests (and that’s what you want most, right?).  If you need help, find a consultant that wants to know your business before telling you what your target conversion rate should be.  That’s a good sign they’ll be helpful.

Next article:  Value Presentation (will link to as soon as the article is finished)

The Llama Commerce Show – Demystifying eCommerce

Gain actionable insights from the brightest minds in eCommerce.

The Llama Commerce Show, at nearly 60 episodes in, continues to demystify eCommerce into actionable bites. Learn about the hottest topics, gain insights and dive into important issues. Go in-depth on topics merchants face including traffic, conversions, data and analytics, strategy, new tools, trends to watch and more.

The Llama Commerce Show is free; available as a podcast for those who want to listen on-the-go.

These recent webcasts from The Llama Commerce Show are worth checking out.

What are the best eCommerce sites?

Go in depth on design and strategy from the world’s best eCommerce businesses according to the IR HOT 100 with Zak Stambor, Managing Editor of Internet Retailer. Available in webcast or podcast.

Episode 57: Insights from the Internet Retailer Hot 100 feat. Zak Stambor

Content marketing for eCommerce

Russ Henneberry of Digital Marketer joins The Llama Commerce Show to share some rock solid strategies for making your eCommerce business stand out. Available in webcast or podcast.

Episode 56: 3 Proven eCommerce Content Marketing Strategies for 2016 feat. Russ Henneberry. 

Identifying target markets in eCommerce

Stop focusing on the wrong customers and begin catering to your best eCommerce customers. Talk with the expert behind nerdmarketing.com, Drew Sanocki, to see how merchants commonly attract the wrong buyers. Available in webcast or podcast.

Episode 55: Stop Focusing on the Wrong Customers feat. Drew Sanocki

PPC, SEO, Google Analytics, Big Data, Oh my!

There is so.much.data. It’s daunting. Carolyn Harrington of Moco Insight helps us narrow down which metrics are worth the time it takes to study and drive growth and please customers. Available in webcast or podcast.

Episode 52: How Data Dictates Growth feat. Carolyn Harrington

Addressing mobile growth in eCommerce

Katie Evans from Internet Retailer and Mobile Strategies 360 joins The Llama Commerce Show to share what merchants need to know about having a stronger mobile eCommerce presence. Available in podcast.

Episode 51: Making the Most of Mobile feat. Katie Evans from Internet Retailer

Watch for upcoming The Llama Commerce Show episodes

February 19, 2016

Guest Matt Price from Tethertug.com talks about the story behind their brand, product and how they’ve become so successful online.

March 18, 2016

Pete Hagemann from Gander Mountain joins for this episode of The Llama Commerce Show

A Classy Year in Review: Celebrating Eight Years

We had quite a year! In 2015, Classy Llama celebrated eight years as a company. In this time, we have grown a lot in size and grown in terms of our bond to one another. Each year is better than the last and 2015 was no exception. Holding true to what we believe to be a key to our success, taking care of one another, we gathered together for a number of social events including picnic potlucks, ice skating, mini golf, laser tag, board game nights, and more. For this Year in Review post, we want to highlight employee events and activities that took place in 2015. We hope you enjoy seeing what we do as a team, when it’s not running command lines or interfacing with our clients.

In 2015, we were honored to be listed in the Inc. 5000 for the second year in a row. In three years, we have seen 92% growth! As a testament to that, we outgrew our office space for the first time since May 2011 (when we relocated from our downtown office). Fortunately, the office building adjacent to us needed new renters. So now we have filled two neighboring offices, making us an official campus!

Believe it or not, we continue to grow! In 2015, we added three new full-time SEO and PPC team members, six new developers, two Project Managers and more to help us manage growth.

[cta prefix=”Related:” count=”1″][/cta]

We have a new president!

Former Classy Llama C.O.O., Carrie Weidenbach, was promoted to President. She has been with Classy Llama since 2011 and a professional in the IT industry for over a decade. Carrie was tech before tech was cool and her experience has already guided Classy Llama towards success in previous years. Her super powers include telekinesis, energy projection, weather manipulation and invisibility. Also, being president of our team.


Classy Llama’s Got Talent

Our first annual talent show proved a major success. This group has a few acts that Howie Mandel would be sure to put through to the next round. Chris, Zach and Gino Nanninga performed a Beatles cover. Adam “Dancing with the Stars” Prost and lady-friend busted a move on the dance floor. Sam Paasch broke a cinder block in half with his bare hands. Ultimately, the trophy went to Miranda “Coltrane” Hodges for her work on the saxophone. But everyone who participated was a winner for braving the stage lights in front of their co-workers and families.

Llamas and Laser Tag

Here in the Midwest, we are blessed with Hickories, Oaks and Evergreens. While some might admire them for their beauty and what they do for the landscape, at Classy Llama, we see them as makeshift obstacles for avoiding sniper fire. I should mention how we are also blessed to have Bill’s Extreme Paintball and Laser Tag.

Each year, we do at least two outdoor laser tag outings, serving as a personal favorite monthly activity for some of the guys, gals, husbands, and wives who participate. This past year, the equipment came to us and the Classy Llama campus (buildings and all) transformed into an urban battlefield. 

Detroit Pistons and Detroit Tigers Outings

Our Michigan office gets to take advantage of next level sports! Michigan employees enjoyed outings to see local NBA team, Detroit Pistons, and local MLB team, Detroit Tigers, this past year. After the Pistons game, the families were allowed to shoot hoops on the floor.  Following the Tigers game, the families went down to meet Paws, the Tigers’ mascot.

Annual Springfield Cardinals Outing

For those of you who had to do a double take, that’s right, Springfield Cardinals. We love our Saint Louis Cardinals and Kansas City Royals, but this minor league team and an affiliate of the Cardinals franchise is a little closer to home for us. This event is fun for all ages. All of our local employees and families turn out for it because Hammons Field is quite beautiful and really adds something to the beauty of our downtown.


Llama Christmas Party

Christmas lights, hot chocolate, mistletoe, carolers, snow… and ice sculptures? That’s just how we roll when it comes to Christmas parties. We decked the halls this year at the Keeter Center at College of the Ozarks. The festivities included games, prizes, a string quartet, dinner and dessert, and some reflective, spirited and heartfelt moments.

Epic Party

Food trucks, games, a dunking tank and lots of fun activities for the whole family marked this year’s celebration of eight years as a company. One fine September evening, we coalesced in the midst of tiki-torch and enchanting overhung lighting. It was a beautiful setting for a party, with wholesome activities for the little ones as the adults mingled and the energetic ones formulated backyard games. After crowning an official Duke and Duchess of Llamaland (nominated and voted on by staff), we topped the night off with a professional fireworks display!

Until next year…

A Classy Year in Review: Our Work from 2015

Select eCommerce Work from 2015.

For this Year in Review, we want to show you several sites we built or serviced and maintained on the Magento platform in 2015. One of our largest accomplishments in 2014 was our role in developing Magento’s Responsive Theme. In 2015, all of our custom builds were on this responsive theme, meaning these sites scale beautifully from mobile to desktop. This is why you will see both the desktop and mobile versions of the highlighted sites. With that said, here are a few highlights from 2015 that we are proud of. We hope you enjoy.

Elemental LED creates and sells innovative, configured, and integrated LED lighting solutions. To show off the company’s superior product designs, we built an inspiration gallery that lets shoppers view beautifully lit rooms and spaces and find the Elemental LED products that made it possible.

www.elementalled.com

TileBar is a direct manufacturer and importer of tiles based in New York and New Jersey. To meet many of their goals without unnecessary custom development, the company’s new site takes full advantage of features that come with the Nucleus Commerce Platform. Another feature we added was the lookbook functionality. Lookbooks allow for Tilebar to showcase product lines or create inspiration around a particular tiling concept.

www.tilebar.com

DaySpring and its team of in-house developers came to Classy Llama to support them by providing further Magento expertise and to gain insight on setting up a multi-server hosting environment for optimal performance. With the help of the Classy Llama team, DaySpring launched a new theme that would strengthen its position as the most well-known brand of Christian and inspirational cards and gifts.

www.dayspring.com

Heat & Cool is one of the largest online distributors of HVAC products in the United States. Classy Llama had the privilege of helping Heat & Cool implement a new responsive theme and a unique system selector that helps its customers feel confident they are purchasing the correct product for their needs. 

www.heatandcool.com

MerryStockings sells unique Christmas decor. Using a custom built embroidery feature, customers can personalize and preview ideas on the front end, and specs for production can be presented to the manufacturer through the back end.

www.merrystockings.com



On the marketing side, longtime client OutdoorCooking.com challenged us with the objective of selling more pellet grills in Q4 of 2015. The team focused primarily on Google Shopping and AdWords Text ads but also allocated a small budget amount to targeted Facebook ads and YouTube video ads. The results were impressive. During the holiday shopping season of 2015, OutdoorCooking.com sold more pellet grills than total online pellet grill sales during all of 2014!  

www.outdoorcooking.com



The Magento sales team works with merchants all over the world to help them determine whether Magento is the right platform for them. Classy Llama worked with Magento to build a sales demo for its internal sales team to use when presenting Magento to prospective merchants. This demo included a custom theme as well as functionality, such as in-store pickup.

As we applaud the Magento team for a job well done launching Magento 2, we can’t help but brag about Classy Llama’s involvement in the process. It was a huge honor to be selected as one of the partners to help Magento build Magento 2. We introduced responsive design, simple internationalization, and separation of content and styling to the email framework in Magento 2. As far as we’re aware, we contributed more to Magento 2’s code base than any other agency.

Marlow White is a major supplier of quality Army, Navy and First Responder uniforms. Classy Llama was able to vastly improve the experience for Marlow White’s customers, especially on mobile platforms, with a Magento upgrade, a new responsive theme, and user experience optimization. Personally, we can’t stop admiring the new Mega Menu!

www.marlowwhite.com

Aggieland Outfitters offers a variety of high-quality Aggie clothing and merchandise. Classy Llama helped Aggieland upgrade its site from the Magento Community Edition to the Enterprise Edition and continues to provide marketing support and development support through Llama Desk.

www.aggielandoutfitters.com

Shurhold designs and manufactures innovative specialty care and accessory products available for the marine, RV, and automotive industries. Shurhold’s tagline is “Clean-N-Simple” and that’s how we approached this new site build. We wanted customers to have quick and easy access to tips on cleaning their equipment. Using technology from search extension, Algolia, the content site seamlessly integrates with the eCommerce site. Did we mention that Shurhold has been a Classy Llama client since 2011? We are excited to usher them into a new phase with an upgrade, a responsive theme and so much more!

www.shurhold.com

It’s Worth Mentioning

2015 was a successful year for our clients. Overall, 60% of our projects came in under budget. Beyond that, the actual cost compared to budgeted cost on projects had a variance of less than a two percent on average. What’s more, the trend line shows that we became more successful at meeting these goals as the year progressed.

The success stories that emerged from last year are immeasurable. In an ever-evolving and fast-paced eCommerce industry, we live for new challenges. Challenges are what drive us forward. We look back with fondness over an amazing year of accomplishments. To our clients and partners, thank you for trusting us. Please continue to challenge us.

We would love to show you more from this past year and talk about new services we are beginning to offer. For example, we introduced affiliate marketing powered by our partner AIM to our clients in 2015. If you would like to know more about what made 2015 so great for our clients or you’d like to work with us in 2016, we’d love to hear from you!

What to Expect When You’re Expecting… Magento 2

It’s taken a bit longer than 9 months, but Magento 2 is nearly here! Magento has done a fantastic job hitting all of the benchmarks and timelines it proposed at the beginning of 2015: Developer beta, then Merchant beta, and now General Availability.

Here at the Classy Llama Worldwide HQ, we’ve been fielding an increasing number of calls from merchants interested in learning more about Magento 2. I thought I would distill down what I’ve learned since Imagine 2015 and try to provide our merchant readers some useful information.

I Feel The Need, The Need for Speed.

image03So what is Magento 2? In one word, faster. There has been an intense focus on building Magento for better performance, especially for large multi-server clusters supporting enterprise users. It’s a complete rebuild of the Magento framework, with all of the lessons learned from 6+ years of software development incorporated into the architecture. Adding to the cart is 50% faster, check out times are 8-10% faster and out of the box Magento 2 can serve up to 200K page views and 50K orders per hour. Magento is requiring automated tests for all extensions distributed via Magento Connect, and are stressing quality throughout the codebase. It should be a good deal more stable, both within the Magento core and in any verified extensions from Connect. Magento extension vendor aheadWorks has a great blog post with more info on the technical details.

Not Sure If…

image00

For merchants already on Magento, there’s always a question of when the next upgrade is coming. The responsive theme that shipped with Community Edition 1.9 and Enterprise Edition 1.14 has been a huge impetus for upgrades, especially in light ofMobilegeddon, Google’s mass shaming of sites that are not mobile-friendly. If your current Magento site is responsive, you can probably afford to wait a little longer to upgrade. If it isn’t responsive, please give us a call ASAP- we would love to build a new, responsive Magento 2 site for you!

The biggest strength of Magento is its ecosystem of partners and service providers, and there are over 100 of these available now for Magento 2, with more to come. There’s not yet 100% coverage though, so you won’t have as robust of a selection of third-party modules until (likely) mid-late 2016. We highly recommend planning a migration strategy with your service providers to ensure they are ready for your timeline.

If you’re a Magento Community Edition user and you’re starting to experience growing pains with site traffic and performance, Magento is rolling the full page cache and some of the improved database indexing from Enterprise 1.X into Community 2.0. If you’re at the point where you need multiple databases and web nodes, then you definitely want to take a look at Magento 2 Enterprise Edition – it’s designed specifically around this use case. A fast site is a happy site, happy sites make happy customers, and happy customers convert!

The bottom line is, if you are happy with your site performance and mobile experience – you may be able to wait until things calm down in mid 2016 before you upgrade to Magento 2.

We Can Rebuild… Magento!

image04On the other hand, if your current eCommerce platform is having performance issues or is not mobile-friendly, then Magento 2 is definitely a sooner-rather-than-later option. Why? Because not having a mobile friendly site hurts your SEO, and reaaallly sllooowwwww page load times annoy shoppers. Magento 2 now represents the quickest path to a truly responsive, scalable, feature-rich eCommerce platform. For those already on Magento 1.X and considering an upgrade, Magento has released a data conversion utility that will convert your catalog, customer, and order core data over fairly painlessly. You will definitely need to replace any custom code or non-core data, and it’s highly likely you will need a new theme built – but it’s not a ‘start-over-from-scratch’ proposition.

Llama Love

image01

So why Classy Llama? Yep, we’re Gold Certified Magento partners. And yes, we only develop on Magento. But what makes us special when it comes to Magento 2? Didn’t you just say it’s a completely new framework built on new technology? I’m glad you asked! Or rather, I’m glad I asked on your behalf.

First off, we’re one of few agencies in the US who are already certified Magento 2 Trained Solution Partners. Additionally, our developers have been contributing to the Magento 2 codebase for several months. You can check out the Contributions page here – Classy Llama team members listed include David Alger, Erik Hansen, Eric Wiese, and Rob Tull. Outside of Magento’s own employees, our team has been one of the largest contributors of code to the new platform. We’ve devoted a ton of time to rebuilding our development environments, reading the docs, writing blog posts, and coding in Magento 2. It’s not scary, it’s not hard – it’s just different. We’re confident that if you need a Magento 2 site built, we’re the best ones to do it.

Magento 2 Trained Solution Partner

Port Magento 1 Modules to Magento 2 with Plugins

Project Overview

One of Magento’s biggest strengths is its multi-store functionality. The ability to override data and configuration for a given website or store while inheriting higher scope values is particularly powerful, allowing merchants and developers to create tailored experiences without the overhead of managing completely different stores.

Although very powerful and flexible, managing many system configuration values for many different websites and stores can make it easy to overlook when a value is overridden at a more specific scope.

A similar problem exists when managing a large codebase with many subclasses. It’s sometimes easy to overlook when a method is overridden by a subclass, wasting development time. I’ve always been a fan of PHP Storm’s indication when a method is overridden by a subclass — the visibility that a simple icon provides is incredible.

While working on a particularly multi-store-heavy site, I found myself similarly wasting time when I would fail to notice (or check) if a system configuration value was overridden at a more specific scope, and wished I had a similar visual cue. This prompted me to explore creating a module which would add this type of functionality to the system configuration.

Due to the recent release of Magento 2 RC2, I also wanted to explore porting this module to Magento 2.

Magento 1 Module

Requirements

Conceptually, this is a relatively simple addition. When each field is rendered, compare its value at the current scope to the values at more specific scopes. If the field’s value is different at any more specific scopes, add a visual indication to the right-most column with details and links to the overriding scopes.

Implementation

As with most modules which inject new functionality into existing areas of Magento, there are two big parts to the approach: find the most suitable point to modify or interact with core code, then use that interaction point to call custom logic which implements new functionality.

Interaction Point

After several debugging sessions, I found that there are no templates or layout XML for individual system configuration fields — we’ll be dealing with straight PHP for this modification. In particular, I need a to find a point in the core where (a) enough information about the field is present to perform value comparisons and (b) I can also influence the output of the scope label column.

In general, Mage_Adminhtml_Block_System_Config_Form::initFields() loops over elements of a group, and encapsulates the entire rendering of a field row. However, it doesn’t dispatch any events, nor is it well structured to subclass without copying the method contents forward — in fact, a rewrite of this method would require copying forward more than 200 lines of very logic-dense code. As such, it is an undesirable interaction point.

Mage_Adminhtml_Block_System_Config_Form::getScopeLabel(), however, seems promising; a simple method which accepts a field element, determines its scope label, and then returns the scope label as a string. A subclass could add content to the returned string without having to copy any code forward. Unfortunately, the element parameter does not contain sufficient information to get the field’s values, rendering this interaction point unusable for our purposes.

After much effort to find a “surgical” interaction point, I came to realize that, unfortunately, it would be necessary to rewrite Mage_Adminhtml_Block_System_Config_Form and copy its entire initFields() method forward. In an effort to avoid any more direct modifications than necessary, the only change I made to this method was to add an event before the element is rendered. This event, in turn, is observed by the module to effect the desired new functionality.

NOTE: this very compatibility concern was quickly manifested — on EE 1.10 (at least), the config scope hints are shown and accurate, but the UI input near them is always blank. After some investigation, this is due to differences in initFields() between the two versions. At this point, reconciling the different version is prohibitively time-consuming.

Custom Logic Implementation

Having determined an interaction point, the actual new functionality must be implemented. After some consideration, I decided to structure the encapsulated value override detection according to the following logic, given a config path and current context scope and scope ID.

  1. Get field’s precise value at context scope.
  2. Generate scope tree below current scope.
  3. Iterate over scope tree, getting values at each precise scope.
  4. If the iteration scope’s value does not match the context scope’s value, then add it to an array of overriding scopes.
  5. Finally, format output HTML string based on override array.

The most complicated part of this logic was reviewing the plethora of system config methods available through the core, and determining which is the best one to use for each of these steps. In particular, getting the precise value of a path at a given scope required using different methods from different models depending on the actual scope. That is, if a given field is set at the default, website, and store values, I couldn’t simply use Mage::getStoreConfig() to get the website value — this method expects a store instance, and passing in one of the website’s child stores would return the store’s value, which may differ from the website value. In addition, since it was necessary to use lower level methods to get precise values, I had to ensure that fields with a backend model were handled correctly.

However, after some trial and error to find core methods to robustly handle these issues, the resulting logic was complete.

<?php

class EW_ConfigScopeHints_Helper_Data extends Mage_Core_Helper_Abstract
{
    const PROFILER_KEY = 'EW_ConfigScopeHints';

    /**
     * Get default store ID.
     * Abstracted so it can be improved if default store
     * ID not always 0.
     *
     * @return int
     */
    protected function _getDefaultStoreId() {
        return 0;
    }

    /**
     * Get scopes tree in following form:
     *
     * array('websites' => array (
     *          website id => array('stores' => array of store ids),
     *          ...
     *     )
     * )
     *
     * @return array
     */
    public function getScopeTree() {
        $tree = array('websites' => array());

        $websites = Mage::app()->getWebsites();

        /* @var $website Mage_Core_Model_Website */
        foreach($websites as $website) {
            $tree['websites'][$website->getId()] = array('stores' => array());

            /* @var $store Mage_Core_Model_Store */
            foreach($website->getStores() as $store) {
                $tree['websites'][$website->getId()]['stores'][] = $store->getId();
            }
        }

        return $tree;
    }

    /**
     * Get config node value after processing with backend model
     *
     * @param Mage_Core_Model_Config_Element $node
     * @param $path
     * @return string
     */
    protected function _getProcessedValue(Mage_Core_Model_Config_Element $node, $path) {
        $value = (string)$node;
        if (!empty($node['backend_model']) && !empty($value)) {
            $backend = Mage::getModel((string)$node['backend_model']);
            $backend->setPath($path)->setValue($value)->afterLoad();
            $value = $backend->getValue();
        }

        return $value;
    }

    /**
     * Get current value by scope and scope ID,
     * or null if none could be found.
     *
     * @param $path
     * @param $contextScope
     * @param $contextScopeId
     * @return mixed|null|string
     * @throws Mage_Core_Exception
     */
    protected function _getConfigValue($path, $contextScope, $contextScopeId) {
        $currentValue = null;
        switch($contextScope) {
            case 'websites':
                $code = Mage::app()->getWebsite($contextScopeId)->getCode();
                $node = Mage::getConfig()->getNode('websites/'.$code.'/'.$path);
                $currentValue = !$node ? '' : $this->_getProcessedValue($node, $path);
                break;
            case 'default':
                $node = Mage::getConfig()->getNode('default/' . $path);
                $currentValue = !$node ? '' : $this->_getProcessedValue($node, $path);
                break;
            case 'stores':
                $currentValue = Mage::app()->getStore($contextScopeId)->getConfig($path);
                break;
        }

        return $currentValue;
    }

    /**
     * Get scopes where value of config at path is overridden.
     * Returned in form of
     * array( array('scope' => overridden scope, 'scope_id' => overridden scope id), ...)
     *
     * @param $path
     * @param $contextScope
     * @param $contextScopeId
     * @return array
     */
    public function getOverridenLevels($path, $contextScope, $contextScopeId) {
        $contextScopeId = $contextScopeId ?: $this->_getDefaultStoreId();

        $currentValue = $this->_getConfigValue($path, $contextScope, $contextScopeId);

        if(is_null($currentValue)) {
            return array(); //something is off, let's bail gracefully.
        }

        $tree = $this->getScopeTree();

        $overridden = array();

        switch($contextScope) {
            case 'websites':
                $stores = array_values($tree['websites'][$contextScopeId]['stores']);
                foreach($stores as $storeId) {
                    $value = $this->_getConfigValue($path, 'stores', $storeId);
                    if($value != $currentValue) {
                        $overridden[] = array(
                            'scope'     => 'store',
                            'scope_id'  => $storeId
                        );
                    }
                }
                break;
            case 'default':
                foreach($tree['websites'] as $websiteId => $website) {
                    $websiteValue = $this->_getConfigValue($path, 'websites', $websiteId);
                    if($websiteValue != $currentValue) {
                        $overridden[] = array(
                            'scope'     => 'website',
                            'scope_id'  => $websiteId
                        );
                    }

                    foreach($website['stores'] as $storeId) {
                        $value = $this->_getConfigValue($path, 'stores', $storeId);
                        if($value != $currentValue && $value != $websiteValue) {
                            $overridden[] = array(
                                'scope'     => 'store',
                                'scope_id'  => $storeId
                            );
                        }
                    }
                }
                break;
        }

        return $overridden;
    }

    /**
     * Format overridden scopes for output
     *
     * @param array $overridden
     * @return string
     */
    public function formatOverriddenScopes(array $overridden) {
        $title = $this->__('This setting is overridden at a more specific scope. Click for details.');

        $formatted = '<a class="overridden-hint-list-toggle" title="'. $title .'" href="#">'. $title .'</a>'.
                     '<ul class="overridden-hint-list">';

        foreach($overridden as $overriddenScope) {
            $scope = $overriddenScope['scope'];
            $scopeId = $overriddenScope['scope_id'];
            $scopeLabel = $scopeId;

            $url = '#';
            $section = Mage::app()->getRequest()->getParam('section'); //grrr.
            switch($scope) {
                case 'website':
                    $url = Mage::getModel('adminhtml/url')->getUrl(
                        '*/*/*',
                        array(
                            'section'=>$section,
                            'website'=>Mage::app()->getWebsite($scopeId)->getCode()
                        )
                    );
                    $scopeLabel = sprintf(
                        'website <a href="%s">%s</a>',
                        $url,
                        Mage::app()->getWebsite($scopeId)->getName()
                    );

                    break;
                case 'store':
                    $store = Mage::app()->getStore($scopeId);
                    $website = $store->getWebsite();
                    $url = Mage::getModel('adminhtml/url')->getUrl(
                        '*/*/*',
                        array(
                            'section'   => $section,
                            'website'   => $website->getCode(),
                            'store'     => $store->getCode()
                        )
                    );
                    $scopeLabel = sprintf(
                        'store view <a href="%s">%s</a>',
                        $url,
                        $website->getName() . ' / ' . $store->getName()
                    );
                    break;
            }

            $formatted .= "<li class='$scope'>Overridden on $scopeLabel</li>";
        }

        $formatted .= '</ul>';

        return $formatted;
    }
}

Results

After adding a few styles and a tiny bit of Prototype UI javascript, the desired functionality works like a charm — fields overridden at more specific scopes now clearly indicate this fact.

Packaging

As with any other module distributed on GitHub, the standard packaging system is to create a modman config file to allow developers and merchants automated installations.

app/code/community/EW/ConfigScopeHints
app/etc/modules/EW_ConfigScopeHints.xml
app/design/adminhtml/default/default/layout/ew/configscopehints.xml
skin/adminhtml/default/default/ew/configscopehints

## translations
app/locale/en_US/EW_ConfigScopeHints.csv
app/locale/es_ES/EW_ConfigScopeHints.csv

Magento 2 Module

Requirements

Magento 2 not only provides a much improved and better-designed framework on which to build, but also the opportunity to rethink existing approaches and requirements. However, given that this project was specifically intended to start as the concrete exercise of a straight port, the requirements were exactly the same as the Magento 1 module.

NOTE: this port was implemented and tested on Magento 2 version 0.74.0-beta4.

Implementation

This module has the same approach considerations as its Magento 1 inspiration: find the best way to interact with the core, then use that interaction point to implement custom functionality to accomplish the requirements. Due to significant architectural differences in M1 and M2, the M2 module would need to be restructured, pulling in a much code from the M1 version as possible.

Interaction Point

The biggest liability of the M1 module is the fact that, despite using a proper extension mechanism (block rewrite), a huge amount of logic had to be copied from the core and modified to create an interaction point, creating a significant surface area for compatibility issues.

In an effort to improve the interaction point in M2, I revisited the oh-so-close MagentoConfigBlockSystemConfigForm::getScopeLabel() method. Unlike M1, the M2 version of this method accepts a more specific object as its parameter, which handily does have all the required information to get its value. This small change means that the surface area of the interaction point (along with the possibility for compatibility issues) is significantly reduced.

Using my Magento customization bag of tricks, honed over the last three years and spanning as many certifications, I examined the options to interact with the getScopeLabel() method. As always, my first consideration is to determine which events, if any, can be observed. Alas, as is too often the case, there are no events which can be used.

In the absence of a usable event, my next consideration is to determine if the interaction point can be created by subclassing Form and overriding the getScopeLabel() method. Subclassing the Form class is still not ideal, as it would require a rewrite to take effect system-wide. While no code will be copied forward, this rewrite would conflict with any other module which also rewrites this class.

If only Magento could have anticipated my requirements and dispatched an event which passed the method’s arguments and also provided the opportunity to append to the method’s return value …

Fortunately, Magento 2, introduces the concept of plugins — a brand new extension mechanism which provides this exact functionality. Instead of depending on developers to anticipate all possible future customizations and dispatch corresponding events, plugins can intercept method calls before or after invocation, providing extremely flexible global interaction points.

In order to accomplish this module’s requirements, it actually needs to observe both before (to have original arguments present) and after (to append to the return value) getScopeLabel() invocation. This is supported by the around plugin listener type.

di.xml

<?xml version="1.0"?>
<config xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xsi_noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <type name="MagentoConfigBlockSystemConfigForm">
        <plugin name="configScopeHints" type="EWConfigScopeHintsModelPlugin" sortOrder="100" />
    </type>
</config>

Plugin.php

<?php

namespace EWConfigScopeHintsModel;
use MagentoConfigModelConfigStructureElementField;
use MagentoFrameworkPhrase;

class Plugin
{
    /** @var EWConfigScopeHintsHelperData */
    protected $_helper;
    /**
     * @param EWConfigScopeHintsHelperData $helper
     */
    public function __construct(EWConfigScopeHintsHelperData $helper) {
        $this->_helper = $helper;
    }
    /**
     * Intercept core config form block getScopeLabel() method
     * to add additional override hints.
     *
     * @see MagentoConfigBlockSystemConfigForm::getScopeLabel()
     * @param MagentoConfigBlockSystemConfigForm $form
     * @param callable $getScopeLabel
     * @param Field $field
     * @return Phrase
     */
    public function aroundGetScopeLabel(MagentoConfigBlockSystemConfigForm $form, Closure $getScopeLabel, Field $field)
    {
        $currentScopeId = null;
        switch($form->getScope()) {
            case 'websites':
                $currentScopeId = $form->getWebsiteCode();
                break;
            case 'stores':
                $currentScopeId = $form->getStoreCode();
                break;
        }
        $overriddenLevels = $this->_helper->getOverridenLevels($field->getPath(), $form->getScope(), $currentScopeId);
        /* @var $returnPhrase Phrase */
        $labelPhrase = $getScopeLabel($field);
        if(!empty($overriddenLevels)) {
            $scopeHintText = $labelPhrase . $this->_helper->formatOverriddenScopes($form, $overriddenLevels);
            // create new phrase, now that constituent strings are translated individually
            $labelPhrase = new Phrase($scopeHintText, $labelPhrase->getArguments());
        }
        return $labelPhrase;
    }
}

With this small amount of code, the module has a perfect interaction point, without having to rewrite the block — the chances of compatibility issues have never been lower.

Custom Logic Implementation

With a slick interaction point established, the M1 custom logic must be ported to M2. This was surprisingly easy — the only significant changes were that the config models must be injected rather than pulled off the old global Mage class and the logic had to be updated to use the M2 config methods to find config values for comparison.

Due to the robust dependency injection support in M2, I had to do nothing special to get instances of the config models — simply adding them to the constructor was sufficient. Adjusting for the new config model methods was also trivial. In fact, it was easy to find new methods which greatly simplified the previously unintuitive mechanisms for finding the precise value of a config field at a given scope. All in all, porting the module’s business logic didn’t take more than a half hour or so.

<?php

namespace EWConfigScopeHintsHelper;
use MagentoStoreModelWebsite;
use MagentoStoreModelStore;

class Data extends  MagentoFrameworkAppHelperAbstractHelper
{
    /** @var MagentoFrameworkAppHelperContext */
    protected $_context;
    /** @var MagentoStoreModelStoreManagerInterface */
    protected $_storeManger;

    /**
     * @param MagentoFrameworkAppHelperContext $context
     * @param MagentoStoreModelStoreManagerInterface $storeManager
     */
    public function __construct(
        MagentoFrameworkAppHelperContext $context,
        MagentoStoreModelStoreManagerInterface $storeManager
    ) {
        $this->_storeManger = $storeManager;
        $this->_context = $context;
    }

    /**
     * Gets store tree in a format easily walked over
     * for config path value comparison
     *
     * @return array
     */
    public function getScopeTree() {
        $tree = array('websites' => array());

        $websites = $this->_storeManger->getWebsites();

        /* @var $website Website */
        foreach($websites as $website) {
            $tree['websites'][$website->getId()] = array('stores' => array());

            /* @var $store Store */
            foreach($website->getStores() as $store) {
                $tree['websites'][$website->getId()]['stores'][] = $store->getId();
            }
        }

        return $tree;
    }

    /**
     * Wrapper method to get config value at path, scope, and scope code provided
     *
     * @param $path
     * @param $contextScope
     * @param $contextScopeId
     * @return mixed
     */
    protected function _getConfigValue($path, $contextScope, $contextScopeId) {
        return $this->_context->getScopeConfig()->getValue($path, $contextScope, $contextScopeId);
    }

    /**
     * Gets array of scopes and scope IDs where path value is different
     * than supplied context scope and context scope ID.
     * If no lower-level scopes override the value, return empty array.
     *
     * @param $path
     * @param $contextScope
     * @param $contextScopeId
     * @return array
     */
    public function getOverridenLevels($path, $contextScope, $contextScopeId) {
        $tree = $this->getScopeTree();

        $currentValue = $this->_getConfigValue($path, $contextScope, $contextScopeId);

        if(is_null($currentValue)) {
            return array(); //something is off, let's bail gracefully.
        }

        $overridden = array();

        switch($contextScope) {
            case 'websites':
                $stores = array_values($tree['websites'][$contextScopeId]['stores']);
                foreach($stores as $storeId) {
                    $value = $this->_getConfigValue($path, 'stores', $storeId);
                    if($value != $currentValue) {
                        $overridden[] = array(
                            'scope'     => 'store',
                            'scope_id'  => $storeId
                        );
                    }
                }
                break;
            case 'default':
                foreach($tree['websites'] as $websiteId => $website) {
                    $websiteValue = $this->_getConfigValue($path, 'websites', $websiteId);
                    if($websiteValue != $currentValue) {
                        $overridden[] = array(
                            'scope'     => 'website',
                            'scope_id'  => $websiteId
                        );
                    }

                    foreach($website['stores'] as $storeId) {
                        $value = $this->_getConfigValue($path, 'stores', $storeId);
                        if($value != $currentValue && $value != $websiteValue) {
                            $overridden[] = array(
                                'scope'     => 'store',
                                'scope_id'  => $storeId
                            );
                        }
                    }
                }
                break;
        }

        return $overridden;
    }

    /**
     * Get HTML output for override hint UI
     *
     * @param MagentoConfigBlockSystemConfigForm $form
     * @param array $overridden
     * @return string
     */
    public function formatOverriddenScopes(MagentoConfigBlockSystemConfigForm $form, array $overridden) {
        $title = __('This setting is overridden at a more specific scope. Click for details.');

        $formatted = '<a class="overridden-hint-list-toggle" title="'. $title .'" href="#"><span>'. $title .'</span></a>'.
            '<ul class="overridden-hint-list">';

        foreach($overridden as $overriddenScope) {
            $scope = $overriddenScope['scope'];
            $scopeId = $overriddenScope['scope_id'];
            $scopeLabel = $scopeId;

            $url = '#';
            $section = $form->getSectionCode();
            switch($scope) {
                case 'website':
                    $url = $this->_context->getUrlBuilder()->getUrl(
                        '*/*/*',
                        array(
                            'section'=>$section,
                            'website'=>$scopeId
                        )
                    );
                    $scopeLabel = sprintf(
                        'website <a href="%s">%s</a>',
                        $url,
                        $this->_storeManger->getWebsite($scopeId)->getName()
                    );

                    break;
                case 'store':
                    $store = $this->_storeManger->getStore($scopeId);
                    $website = $store->getWebsite();
                    $url = $this->_context->getUrlBuilder()->getUrl(
                        '*/*/*',
                        array(
                            'section'   => $section,
                            'website'   => $website->getCode(),
                            'store'     => $store->getCode()
                        )
                    );
                    $scopeLabel = sprintf(
                        'store view <a href="%s">%s</a>',
                        $url,
                        $website->getName() . ' / ' . $store->getName()
                    );
                    break;
            }

            $formatted .= "<li class='$scope'>Overridden on $scopeLabel</li>";
        }

        $formatted .= '</ul>';

        return $formatted;
    }
}

Results

Porting over the tiny bit of CSS into LESS and porting the tiny bit of UI javascript from Prototype to jQuery took no time at all, and before I knew it I had successfully ported my M1 module to M2.

Packaging

With the minor detail of actually accomplishing the requirements out of the way, it was time to package up the module and make it easy for merchants and developers to install. Due to Magento 2’s standardization on composer, this was very straightforward.

The module was committed to GitHub at the module root level, and a simple composer.json file added.

{
    "name": "ericthehacker/magento2-configscopehints",
    "description": "Magento 2 store config override hints module",
    "require": {
        "magento/magento-composer-installer": "*"
    },
    "type": "magento2-module",
    "version": "2.0",
    "extra": {
        "map": [
            [
                "*",
                "EW/ConfigScopeHints"
            ]
        ]
    },
    "authors": [
        {
            "name": "Eric Wiese",
            "homepage": "https://ericwie.se/",
            "role": "Developer"
        }
    ]
}

With this file in place, merchants and developers can easily install the module by running commands similar to the following from their Magento 2 root directory.

$ composer config repositories.magento2-configscopehints vcs https://github.com/ericthehacker/magento2-configscopehints.git # add repo
$ composer require ericthehacker/magento2-configscopehints # require module
$ php -f bin/magento module:enable EW_ConfigScopeHints 
$ php -f bin/magento setup:upgrade

Pro Tips and Lessons Learned

Although I consider this port to have gone very smoothly, there were a few roadblocks for which the solution was not obvious. These are the gotchas and hiccups that I encountered and their solutions.

Outdated / missing documentation

When following Magento’s official developer documentation (as opposed to the old documentation which, annoyingly, seems to outrank the new docs in Google search results), a module’s basic declaration is of the following form.


<config>
 <module name="Namespace_Module" schema_version="2.0.0">	 	 
 </module>	 	 	 
</config>

Following that snippet, I created my module shell, only to be greeted with the following error message.

Attribute 'setup_version' is missing for module 'EW_ConfigScopeHints'.

As stated in the error message, the schema_version attribute should actually be setup_version. Not a big change, but it’s kind of concerning that such a fundamental snippet is factually incorrect in the documentation — especially since this will be each new developer’s first stop.

At the very least, however, the presence of that error message demonstrated that my module was, indeed, being loaded. However, after fixing the attribute name, I couldn’t seem to get any of my module’s functionality to work. Struggling with out of date and missing documentation, I assumed that my dependency injection XML was simply incorrect. After much trial and error and debugging, I realized my module simply wasn’t enabled.

Although the documentation does give the following note, it doesn’t say how to actually enable a module — I’m doing development and I don’t have a deployment configuration file!

The enabled/disabled flag for a module is no longer set within a module; it is controlled by the deployment configuration file, with enabled=1, disabled=0. This is controlled by administrators and integrators, not by module developers.

With no actual documentation to guide me, I eventually figured out that modules must be enabled in app/etc/config.php. However, editing this file manually is discouraged, so the following commands (currently) are the best way to enable a module manually.


$ php -f bin/magento module:enable EW_ConfigScopeHints 
$ php -f bin/magento setup:upgrade

Since I think these documentation gaps are critical, I’ve created a pull request to update the documentation.

Developer Mode

Looking up errors in var/report gets old pretty quickly, so one of the first things I needed to figure out is how to display errors by enabling developer mode. Fortunately, it’s really straightforward — the MAGE_MODE environmental variable must be set to the value developer, as shown in the following examples.

  • Apache config: SetEnv MAGE_MODE developer
  • Bash .bash_profile: export MAGE_MODE=developer

Compiled Assets

Due to Magento 2 “compilation” of interceptors and views, I frequently ended up with Magento running out of date code. Having to work with this type of issue was not entirely unforeseen, but it took a few tries to figure out the exact directories to flush. For back end compiled assets (such as interceptors), deleting the contents of var/generation will cause them to be regenerated. For front end assets (such as LESS files), var/view_preprocessed should be flushed.

Where to go from here

As mentioned earlier, Magento 2 is not only an updated framework but also an opportunity to rethink existing solutions. Although this module provides value as a straight port, there is room to improve its adherence to Magento 2 conventions.

For example, in my opinion, the admin UI should be rethought — the tiny icon looks out of place on M2 and having all the content in the last store config field column doesn’t work as well, especially considering the semi-responsive nature of the M2 admin.

Another possible step forward, if the UI is updated to be more consistent with Magento 2 admin, and given the fact that Magento is accepting community contributions via GitHub, is that this module might be worth porting it to the core and issuing a pull request to be included in Magento 2 core.

Where to get it

Interested in using one of these modules or just want to review the entire source code? They’re freely available and liberally licensed at the following GitHub links.

Use it wisely!

Making Your Magento Code More Portable

We all know we should be writing portable code. Functionality that’s reusable is sure to pay off in efficiency gains, and a portable code is also naturally clearer, better organized and easier to maintain.

In agency or in-house development of a Magento site, the “full build” cycle makes it easy to fall into a pattern where function and presentation are treated as one entity. Feature code gets peppered throughout one particular theme, ad-hoc modules become unwieldy as unrelated bits of functionality are bolted on over time, and the application becomes over-reliant on assumptions about the site’s specific use cases. And sure enough, when a particularly slick feature becomes relevant to another site down the road, it’s a chore to hunt down all the components that made that feature tick.

A scenario like this comes about because of a “hurry and get it done” mindset, but keeping code more separated, reusable and maintainable doesn’t have to mean a ton of extra time. The following tips can make a big difference with very little extra effort once you’ve practiced them. They’re hardly revelatory, either. In fact, you probably see them all the time in third-party extensions – otherwise known as the scenario where portability is mandatory. The most important effort required is simply a little forethought.

The “all one piece” approach

As a very quick example of the familiar pattern that often tangles theme and functionality, let’s envision a hypothetical site build. Incorporated into the prescribed theme are two features of note: Support for showing a YouTube or Wistia embedded video in the media gallery on the product detail page (using information, like thumbnails, pulled directly from the video service), and custom badges shown on the product listing page.

As we work on the theme and these features together as one, our code base starts to grow, and our end result more often than not is going to end up looking something like this:

app/
  code/
    local/
	  MyCompany/
        MyStore/
  design/
    frontend/
      mystore/
        default/
          layout/
            local.xml
          template/
            ...
            catalog/
              product/
                list.phtml
                view/
                  media.phtml

I’ve imagined that the various blocks, helpers and observer methods we need for our videos and product badges have been lumped together in a single module (MyCompany_MyStore). This would be a natural outgrowth of having added a method here and there as we fleshed out the site. Also natural is the fact that the additions to the theme structure itself ended up in direct overrides of the key templates, sitting alongside no doubt numerous other templates in the theme. All necessary layout changes occur within the theme’s local.xml file, and of course, while it’s not shown, any necessary styles have been incorporated into the theme’s main styles.css.

Both pieces of functionality likely rely on product attributes. For the videos, this might be a text attribute to hold one or more embed codes. For the badges, a multi-select attribute with pre-defined badge text. So for an extra wrench in the works, we’ll assume we created these attributes through the easy admin interface.

Looking at the list of files above, there are certainly no clues about where the key logic for the video feature resides. If we want to track that down, we’ll have to start looking in the code itself.

To arrive at a clearer and more portable structure, we need to go back to before development started and ask the important question: What constitutes merely part of our theme, and what constitutes functionality?

The age-old maxim: Separation of concerns

That question may seem obvious, but it takes practice to make it a part of your mindset each and every time you sit down to scope a piece of development. If we ask what parts of our hypothetical site design are “theme” components only, we would quickly identify that our two noted features – videos and product badges – don’t fit the bill.

With this mindset from the beginning, instead of lumping our engineering code together, we create two separate modules specifically for these pieces of functionality: MyCompany_Videos and MyCompany_Badges. The former likely contains a model for fetching info from YouTube or Wistia and a product save observer to do so at the right time, and the latter probably has some helper methods to cleanly process product attributes into the appropriate output. Both certainly contain blocks.

With this dead simple first step, we’ve actually already worked wonders for our code organization. Even if we went no further, we at least have a starting point for tracking down our feature code at a glance.

The caveat here is that you can certainly take this approach of a separate module for every feature too far. We don’t want to end up with 30 modules containing a single helper method each! Grouping small bits of functionality into a catch-all module is just fine. (The simple features in our example, in fact, ride that line in their present form, but we’re running on the assumption that their reach would be broader in the real world.) It’s the kind of judgment call that becomes easier and more intuitive the more you ask the questions of separation of function and presentation.

Theme considerations

It’s in our theme files where the rubber meets the road when it comes to separating features, and there are more challenges to be found here than in the first step.

Simple separations

For starters, I’ll suggest that truly feature-focused code shouldn’t go in a site-specific theme at all. If at all possible, it should be placed in a universal location in the fallback scheme: base/default, or rwd/default if the files extend or rely on Magento’s responsive theme. If we’re building a feature like embedded video on the product detail page, why should this be dependent on any particular theme? By locating our files somewhere more global, we ensure our functionality is available on any store we add to our Magento install, without the need for cherry-picking and copying components from theme to theme. More sneakily, it horses us to start asking other questions. Without a specific theme to work in, where do our changes go?

The simplest answer is for layout files. We already have different modules defined for our distinct features; those modules can define their own layout files as well. So now, rather than functionality-related layout additions sharing space with general theme changes in local.xml, we have videos.xml and badges.xml.

Templates

For template files, things get trickier. We should start by moving our logic into separate, brand new templates wherever possible. The core theme provides a decent number of text list blocks (defined in layout with type “core/text_list”) that will assist with this; children of these block types will be output automatically. In our case, though, we’re almost certainly going to have to override the markup of two core templates: the product list template (for outputting our badges), and the media template (for outputting our video).

One strategy is to use layout XML to change the template path of the right block. (<action method=“setTemplate”><path>file.phtml</path></action>) This is a fine solution for smaller, little-touched templates. It’s problematic for many others, though. If we change the path of the product list template, we’ve certainly accomplished the goal of making it clear where our feature code resides, but at the cost of maintainability of the theme. If another developer inherits our code and goes looking for the right place to tweak the product list template, he or she might be in for some frustration.

It’s notable that, if we’re extending Magento’s responsive theme, quite a few more text list blocks have been added that could assist with our problem. For example, we may find that the responsive-specific “product_list.name.after” block could be just right for placing our product badges without touching the product list template. In fact, we could pair this with some DOM manipulation in JavaScript to move the content exactly where we want it – say, before the product name instead of after it. (Don’t go crazy with this last technique. In the example I just mentioned, it’s only because the content has already been placed almost exactly where it’s needed that the semantics of the page or maintainability of the code don’t suffer greatly.)

If all else fails, go ahead and use template overrides in your theme to contain the appropriate feature logic. Or try this approach on for size: Splash some more text list blocks right where you need them in your theme, and then make use of them to add content using module-specific template files. Yes, this strategy still depends on a particular theme in order for the functionality to work. But the footprint is as small (and generic) as possible, while the main event is still contained in theme files specific to the feature.

Dependent assets

For JavaScript and CSS, it’s fairly simple to keep feature-related code separate from theme. The only sticky question is whether the size of a particular piece of functionality is truly sufficient to warrant a separate JS or CSS file since these will increase the number of HTTP requests necessary for page loads unless you are enabling file merging for these types of files.

It’s worth noting, though, that Magento’s responsive theme and its adoption of CSS compilation with Sass/Compass make code organization a lot easier without necessitating separate final CSS files. In this new paradigm, styles.css is compiled from the contents of several “partial” files grouped by concern. Thanks to this structure, keeping the styles related to a particular feature cleanly separated is as simple as adding a new partial (e.g., skin/frontend/mystore/default/scss/module/_badges.scss) and importing it within styles.scss. Learn more about making Sass part of your toolset in Magento’s knowledgebase.

As a final note, a truly separate CSS file for a feature does carry the benefit of being able to reside in a fallback theme as prescribed above, and thus free of dependence on a specific theme. This is obviously more relevant for structural CSS than cosmetic, and it again comes down to a judgment call about the complexity of the feature.

Keep it in the code

Our example features rely on a couple of different product attributes, which I made a point to mention might easily be added in the admin. Admin attribute management is a powerful tool for merchants to add to their catalog content without needing to touch code. Creating attributes for layered navigation, product comparison, or the specs list on the product detail page are great uses of this interface.

When a piece of site functionality relies on a particular attribute, however (as in the case of our video embed code and badge text attributes), resorting to admin-entered content is a poor solution. What if one particular attribute is missed when migrating a feature from a staging environment to production? What if its definition isn’t consistent between the two? The clarity and reusability of our feature code also suffer dramatically, as the necessary attributes can only be discerned by finding where they are used. (And even then, their exact definitions can only be guessed at.)

This kind of attribute belongs in the code with the rest of the feature, and the proper place is in a module install/upgrade script. You can find a plethora of examples in the Magento core code. Upgrade scripts are located in the sql directory of a module, and to work the setup resource that matches their directory name must be defined in a node like “global/resources/*/setup” in etc/config.xml. The class Mage_Catalog_Model_Resource_Setup is necessary for creating product attributes, accomplished within the upgrade scripts with the addAttribute method.

This technique is likely to be intimidating to you if you are unfamiliar with it, but it’s a vital step in writing reliable and maintainable feature code. With a little practice and some examination of the core codebase’s use of addAttribute, it will become old hat quickly. (Examine the _prepareValues methods of Mage_Catalog_Model_Resource_Setup and its ancestor Mage_Eav_Model_Entity_Setup for an instant digest of the configuration values you can set on any product attribute.)

The same principle can apply to any piece of content your functionality expects to be present, including CMS static blocks or custom variables. (And don’t dare hard code a product or category ID. Create a System Configuration value to capture such information. More on System Config below.) Any kind of content can be created in an upgrade script, and if the code relies on it, the code is where it belongs.

NOTE: If you want to create CMS content that your feature relies on, the appropriate kind of upgrade script is actually a data upgrade script, which shares the same setup resource definition in config.xml but reside in the data directory instead of sql. The main difference is that the Magento application is fully bootstrapped by the time data upgrade scripts are run, and therefore you can use standard model logic to create and save your content.

Can you turn it off?

For this last section, think about the following question: What if we ever need to remove a site feature? Can it be done easily, or will an attempt to delete a few lines of code lead to a hunt for all the little references and dependencies that are now making everything blow up?

I suppose the answer to the question could itself be a testament to how portable the code was written. Regardless, why not make it possible to enable/disable functionality on our site without touching the code at all? We’ve gone this far in making our feature code as self-contained as possible; why not go the extra mile and add an on/off switch? It’s almost absurdly easy to do so.

You’re no doubt familiar with the System Configuration area of the Magento admin, where a host of site settings live. What our video and product badge modules need is a couple of Yes/No values in this admin section to control whether these features are enabled or not. If you’re unfamiliar with creating such values, there’s nothing to fear at all. A simple XML structure in etc/system.xml of any module defines them, and opening a few such files in the core codebase will demystify the process swiftly. Slightly more effort is required if you want to create a System Configuration section of your own rather than adding to an existing one since these sections carry specific admin user permissions. But even this only requires examining one more type of module config file – etc/adminhtml.xml – to get a handle on.

Once these values are defined, it’s a simple matter of calling Mage::getStoreConfig in the appropriate entry points of our modules to check whether the features are enabled. (Any calls to block methods in layout XML, such as setTemplate, can also use the “ifconfig” property to make their execution conditional on such values.)

The hidden benefits of portable code

A final snapshot of our hypothetical codebase might look something like this:

app/
  code/
    local/
      MyCompany/
        Badges/
        Videos/
  design/
    frontend/
      rwd/
        default/
          layout/
            badges.xml
            videos.xml
          template/
            badges/
              catalog/
                product/
                  badges.phtml
            videos/
              catalog/
                product/
                  view/
                    media.phtml
  skin/
    frontend/
      mystore/
        default/
          scss/
            ...
            module/
              _videos.scss
              _badges.scss
            styles.scss

In this example, we’ve opted to use setTemplate in our layout file to change the location of our product media template, accounting for the new one under the “videos” namespace. We’ve managed to avoid any template overrides with product badges, though, with this feature making use of an entirely new template. And naturally, our new modules contain appropriate install/upgrade scripts to set up required product attributes, as well as defining System Config values for enabling these features.

The immensely improved new structure makes it crystal clear where to find the code related to certain features, and pulling out those features for use elsewhere will be so much easier. Even if we never have occasion to port our code to other sites, the greater maintainability is sure to pay dividends.

Those are the benefits at the development level, and yet they’re not even the only benefits. Throughout this article, I’ve repeatedly mentioned the required mindset of separating theme from function, and this is a cyclical process. Thinking about your project in this way will result in better-organized code, and consistently writing better code will come right back around and improve your way of thinking about requirements. In the terms of Agile methodology, the codebase structure I outlined at the beginning likely came out of a project backlog with simple “product list page” and “product detail page” tasks. A developer practiced in separating form from function would quickly recognize that the video and product badge features deserve to be elevated; they should be talked about, prioritized, and developed independently from the theme. With a routine mentality like this, you’ll deliver working code faster, you’ll drive better conversations with your client or project owner, and you’ll realize a much better foundation for expanding your features in an organic way.

Agile Software Development at Classy Llama

What is Agile?

Agile has become a very common term within the software development industry. So what is agile? Some of the concepts of agile software development have been around from the early days of software development but as a cohesive concept, agile software development was defined by the creation of a very short document called The Agile Manifesto included below.

“We are uncovering better ways of developing software by doing it and helping others do it. Through this work we have come to value:

Individuals and interactions over processes and tools

Working software over comprehensive documentation

Customer collaboration over contract negotiation

Responding to change over following a plan

That is, while there is value in the items on the right, we value the items on the left more.

Kent Beck Mike Beedle Arie van Bennekum Alistair Cockburn Ward Cunningham Martin Fowler James Grenning Jim HighsmithAndrew Hunt Ron Jeffries Jon Kern Brian Marick Robert C. Martin Steve Mellor Ken Schwaber Jeff Sutherland Dave Thomas

© 2001, the above authors this declaration may be freely copied in any form, but only in its entirety through this notice.”

The Agile Manifesto promotes the importance of individuals and interactions, working software, customer collaboration, and responding to change. These tenets of agile software development define the goals and intentions of using an agile methodology, but they are very non-specific about how to go about achieving these goals. There are numerous methodologies that different individuals, groups, and companies have come up with to document a specific implementation of a set of rules to accomplish the goals of the Agile Manifesto. Some of the most prominent implementations are probably recognizable to anyone who has heard about Agile before including Scrum, Kanban, Extreme Programming, Adaptive Software Development, and others. There are also numerous agile concepts that cross multiple implementations of agile software development including backlogs, sprints, cross-functional teams, iterative and incremental development, pair programming, planning poker, user stories, retrospectives, velocity tracking, and much more. The important thing to remember when you hear that someone is “using agile,” is that their goals should be centered around individuals and interactions, working software, customer collaboration, and responding to change.

Classy Llama’s Implementation

Because of the wide variety of practices surrounding agile software development, there is a great deal of variety and inconsistency about what a company means when they say they build software using an agile methodology. To combat confusion and help answer the question of “what does Classy Llama mean when they talk about agile?”, I am going to document a number of the most important pieces of our implementation of agile software development. I will not be discussing every single thing that we at Classy Llama do to implement agile nor will this blog post always match what we do on every project, but my goal is to document some of the things that we at Classy Llama prioritize and emphasize when doing software development.

As we endeavored to implement agile as a company, we found that many of the traditional agile training, books, and coaching available were designed for people building a product instead of those doing very targeted custom development for a broad range of clients. This has caused us to implement pieces of agile whenever possible while being very open to the possibility that a particular brand of agile development may not work for our clients. It would even be fair to say that our implementation of agile has been an agile process. One of the things that were really challenging at first was that we did not have a comprehensive set of guidelines handed to us as we switched from waterfall to agile project management. We simply began following some agile processes, and as people mastered those, we introduced a few more, and a few more, and a few more. This has at times led to resistance from people who prefer to have everything well documented and clear before doing anything. Ultimately though, I am proud to say that our transition to agile went very smoothly.

Below, I detail the agile practices that are most closely linked to the day to day process of working with the project management and development teams and moving your project forward. I may in a future blog post detail some of the methodologies that we use to track a project’s progress vs. the plan and how we learn and build our process iteratively from project to project.

Backlogs and Sprints

Probably the most client visible agile practices that we use are backlogs and sprints. A backlog is a prioritized list of features. It should encompass all features that are needed to complete the project at any given time. Estimates and details associated with these features help us to have conversations about priority and planning for the project. This backlog gains definition as a project moves forward. Initially, this backlog starts with a list of items taken directly from a proposal which was delivered to a client merged with a list of the basic tasks that every website build needs in order to launch successfully. This often ends up being as much as 70% or more of what the backlog will contain over the course of the project. It is important to understand, that this backlog is not a fixed list. It changes as tasks gain more definition (splitting one task into multiple), as new tasks are discovered, as tasks are completed, and as tasks are deemed unnecessary. The goal of using this backlog is to have a central place to document all of the features that a project will include. We use the backlog to sort things by priority and have a central place to pull development tasks from as well as to track the estimated amount of time remaining on the project against the budget for the project.

Sprints are inextricably linked to the backlog. Without sprints, the backlog would only grow in size and definition. This is where one of the key differences between a waterfall and an agile methodology comes to the surface. Using agile, you could use something similar to a backlog, but you would be building it to what was considered completion before starting any development. When we introduce sprints, the backlog is growing, changing, and resorting while we are completing items contained within it. A sprint is an instance of an agreed upon period of time that is designated for completing the next most important features from the backlog. This means that if Classy Llama and a given client agree that two-week sprints will be best for moving a project forward, in the first sprint, we will spend two weeks working on the most important items for the completion of the project that we have sufficient information to work on. These items will be pulled from what is considered the top of the backlog and will be put into the sprint during the sprint planning meeting which proceeds the sprint. So if we are going to be doing twelve days of development (between all developers) during the first two-week sprint, we will put as many of the items on the top of the backlog into the sprint as we estimate will fit into twelve development days.

User Stories

We have talked about the backlog and sprints, both of which are made up of tasks. A task is a single item which can be identified and estimated independently that corresponds to a feature, bug fix or another work item. The user story is one tool that we use to clearly define a task’s goal and purpose. A user story is defined as a sentence with three major components. The components of the user story, in order, are the who, the what, and the why. These three components are placed into a template to create what we know as a user store. “As a[n] ____________, I want ____________ so that ____________.” For example, say there is a situation where a merchant wants to capture a customer’s employee identification number to allow verification of an employee’s status as a purchasing representative for the company. A potential user story to describe this situation would like something like this: “As a customer, I want to be able to easily verify my identity on the website so that I do not have to call or email identification before I can purchase items on the website.” After seeing the previous user story, you may be thinking that you were expecting it to talk from an entirely different perspective. The same situation can absolutely be described from different perspectives. Another user story representing the same situation might be: “As a website administrator, I want to be able to easily gather employment verification information so that I can quickly verify employment status without having to take additional, unnecessary steps.” Interestingly, both of the above user stories have told a relatively similar story, but there is one key difference between them. In the first user story, it is implied that the customer will likely be the one who would have to call or write providing verification before they can shop. However, the second story indicates that the burden of verification would typically fall on the website administrator. There are situations where both of the user stories may be helpful in accurately describing the purpose for this feature. There is at least one other distinct user story that could be told about this task that would drastically alter the context around the task. “As a manager, I want to be able to easily see information about the employees that are allowed to purchase items on my company’s behalf, so that I can appropriately track and administer employee activities.” In this user story, the context is totally different. This is no longer a field used to simply pass information back and forth between the customer and website administrator. There is now a company administrator that is likely involved in the verification process directly on the website. This user story may be related to a larger user story that talks about a larger set of features being developed like: “As a manager, I want to have a portal to track my employee’s use of this website’s purchasing features so that I can approve, track, and manage their activities.” User stories are a tool that not all of our clients use but can be very helpful for the client to be able to communicate context to us or for us to be able to demonstrate an understanding of the context for a task to our client.

Conclusion

Agile is a huge topic and not something that I can cover in a single blog post. I have talked about how agile is largely a mindset around software development, how everyone using agile means something slightly or drastically different when using the term, and how we use backlogs, sprints, and user stories to organize and communicate with our clients what the most important things on their project are. The most important reasons we choose to use agile are (1) to increase visibility and (2) to simultaneously decrease risk for our clients and our company. We have found that agile has helped us to do this by eliminating unnecessary items from the project and making sure that the most important items are done first. By giving our clients the tools to guide the direction of their project as the needs of the business change and are clarified over the course of the project, we have been able to build more successful projects and come in on budget and on time more consistently and reliably.

Contact Us