Areas
What frontend, adminhtml, webapi_rest, webapi_soap, graphql and crontab change about configuration, routing and design.
$this->state->emulateAreaCode(\Magento\Framework\App\Area::AREA_FRONTEND, $callback, $params = [])An area is a named mode of the application. It decides which configuration files load, which routers run, which design and translations apply, and therefore which of your classes exist at all. A module ships one global etc/ folder plus one folder per area it takes part in.
The areas in 2.4.9
| Code | Front name | Serves |
|---|---|---|
frontend | none; the fallback | the storefront |
adminhtml | backend/frontName from app/etc/env.php, usually admin | the admin panel |
webapi_rest | rest | REST endpoints |
webapi_soap | soap | SOAP endpoints |
graphql | graphql | the single GraphQL endpoint; area added in 2.3.0 |
crontab | none | scheduled jobs run by bin/magento cron:run |
global | none | not a request area: configuration with no area folder, applying everywhere |
How the area is chosen
For an HTTP request, Http::launch() takes the first URL segment and asks AreaList::getCodeByFrontName(). The admin front name, rest, soap and graphql are claimed; everything else falls through to frontend. The code is then fixed: State::setAreaCode() throws Area code is already set if anything tries to change it.
What an area changes
Configuration merges global first, area second, and the area file wins. etc/di.xml then etc/adminhtml/di.xml; the same for routes.xml and the rest. A class registered only under etc/adminhtml/ does not exist as far as the storefront is concerned, which is the usual explanation for code that works in the admin and silently does nothing on the storefront.
Example
<!-- app/code/Vendor/Pricing/etc/adminhtml/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Vendor\Pricing\Api\LabelSourceInterface"
type="Vendor\Pricing\Model\Label\AdminSource"/>
</config>When to use it
- Use one implementation on the storefront and a different one in the admin.
- Work out why a configuration file you added has no effect on REST requests.
- Explain why a cron job cannot render a template that a page renders fine.
- Track down an "Area code is not set" error from a command that works in the browser.
More examples
The global half of the same preference
Read this file together with the adminhtml one above. Magento loads the global configuration first and then the area's, so a storefront, REST or cron request resolves LabelSourceInterface to StorefrontSource, and an admin request resolves it to AdminSource. Putting the common choice in etc/di.xml and only the difference in the area file keeps the override small and visible. Both files apply after bin/magento cache:flush in developer mode; in production mode run bin/magento setup:di:compile first, because the per-area dependency injection configuration is baked during compilation.
<?xml version="1.0"?>
<!-- app/code/Vendor/Pricing/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Vendor\Pricing\Api\LabelSourceInterface"
type="Vendor\Pricing\Model\Label\StorefrontSource"/>
</config>One file name, two areas, different meanings
routes.xml is the clearest case of an area changing what a file means: the storefront copy registers into the standard router, the admin copy into the admin router, and a route declared in the wrong folder produces a 404 with no error anywhere. The area folder also decides which controller directory is reachable. There is no etc/webapi_rest/routes.xml or etc/graphql/routes.xml, because those areas do not use routers; REST paths come from webapi.xml and GraphQL from the schema. Apply either file with bin/magento cache:flush.
<?xml version="1.0"?>
<!-- app/code/Vendor/Blog/etc/frontend/routes.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="blog" frontName="blog">
<module name="Vendor_Blog"/>
</route>
</router>
</config>
<!-- app/code/Vendor/Blog/etc/adminhtml/routes.xml declares the same module
under router id="admin" instead, and those controllers live in
Vendor/Blog/Controller/Adminhtml/ -->How a front name becomes an area
This is worth reading rather than copying. Each area contributes one item to AreaList, and the front name is matched before any router runs, which is why a URL beginning with /rest never reaches the standard router and why no module can claim rest, soap, graphql or the admin front name for a route of its own. Magento_Store registers frontend with a null front name and marks it the default, so unclaimed URLs land there. Declaring a new custom area is possible but rarely the right answer: an area brings its own configuration load, design and routing, and almost every real requirement is met by adding files to an existing one.
<!-- vendor/magento/module-webapi/etc/di.xml (core, trimmed) -->
<type name="Magento\Framework\App\AreaList">
<arguments>
<argument name="areas" xsi:type="array">
<item name="webapi_rest" xsi:type="array">
<item name="frontName" xsi:type="string">rest</item>
</item>
<item name="webapi_soap" xsi:type="array">
<item name="frontName" xsi:type="string">soap</item>
</item>
</argument>
</arguments>
</type>
Discussion