Skip to content

Architecture

Short URL is built on interface-based services that can be individually overridden via Drupal's service container.

Services

ShortUrlManager

Implements ShortUrlManagerInterface. Orchestrates the redirect lifecycle for short URL nodes.

Key methods:

  • buildFullUrl(NodeInterface $node) — builds the full short URL through the outbound path processor pipeline
  • syncRedirects(NodeInterface $node) — deletes and recreates all redirects for the node (handles slug, destination, language, and publish status changes)
  • deleteRedirects(NodeInterface $node) — removes all redirects associated with the node
  • resolveNodeBySlug(string $slug) — loads a short URL node by its slug value
  • getUrlOptions(NodeInterface $node) — returns options array for URL generation (extensibility point for decorators)

SlugGenerator

Implements SlugGeneratorInterface. Generates slugs for the base36 and auto-increment modes.

Key methods:

  • generateBase36(int $length) — generates a random base36 slug using random_bytes()
  • generateAutoIncrement() — returns the next sequential number from the State API counter, with a database safety check

VisitTracker

Implements VisitTrackerInterface. Records visits and computes statistics from the shorturl_visits table.

Tagged as backend_overridable, which means it can be swapped out for an external analytics backend (Matomo, Google Analytics, custom database) via service override.

Key methods:

  • trackVisit(int $nid, array $fields) — inserts a visit record
  • getStats(int $nid) — returns the full statistics array
  • supportsStats() — indicates whether inline statistics are available (returns FALSE in overrides that delegate to external systems)

QrCodeGenerator

Implements QrCodeGeneratorInterface. Generates QR code images using endroid/qr-code.

Key methods:

  • generateSvgDataUri(string $url) — returns a base64-encoded SVG data URI for inline embedding
  • generatePngResponse(string $url) — returns a Symfony Response with PNG content
  • generateSvgResponse(string $url) — returns a Symfony Response with SVG content

ShortUrlVisitMiddleware

HTTP middleware at priority 210 (above PageCache at 200). Intercepts redirect responses bearing the X-Shorturl-Nid header and inserts a visit row. Resolves country codes via Smart IP when available.

ShortUrlQrPathProcessor

Inbound path processor at priority 200. Rewrites /{slug}.qr paths to /api/shorturl/qr/{slug} so that appending .qr to any short URL serves its QR code.

URL generation

Short URLs are built through Drupal's outbound path processor pipeline via ShortUrlBuilderTrait. This trait:

  1. Calls PathProcessorManager::processOutbound() with the slug as the path (e.g. /promo).
  2. Applies any prefix from $options['prefix'] (e.g. a language prefix like /fr/promo).
  3. Uses $options['base_url'] if set by a path processor, otherwise falls back to the current request's scheme and host.

This approach ensures short URLs include the correct language prefix and hostname, and allows decorator modules to inject additional context into the options array.

Hook implementations

Hook implementations are organized into dedicated classes:

Class Hooks
ShortUrlEntityHooks node_presave, node_insert, node_update, node_delete, redirect_response_alter, entity_operation, entity_base_field_info
ShortUrlFormHooks form_node_shorturl_form_alter, form_node_shorturl_edit_form_alter
ShortUrlViewHooks entity_extra_field_info, node_view, theme
ShortUrlViewsHooks views_data, views_data_alter

Database schema

shorturl_visits table

Column Type Description
id serial Primary key
nid int Short URL node ID
langcode varchar(12) Translation language code
timestamp int Unix timestamp
referrer varchar(2048) HTTP Referer header
country_code varchar(2) ISO 3166-1 alpha-2 country code
ip_hash varchar(64) SHA-256 hash of visitor IP

Indexes: nid, nid + langcode, timestamp

Redirect entity additions

A shorturl_nid base field is added to the redirect entity type to link redirects back to their source short URL node. This enables efficient cleanup on node delete and status synchronization on publish/unpublish.

Extending the module

Replacing the visit tracker

To delegate visit tracking to an external system:

# my_module.services.yml
services:
  Drupal\shorturl\VisitTrackerInterface:
    class: Drupal\my_module\ExternalVisitTracker

Your implementation should return FALSE from supportsStats() if statistics are handled externally. The Statistics tab will be hidden automatically.

Customizing slug generation

Override SlugGeneratorInterface to add custom slug algorithms:

services:
  Drupal\shorturl\SlugGeneratorInterface:
    class: Drupal\my_module\MySlugGenerator

Customizing QR codes

Override QrCodeGeneratorInterface to change QR code appearance, size, error correction, or use a different library:

services:
  Drupal\shorturl\QrCodeGeneratorInterface:
    class: Drupal\my_module\MyQrCodeGenerator