URL { .text-brand }
A modular core package for complete URL management, link shortening, click tracking, and privacy-safe link analytics. Designed for the Obelawium ERP framework.
Getting Started
Installation
$ composer require obelaw/ium-url
Publish Migrations
$ php artisan vendor:publish --tag=ium-url-migrations
$ php artisan migrate
Publish Config (optional)
$ php artisan vendor:publish --tag=ium-url-config
Requirements
- PHP 8.2+
- Laravel 11.0+ or 12.0+
obelaw/ium
Shorten URLs
Use the ShortenUrlData DTO to validate arguments and shorten links via the shorten service method. Returns an instance of Link.
Basic Shortening
use Obelaw\Ium\Url\Data\ShortenUrlData;
$link = ium()->url()->shorten(ShortenUrlData::from([
'original_url' => 'https://example.com/very/long/url',
]));
// Access the shortened link
echo $link->code; // auto-generated short code
echo $link->original_url; // https://example.com/very/long/url
With Custom Code & Options
// With a custom short code, status, and expiration
$link = ium()->url()->shorten(ShortenUrlData::from([
'original_url' => 'https://example.com/very/long/url',
'code' => 'my-custom-code',
'status' => 'active',
'expires_at' => '2026-12-31 23:59:59',
]));
echo $link->code; // 'my-custom-code'
echo $link->status; // 'active'
echo $link->expires_at; // '2026-12-31 23:59:59'
Link Model Properties
| Property | Type | Description |
|---|---|---|
code | string | Unique short code (custom or auto-generated) |
original_url | string | The original long URL |
status | string | active or inactive |
expires_at | datetime|null | Optional link expiration timestamp |
clicks_count | integer | Total click count (cached counter) |
Redirect & Click Tracking
Pass visitor request details from the HTTP layer to the decoupled tracking service. The TrackUrlData DTO encapsulates IP, user agent, and referrer context.
Tracking a Click
use Obelaw\Ium\Url\Data\TrackUrlData;
// In your controller, pass request context to the service
$click = ium()->url()->track(TrackUrlData::from([
'link_id' => $link->id,
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'referrer' => $request->header('referer'),
]));
echo $click->device_type; // 'desktop', 'mobile', etc.
echo $click->clicked_at; // timestamp of the click
Click Model Properties
| Property | Description |
|---|---|
ip | Anonymized IP address (masked before storage) |
user_agent | Original user agent string |
device_type | Parsed device: desktop, mobile, tablet, robot, other |
referrer | HTTP referrer header value |
clicked_at | Timestamp of the click event |
Analytics
Retrieve access aggregates, device distributions, and click referrers. The UrlStatsData DTO lets you filter results by date range and device type.
Basic Analytics Query
use Obelaw\Ium\Url\Data\UrlStatsData;
$analytics = ium()->url()->analytics(UrlStatsData::from([
'link_id' => $link->id,
'date_from' => '2026-01-01',
'date_to' => '2026-05-30',
'device_type' => null, // all devices
]));
echo $analytics->total_clicks; // 150
print_r($analytics->clicks_by_device); // array by device type
print_r($analytics->clicks_by_referrer); // array by referrer
Analytics Response Structure
| Key | Type | Description |
|---|---|---|
total_clicks | integer | Total number of clicks |
clicks_by_device | array | Clicks grouped by device type |
clicks_by_referrer | array | Clicks grouped by referrer source |
Link Statistics
Get detailed, index-friendly aggregate counts separating total link clicks from unique visitors. The stats method returns an immutable UrlStatsResultData object.
Querying Stats
$stats = ium()->url()->stats($link->id);
echo $stats->totalClicks; // 150 (all click interactions)
echo $stats->uniqueClicks; // 42 (unique visitors)
echo $stats->uniqueRatio; // 0.28 (28% unique vs total)
UrlStatsResultData Properties
| Property | Type | Description |
|---|---|---|
totalClicks | integer | All click interactions |
uniqueClicks | integer | Unique visitors (IP + User Agent) |
uniqueRatio | float | Percentage of unique vs total clicks |
How Unique Detection Works
Unique visitors are determined by aggregating distinct IP + User Agent pairs. This means the same IP from different browsers counts as multiple unique visitors, while repeat visits from the same device are counted only once.
Utilities
IP Anonymizer
The IpAnonymizer cleans visitor IP logs before storage. Both IPv4 and IPv6 addresses are masked to their network prefix, discarding the host portion.
use Obelaw\Ium\Url\Support\IpAnonymizer;
$masked = IpAnonymizer::mask('10.0.0.50');
// Result: '10.0.0.0'
$maskedV6 = IpAnonymizer::mask('2001:db8::1');
// Result: '2001:db8::'
User Agent Parser
The decoupled UserAgentParser analyzes headers to categorize visitors into device types — no heavy third-party vendor dependencies.
use Obelaw\Ium\Url\Support\UserAgentParser;
$parser = new UserAgentParser;
$parser->parse('Mozilla/5.0 (iPhone; CPU OS ...)');
echo $parser->deviceType(); // 'mobile'
$parser->parse('Mozilla/5.0 (Windows NT 10.0; ...)');
echo $parser->deviceType(); // 'desktop'
desktop— Desktop browsermobile— Mobile phonetablet— Tablet devicerobot— Bot / crawlerother— Unknown
10.0.0.50→10.0.0.0172.16.5.99→172.16.5.0::1→::2001:db8::1→2001:db8::
:::
Testing
Run the Pest test suite to verify everything works correctly:
$ composer test