saudi_municipal_coding
Reference data and tooling for Saudi Arabia's MOMRAH Unified Municipal Coding — regions → cities → districts — with Arabic/English names, dependent address fields, and a central address-resolution
service.
Data comes from the yasseralsamman/saudi-municipal-coding package (13 regions, 15,513 cities, 21,235 districts).
Installation
composer require yasseralsamman/saudi-municipal-coding drush en saudi_municipal_coding -y drush smc:import # seed regions/cities/districts (idempotent)
You can also import from Configuration » Regional » Saudi Municipal Coding.
Drush commands:
Command Purposedrush smc:import
Create/update SMC terms from the dataset.
drush smc:import --purge
Wipe and re-import.
drush smc:purge
Delete all SMC terms.
drush smc:resolve <districtId>
Resolve a full address (smoke test).
The taxonomy
Three vocabularies are installed: smc_region, smc_city, smc_district. Each term stores field_smc_id (the numeric municipal-coding ID) plus parent references. Because
municipal-coding district IDs are non-composite (plain integers, not encodable), the parent hierarchy is stored, not decoded: a smc_city references its region via field_parent_region,
and a smc_district references its city via field_parent_city and its region via field_parent_region (denormalized, always the parent city's region). Term names are
translatable (English default, Arabic translation).
The AddressResolver service
saudi_municipal_coding.address_resolver is the single source of address logic. Highlights:
$resolver = \Drupal::service('saudi_municipal_coding.address_resolver'); // Full hierarchy from a district ID: $address = $resolver->resolveByDistrictId(1001); $address->fullName(); // "العليا، الرياض، منطقة الرياض" (current language) $address->region->smcId; // 1 // Lookups, hierarchy, search, validation: $resolver->getCity(3, 'ar'); $resolver->getCitiesInRegion(1); $resolver->getDistrictsInCity(3); $resolver->getCityOfDistrict(1001); $resolver->getRegionOfDistrict(1001); $resolver->searchCities('Riy'); $resolver->isValidHierarchy(1, 3, 1001);
Because a municipal-coding district ID cannot be decoded, resolveByDistrictId() requires the district term to exist (run the import first); an unknown ID resolves to NULL.
Adding dependent address fields to your entity
Attach plain entity reference fields to any entity (node, etc.) and wire the cascade through the field's reference settings + widget:
- Add a
field_regionreference field (target: taxonomy term, bundlesmc_region). Reference method: default. Widget: SMC dependent term (cascading select). - Add a
field_cityreference field (bundlesmc_city). Reference method: SMC dependent (filter by parent term), and set Parent field machine name to
field_region. Widget: SMC dependent term. - Add a
field_districtreference field (bundlesmc_district). Reference method SMC dependent, parent fieldfield_city. Widget: SMC dependent term.
On the form, choosing a region filters the city options, and choosing a city filters the district options. The selection handler scopes the option list to the selected parent on both the initial render and live cascade, and
also rejects, on submit, any child that does not belong to the selected parent.
Searchable widget (select2)
The city and district lists are large (thousands of terms), so a plain select is hard to use. Enable the Saudi Municipal Coding — Searchable (select2) submodule (requires the
href="https://www.drupal.org/project/select2">select2 module + library) to get a second widget, SMC dependent term (searchable, select2), which renders the same cascading, parent-scoped list with a search box. Both
widgets are available — pick either per field in Manage form display.
Duplicate district names
Some districts share a name. Across different cities this is harmless once the list is filtered to one city. A number of cities contain two districts with the identical name; for those, the district ID is appended to the
colliding option labels (e.g. Governmental (1055)) so they stay distinguishable. Unique labels are left untouched, and term names are not modified.