Skip to content

Architecture

Domain Short URL extends the base Short URL module through service decoration. Each decorated service wraps the original and adds domain-aware behavior while delegating non-domain operations to the inner service.

Decorated services

DomainAwareShortUrlManager

Decorates ShortUrlManager. Overrides:

  • buildFullUrl() — uses the assigned domain's hostname and path prefix instead of the current request's host
  • syncRedirects() — sets domain_id on all created redirect entities to scope them to the correct domain
  • resolveNodeBySlug() — queries for nodes matching both the slug and the active domain, falling back to a global lookup
  • getUrlOptions() — injects the domain entity into the options array for outbound path processing

DomainAwareSlugGenerator

Decorates SlugGenerator. Adds:

  • generateAutoIncrementForDomain(string $domain_id) — generates a domain-scoped sequential slug using a per-domain State API counter when counter_scope is per_domain

Base36 generation delegates directly to the inner generator since random slugs don't need domain scoping.

DomainAwareVisitMiddleware

Replaces the base ShortUrlVisitMiddleware via a service provider (DomainShorturlServiceProvider). Extends the base middleware to:

  • Read the X-Shorturl-DomainId response header and include domain_id in the visit record
  • Strip the header before the response reaches the client

Data flow

Visit tracking

  1. A redirect response is generated by the Domain Redirect module.
  2. DomainShortUrlEntityHooks::redirectResponseAlter() stamps the X-Shorturl-DomainId header from the redirect's domain_id field.
  3. DomainAwareVisitMiddleware::buildVisitFields() reads the header and adds domain_id to the visit row.
  4. The header is stripped before sending to the client.

Slug generation (auto-increment)

  1. User creates a short URL node with a domain selected.
  2. ShortUrlEntityHooks::nodePresave() runs first and generates a global slug.
  3. DomainShortUrlEntityHooks::nodePresave() runs after (via OrderAfter attribute), detects the domain, and replaces the slug with a domain-scoped counter value.
  4. The node title is set to {slug} @ {domain_label}.

Hook implementations

Class Hooks
DomainShortUrlEntityHooks node_presave, redirect_response_alter
DomainShortUrlFormHooks form_node_shorturl_form_alter, form_node_shorturl_edit_form_alter
DomainShortUrlViewsHooks views_data_alter

Database additions

The shorturl_visits table gains a domain_id column (VARCHAR 255, indexed) added during module installation and removed on uninstall.