Magento2 Stores and Scopes

Update Oct 2015: This version is outdated as the store package has been refactored between 0.74.0-beta4 and 1.0.0-beta.

See updated post here.

Exploring the Store package and its init process with scopes in Magento2 (0.74.0-beta4) and Magento1 (1.9 CE).

A quick overview of some demo stores on how it looks in the Magento2 backend. The confusing name Store of the middle column should be called Store Group.

Graphical view of websites and stores

Stores

Overview of the models and database tables. Each of the model is also its own scope. In addition there is also the default scope. In total: 4 scopes.

MagentoBackend LabelWebsiteStoreGroupStore View
v1Model ClassMage_Core_Model_WebsiteMage_Core_Model_Store_GroupMage_Core_Model_Store
v1Database Tablecore_websitecore_store_groupcore_store
v2Model ClassMagento\Store\Model\WebsiteMagento\Store\Model\GroupMagento\Store\Model\Store
v2Database Tablestore_websitestore_groupstore

Relation view of the three tables:

Store Table relations

Important: Table store has a unique key on column code. Why that is important will be explained later.

Scopes

app/code/Magento/Store/Model/ScopeInterface.php defines these scope types:

const SCOPE_STORES = 'stores';
const SCOPE_WEBSITES = 'websites';

const SCOPE_STORE   = 'store';
const SCOPE_GROUP   = 'group';
const SCOPE_WEBSITE = 'website';

where as lib/internal/Magento/Framework/App/ScopeInterface.php defines the default global scope:

const SCOPE_DEFAULT = 'default';

These single scope names are mainly used to retrieve via PHP a configuration value:

SCOPE_STORE ~973x

$this->_scopeConfig->getValue('path/to/key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);

$useCategoryUrl = $this->_scopeConfig->getValue(
    \Magento\Catalog\Helper\Product::XML_PATH_PRODUCT_URL_USE_CATEGORY,
    \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
    $this->getStoreId() // can be either a store_id or store_code
);

$this->scopeConfig->isSetFlag(
    Url::XML_PATH_CUSTOMER_STARTUP_REDIRECT_TO_DASHBOARD,
    ScopeInterface::SCOPE_STORE
);

SCOPE_GROUP ~9x

Mostly used in these switch statements and more often in unit tests.

switch ($type) {
    case \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE:
        $this->_loadWebsiteCollection();
        break;
    case \Magento\Store\Model\ScopeInterface::SCOPE_GROUP:
        $this->_loadGroupCollection();
        break;
    case \Magento\Store\Model\ScopeInterface::SCOPE_STORE:
        $this->_loadStoreCollection();
        break;
    default:
        break;
}

SCOPE_WEBSITE ~63x

$this->config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE);

Used all over the place in different szenarios.

The plural scope names and default are used to handle the SQL statements for table core_config_data together with the scope_id (= store_id).

core_config_data table

Unique key on: scope, scope_id and path.

Scope Matrix

 DefaultWebsiteStore GroupStore View
URL rewrites   X
Product settingsX  X
Product pricesXX  
Product tax classXX  
Base currencyXX  
(Default) display currencyX  X
Category settingsX  X
System configuration settingsXX X
Root category configuration  X 
Orders   X
CustomersXX  
CMS PageX  X
CMS BlockX  X
Checkout AgreementsX  X

Inspired from Aoe_ManageStores.

StoreManager

With Magento2 there has been introduced the Magento\Store\Model\StoreManager which is wrapper abround the Magento\Store\Model\Storage\Db or \Magento\Store\Model\Storage\DefaultStorage. The DefaultStorage will only be used in testing.

The StoreManager also defines the MAGE_RUN_CODE and MAGE_RUN_TYPE environment variables which are used in Nginx or Apache config to force set a specific website, store group or store view to a domain/path/etc. In PHP (especially in the index.php) it can be written like (don’t do that in the core file):

$_SERVER[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = 'website_code|group_id|store_code';
$_SERVER[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website|group|store';

Yes you can even set the group_id in MAGE_RUN_CODE when MAGE_RUN_TYPE is group.

StorageFactory

With Magento2 there has been introduced the Magento\Store\Model\StorageFactory which is only used within the StoreManager and instantiates the Storage\Db or Storage\DefaultStorage depending on website code, group id or store code.

StorageFactory responsibilities are detecting which website/store to load and to reinit the collections of websites, store groups and stores.

Errors during init process when a RUN_CODE or RUN_TYPE string cannot be found:

  1. \Magento\Store\Model\StorageFactory:_reinitStores(): Store Manager has not been initialized properly.
  2. \Magento\Store\Model\Storage\Db::getStore(): Store Manager has been initialized not properly.

Confusing messages… 😂 and its meanings are:

  1. The scope type (store, group, website) cannot be found. Mostly a typo in $_SERVER['MAGE_RUN_TYPE']. See all options in the scopeType switch.
  2. The call getStore() in the Storage\Db class returns null, no (default) store found.

The scopeType switch (in 1.) sets the store depending on the scopeCode which can be website code, group id or store code.

If all scopes and stores have been found the current store can be overridden in the following order with:

  1. Cookie Name \Magento\Store\Model\Store::COOKIE_NAME => store. If found then setCurrentStore() will be called.
  2. Request: Either in $_GET or in $_POST (and maybe also in $_COOKIE). Name: ___store. If found then setCurrentStore() will be called if this method returns true then check if ___store is equal to the current store then check if the websites default store is equal to the __store -> cookie delete else 2x cookie create.

The Request always wins in setting a store but only if: see next.

setCurrentStore($storage, $scopeCode, $scopeType) implements besides setting the store also some checks: Prevent running a store from another website or store group, if website or store group was specified explicitly. Falls back to the default store for a website or store group.

The store code in a request parameter will be used to select a store from store table. Therefore the column code must be unique.

StoresConfig

With Magento2 there has been introduced the Magento\Store\Model\StoresConfig which is a convenience helper class to retrieve (with method getStoresConfigByPath()) for a config path all config values for each store view. Only used in Customer group to check if the current group is the default group to create an account AND in \Magento\Sales\Model\Observer\CleanExpiredQuotes to clean the expired quotes.

Other

During code review I’ve found a lot of unused properties and variables in Magento2 Store module. No I didn’t send a PR …

Related posts