What You Are Building
The capstone module in one page: one entity, an admin grid, a storefront page, REST and GraphQL, and tests.
app/code/Acme/Locator/{registration.php, etc/module.xml, Api, Model, Controller, view, Test}The capstone is one module, Acme_Locator, that stores pickup locations and shows them in three places: the admin, the storefront and the API. A module is a directory under app/code/Vendor/Name with a registration.php and an etc/module.xml. Nothing in Magento loads any other way.
The five pieces
- Data: a table in
db_schema.xml, the model trio, a data interface and a repository. - Admin: a UI component grid and a form, an ACL rule, a menu entry.
- Storefront: a route, a controller, layout XML, a view model, a template.
- API: a REST route in
webapi.xmland a GraphQL query over the same repository. - Tests: one unit test, one integration test, one API functional test.
Decide the names first
Four names are expensive to change later, because each ends up in a table, a URL or an API path: the module name Acme_Locator, the table acme_locator_location, the storefront front name locator, and the ACL resource id Acme_Locator::location. Choose them now and keep them identical everywhere.
Order of work
Data first. The grid, the storefront page and both APIs read the same repository, so building any of them before the repository exists means writing it twice.
Edition and commands
Every file in this stage is Magento Open Source 2.4.9 on PHP 8.4. Nothing here needs Adobe Commerce. Once the two files exist, run bin/magento module:enable Acme_Locator and then bin/magento setup:upgrade. The CLI accepts unambiguous abbreviations: bin/magento s:up is the same command as bin/magento setup:upgrade.
Example
<!-- app/code/Acme/Locator/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Acme_Locator">
<sequence>
<module name="Magento_Ui"/>
<module name="Magento_Backend"/>
<module name="Magento_Webapi"/>
<module name="Magento_GraphQl"/>
</sequence>
</module>
</config>
<!--
What this declares: the module exists, and its configuration and data
patches are processed after Magento_Ui, Magento_Backend, Magento_Webapi
and Magento_GraphQl. Load order is the only thing <sequence> controls;
it is not a list of Composer requirements.
module.xsd still allows a setup_version attribute, but 2.4 modules leave
it out: declarative schema and data patches replaced the install and
upgrade scripts it used to version. It is not ignored if you add one.
Magento compares it with the module's row in setup_module and reports
the database as out of date until the two match.
Apply it with:
bin/magento module:enable Acme_Locator
bin/magento setup:upgrade
-->When to use it
- Scope a custom entity for a client so the admin grid and the API are designed in, not bolted on later.
- Fix the module name, table name, front name and ACL id once, before they appear in a URL and a migration.
- Use the file tree as a checklist when reviewing somebody else's module for missing pieces.
More examples
registration.php, the file that makes the module exist
Magento finds modules by running every registration.php it can see, not by scanning directories for classes. The name here, Acme_Locator, is the one used by module:enable, by the <sequence> entries of other modules, and by the Acme_Locator:: prefix on ACL resources and template paths. If this file is missing, the module is invisible and no error is printed.
<?php
// app/code/Acme/Locator/registration.php
declare(strict_types=1);
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Acme_Locator', __DIR__);Enabling it and reading the output
Two commands, two different jobs. module:enable records the module in app/etc/config.php. setup:upgrade is the one that creates the table from db_schema.xml and runs the data patches, and the per-module lines it prints are how you confirm your module was seen. Reach for this pair after every change to module.xml, db_schema.xml or a patch class.
# run from the Magento root
bin/magento module:enable Acme_Locator
# The following modules have been enabled:
# - Acme_Locator
#
# Cache cleared successfully.
# Generated classes cleared successfully. Please run the 'setup:di:compile' command to generate classes.
bin/magento setup:upgrade
# Cache cleared successfully
# File system cleanup:
# ...
# Module 'Acme_Locator':
# Installing schema..
# Installing data..
# Nothing to import.Where each capstone file lives
In Magento the path is part of the contract. etc/di.xml applies everywhere, etc/frontend/routes.xml only on the storefront, etc/adminhtml/menu.xml only in the admin, and a UI component is found only under view/<area>/ui_component. Keep this tree open while working through the next five lessons; each one fills in part of it.
app/code/Acme/Locator/
registration.php
composer.json
etc/module.xml
etc/db_schema.xml
etc/db_schema_whitelist.json
etc/di.xml
etc/acl.xml
etc/webapi.xml
etc/schema.graphqls
etc/frontend/routes.xml
etc/adminhtml/menu.xml
Api/Data/LocationInterface.php
Api/LocationRepositoryInterface.php
Model/Location.php
Model/LocationRepository.php
Model/ResourceModel/Location.php
Model/ResourceModel/Location/Collection.php
Model/Resolver/Locations.php
Controller/Index/Index.php
Controller/Adminhtml/Location/Index.php
ViewModel/LocationList.php
Setup/Patch/Data/AddDefaultLocations.php
view/frontend/layout/acme_locator_index_index.xml
view/frontend/templates/list.phtml
view/adminhtml/layout/acme_locator_location_index.xml
view/adminhtml/ui_component/acme_locator_location_listing.xml
Test/Unit/ViewModel/LocationListTest.php
Discussion