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 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
ScopeResolver class uses the type/interface
MagentoFrameworkAppScopeResolverInterface, which resolves to the class
MagentoStoreModelResolverStore class injects a
MagentoStoreModelStoreManagerInterface which, as we mentioned previously, eventually ends up using a
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.