Categories


Archives


Recent Posts


Categories


Taming Magento 2’s Scope

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

This is another one of those 10,000 foot view posts, as much to get my own head straight about something as anything else.

In programming, the term scope usually means

If I’ve defined variable X “here”, where else can I access it?

Magento’s core team have borrowed the term scope to described Magento’s multi-store features.

Websites, Store Groups, and Store Views

Magento allows you to create CRUD objects called “Websites”. A Website is a simple object and has two main pieces of data – a unique alphanumeric code for identification, and a reference to a default “Store Group”.

A “Store Group” is another CRUD object. It is similarly simple, and two two primary pieces of data – a default category ID, and default Store View.

A “Store View” is a third CRUD object. Like Websites, Store Views have a unique alphanumeric code for identification. Store Views can also be enabled and disabled.

These three objects form a hierarchy.

Website A
    Store Group B
        Store View D
        Store View E            
    Store Group C
        Store View F
        Store View G
Website B
    //..etc

In Magento 2, the CRUD data for each object type is stored in the following tables

Website:     store_website
Store Group: store_group
Store View:  store

Unfortunately, the code, UI, documentation, and community of developers around Magento will throw around the term “Store” a lot – sometimes referring to Store Groups, other times referring to Store Views. Without enough context, this can be confusing.

Magento also has two “run modes” – a Store run mode, and a Website run mode. When you’re running Magento in “Website” run mode, you bootstrap the system with a specific website code – that’s the alphanumeric code from the CRUD object. When you’re running Magento in “Store” run mode, you’re bootstrapping the system with a specific Store View code. It would probably better to call this “Store View run mode” – but legacy terms are legacy.

Magento 1 vs. Magento 2

Magento 1 has a core/store object with a store_id (that’s Store View id). When Magento is in “Store” run type mode, this object is instantiated using the passed in Store View code. When Magento is in “Website” run type mode, the system instantiates this object by looking at the Website’s default Store Group, and then that Store Group’s default Store id.

Note: in both modes, its possible to switch the system to a different Store View by passing in ___store query string parameter with the new Store View id. This value gets saved to a cookie, and future requests will reference this cookie when loading stores.

Magento 2 has a MagentoStoreApiStoreResolverInterface type/interface, with a concrete implementation class of MagentoStoreModelStoreResolver. This object has a getCurrentStoreId method that contains logic similar to Magento 1’s core/store object, and will figure out what the current store ID is based on the run mode, you system settings, the ___store parameter, and any saved values in your browser cookies.

What all this Does

Every bootstrap of the Magento system ends up having a Website ID, Store Group ID, and Store View ID. These IDs impact how other parts of the system behave.

For example – the store group ID controls which categories are displayed to the end user.

Another example – the Stores -> Configuration section allows you to change configuration values based on website ID, store group ID, and Store View ID. This is where a lot of Magento’s multi-store power comes from. Also, for the historically minded, the “Website” run type mode exists because, historically, there were some configuration settings whose values could only be set at the Website level.

Magento 2 Scope

By itself, Magento 2’s scope resolver is a good thing. However, where Magento 2 loses the plot a bit is whenever some other class or system needs to know the current website, store group, or store id, developers introduce a hierarchy of objects with “Scope” in the name. Scope refers not to PHP variable scope, but to the website, store group, or store scope.

For example, to access the Store Config (formerly System Config) variables in Magento 2, you inject a MagentoFrameworkAppConfigScopeConfigInterface type, which currently resolves to the concrete class MagentoFrameworkAppConfig. This MagentoFrameworkAppConfig object uses a MagentoFrameworkAppConfigScopePool object. This ScopePool object uses a MagentoFrameworkAppScopeResolverPool object, and the ScopeResolverPool has a number of resolver objects injected via automatic constructor dependency injection

<type name="MagentoFrameworkAppScopeResolverPool"><arguments><argument name="scopeResolvers" xsi:type="array"><item name="store" xsi:type="object">MagentoStoreModelResolverStore</item><item name="stores" xsi:type="object">MagentoStoreModelResolverStore</item><item name="website" xsi:type="object">MagentoStoreModelResolverWebsite</item><item name="websites" xsi:type="object">MagentoStoreModelResolverWebsite</item></argument></arguments></type>

These resolver object inject a MagentoStoreModelStoreManagerInterface type/interface, which currently resolves to a the concrete class MagentoStoreModelStoreManager. Finally, this StoreManager object injects the MagentoStoreApiStoreResolverInterface type/interface we mentioned earlier.

Compare this to Magento’s URL type/interface, which resolves to the concrete class MagentoFrameworkUrl. This object uses the MagentoFrameworkUrlScopeResolverInterface type/interface, which resolves to the concrete class MagentoFrameworkUrlScopeResolver. This ScopeResolver class uses the type/interface MagentoFrameworkAppScopeResolverInterface, which resolves to the class MagentoStoreModelResolverStore. This MagentoStoreModelResolverStore class injects a MagentoStoreModelStoreManagerInterface which, as we mentioned previously, eventually ends up using a MagentoStoreApiStoreResolverInterface.

There are other classes and systems that need to know the Website, Store Group, or Store View IDs, and these classes and systems use different class hierarchies with “Scope” in their name. There doesn’t appear to be any overarching philosophy to these systems. My best guess is they’re the result of a junior or intermediate developer showing their code to an architect and that architect saying “Not Abstract Enough”. If there’s is a logic to this system, I’d be happy to hear it.

This endless abstraction usually comes from programmers with not enough to do and no clear goals. With an endless number of software engineers this wouldn’t be a problem, but in the real world the more code you’re working with, the greater chance of a bug or incorrect assumption slipping into the system.

The good news is with Magento 2 finally shipping code that people are using, this endless splitting of the abstraction atom should begin to taper off as the core team starts to learn their decisions and code impact thousands of other systems.

Copyright © Alan Storm 1975 – 2019 All Rights Reserved

Originally Posted: 28th June 2016