RegionHrefBuilder
in package
Computes region slugs (`href`) as the path of the parents.
Pure computation without I/O: the single implementation of the rule, shared by the Api rebuild hook
(RegionManager::rebuildSubtreeHrefs()) and the import
bin/geo-import-regions.php (which calls the same manager after writing). The repository supplies the data.
Everything is per language, from the parent hrefs and the name in the same language:
- country (
type = country) and root: href entered manually (empty falls back to the name slug), descendants inherit it, - district:
href(region)-okres-slug(name without the district prefix), e.g.cr-stredocesky-kraj-okres-nymburk,cr-hlavni-mesto-praha-okres-praha-1, - municipality, region and untyped node:
href(parent)-slug(name), e.g....-okres-nymburk-nymburk(repetition is deliberately not skipped: one uniform rule without exceptions), - collision within a language:
-{official_code}, otherwise-{region_id}; the bare href stays with the node closest to the root, a tie on the same level puts the suffix on all of them.
Steps (between them the repository fetches the colliding rows from the whole table):
- buildBaseHrefs() - hrefs without collision handling, top down,
- buildBaseLookupValues() - bare hrefs to query for collisions,
- buildSuffixLookupValues() - suffixes of the colliding nodes to query (occupancy),
- resolveHrefs() - collision to suffix.
Node shape: array{id: int, parentId: ?int, type: ?string, officialCode: ?string, texts: array<int, array{name: string, href: string}>} (the texts key is the language id).
Tags
Table of Contents
Constants
- DISTRICT_NAME_PREFIX : string = 'Okres '
- District name prefix (as stored in the DB) that the slug replaces with the {@see DISTRICT_SEGMENT} marker.
- DISTRICT_SEGMENT : string = 'okres'
- District marker in the path: `...-okres-nymburk`.
- TYPE_COUNTRY : string = 'country'
- TYPE_DISTRICT : string = 'district'
- TYPE_MUNICIPALITY : string = 'municipality'
Properties
- $slugCache : array<string, string>
Methods
- buildBaseHrefs() : array<int, array<int, string>>
- Step 1: subtree node hrefs without collision handling.
- buildBaseLookupValues() : array<int, string>
- Step 2: bare subtree hrefs (all languages together, one table pass per batch).
- buildSuffixLookupValues() : array<int, string>
- Step 3: suffixes (`-{official_code}`, `-{region_id}`) only for nodes that collide according to the rows from step 2; the repository uses them to verify that the suffix is not taken.
- isManualNode() : bool
- A manual node is a country or a tree root (also one added by hand without a type): a person enters its href.
- resolveHrefs() : array<int, array<int, string>>
- Step 4: collision to suffix.
- buildPathHref() : string
- buildSegment() : string
- Slug of the own name; without a name in the language it falls back to the region id (the href must not be empty).
- buildSubtreeIdSet() : array<int, int>
- buildSuffixCandidates() : array<int, string>
- Suffixes in order of preference: `-{official_code}`, then `-{region_id}`.
- computeDepths() : array<int, int>
- Depth of the subtree nodes (tree root = 1, same as the `level` column), computed from the number of ancestors and not from the column: a newly inserted node gets `level` only after `afterSave`.
- findSuffixedNodes() : array<int, RegionHrefNode, 1: int, 2: string}>
- Subtree nodes that must receive a suffix (see {@see resolveHrefs()}).
- joinSegments() : string
- slug() : string
- `Strings::webalize()` is expensive (transliteration) and district names repeat for every municipality.
Constants
DISTRICT_NAME_PREFIX
District name prefix (as stored in the DB) that the slug replaces with the {@see DISTRICT_SEGMENT} marker.
public
string
DISTRICT_NAME_PREFIX
= 'Okres '
DISTRICT_SEGMENT
District marker in the path: `...-okres-nymburk`.
public
string
DISTRICT_SEGMENT
= 'okres'
TYPE_COUNTRY
public
string
TYPE_COUNTRY
= 'country'
TYPE_DISTRICT
public
string
TYPE_DISTRICT
= 'district'
TYPE_MUNICIPALITY
public
string
TYPE_MUNICIPALITY
= 'municipality'
Properties
$slugCache
private
array<string, string>
$slugCache
= []
Methods
buildBaseHrefs()
Step 1: subtree node hrefs without collision handling.
public
buildBaseHrefs(array<int, RegionHrefNode> $ancestors, array<int, RegionHrefNode> $subtree[, array<int, string> $rootHrefs = [] ]) : array<int, array<int, string>>
$ancestors are the ancestors of the subtree root (from the tree root); their href is taken from
the DB as is. $subtree is the subtree root plus descendants ordered so that a parent precedes its
child (nested set by lft). $rootHrefs is the manual subtree root href per language (CLI country
abbreviation setting); used only for a manual node (country / root).
Parameters
- $ancestors : array<int, RegionHrefNode>
- $subtree : array<int, RegionHrefNode>
- $rootHrefs : array<int, string> = []
-
languageId => href
Return values
array<int, array<int, string>> —regionId => languageId => href
buildBaseLookupValues()
Step 2: bare subtree hrefs (all languages together, one table pass per batch).
public
buildBaseLookupValues(array<int, array<int, string>> $bases) : array<int, string>
Parameters
- $bases : array<int, array<int, string>>
-
regionId => languageId => href
Return values
array<int, string>buildSuffixLookupValues()
Step 3: suffixes (`-{official_code}`, `-{region_id}`) only for nodes that collide according to the rows from step 2; the repository uses them to verify that the suffix is not taken.
public
buildSuffixLookupValues(array<int, array<int, string>> $bases, array<int, RegionHrefNode> $ancestors, array<int, RegionHrefNode> $subtree, array<int, RegionHrefOutsideRow> $outsideRows) : array<int, string>
Parameters
- $bases : array<int, array<int, string>>
- $ancestors : array<int, RegionHrefNode>
- $subtree : array<int, RegionHrefNode>
- $outsideRows : array<int, RegionHrefOutsideRow>
Return values
array<int, string>isManualNode()
A manual node is a country or a tree root (also one added by hand without a type): a person enters its href.
public
isManualNode(RegionHrefNode $node) : bool
Parameters
- $node : RegionHrefNode
Return values
boolresolveHrefs()
Step 4: collision to suffix.
public
resolveHrefs(array<int, array<int, string>> $bases, array<int, RegionHrefNode> $ancestors, array<int, RegionHrefNode> $subtree, array<int, RegionHrefOutsideRow> $outsideRows) : array<int, array<int, string>>
Candidates for a bare href within a language: subtree nodes with the same href and rows outside
the subtree with the same href or the same base (base = href without its own
-{official_code} / -{region_id} suffix, computed by the repository). The bare href stays with
the single candidate on the highest level (smallest depth); other subtree nodes get a suffix.
A subtree node never receives an href exactly occupied by a row outside the subtree (a foreign row
is not rewritten, uniqueness wins).
Only the set of candidates and their level decide, not the processing order, so rebuilding one
municipality gives the same result as rebuilding the whole country.
Manual nodes (country / root) are never renamed because a person entered them; they only occupy their href.
Parameters
- $bases : array<int, array<int, string>>
-
regionId => languageId => href
- $ancestors : array<int, RegionHrefNode>
- $subtree : array<int, RegionHrefNode>
- $outsideRows : array<int, RegionHrefOutsideRow>
-
rows from the whole table (subtree rows are ignored)
Return values
array<int, array<int, string>> —regionId => languageId => href
buildPathHref()
private
buildPathHref(RegionHrefNode $node, int $languageId, array<int, RegionHrefNode> $nodes, array<int, array<int, string>> $hrefs) : string
Parameters
- $node : RegionHrefNode
- $languageId : int
- $nodes : array<int, RegionHrefNode>
- $hrefs : array<int, array<int, string>>
-
hrefs already computed (ancestors from the DB, subtree top down)
Return values
stringbuildSegment()
Slug of the own name; without a name in the language it falls back to the region id (the href must not be empty).
private
buildSegment(RegionHrefNode $node, int $languageId) : string
Parameters
- $node : RegionHrefNode
- $languageId : int
Return values
stringbuildSubtreeIdSet()
private
buildSubtreeIdSet(array<int, RegionHrefNode> $subtree) : array<int, int>
Parameters
- $subtree : array<int, RegionHrefNode>
Return values
array<int, int>buildSuffixCandidates()
Suffixes in order of preference: `-{official_code}`, then `-{region_id}`.
private
buildSuffixCandidates(RegionHrefNode $node, string $base) : array<int, string>
Parameters
- $node : RegionHrefNode
- $base : string
Return values
array<int, string>computeDepths()
Depth of the subtree nodes (tree root = 1, same as the `level` column), computed from the number of ancestors and not from the column: a newly inserted node gets `level` only after `afterSave`.
private
computeDepths(array<int, RegionHrefNode> $ancestors, array<int, RegionHrefNode> $subtree) : array<int, int>
Parameters
- $ancestors : array<int, RegionHrefNode>
- $subtree : array<int, RegionHrefNode>
Return values
array<int, int> —regionId => depth
findSuffixedNodes()
Subtree nodes that must receive a suffix (see {@see resolveHrefs()}).
private
findSuffixedNodes(array<int, array<int, string>> $bases, array<int, RegionHrefNode> $ancestors, array<int, RegionHrefNode> $subtree, array<int, RegionHrefOutsideRow> $outsideRows) : array<int, RegionHrefNode, 1: int, 2: string}>
Parameters
- $bases : array<int, array<int, string>>
- $ancestors : array<int, RegionHrefNode>
- $subtree : array<int, RegionHrefNode>
- $outsideRows : array<int, RegionHrefOutsideRow>
Return values
array<int, RegionHrefNode, 1: int, 2: string}> —[node, language, bare href]
joinSegments()
private
joinSegments(string ...$segments) : string
Parameters
- $segments : string
Return values
stringslug()
`Strings::webalize()` is expensive (transliteration) and district names repeat for every municipality.
private
slug(string $value) : string
Parameters
- $value : string