jeffersongoncalves/laravel-dub
A Laravel integration for the Dub.co link management API that lets you create, update, and organize short links and retrieve click analytics directly from your PHP application using a clean, fluent interface. Simplify branded link sharing and tracking.
php artisan vendor:publish --tag="dub-config" (check the package README for the exact tag — the config file is the first thing worth opening)..env:
DUB_API_KEY=your_dub_api_key
DUB_API_URL=https://api.dub.co
DUB_TIMEOUT=10
DUB_API_URL and DUB_TIMEOUT normally never change; only DUB_API_KEY is per-environment. Keep a separate key per environment/workspace so staging traffic doesn't pollute production analytics.$link = Dub::createLink([
'url' => $post->publicUrl(),
'domain' => 'yourdomain.co',
'key' => 'post-' . $post->id, // deterministic keys make retries safe
'tags' => ['post'],
]);
Then Dub::analytics($linkId, [...]) to render clicks. Because everything is static, there is nothing to register, inject, or boot — you can call it from a tinker session as soon as the env vars are set, which makes it a fast package to evaluate.Dub:: is a static client, so it's the one thing in your app you can't swap via the container. A three-method ShortLinkService (create, forModel, stats) pays for itself the first time you need a fake in tests or a second provider.key from your model (post-{id}, invite-{uuid}) rather than letting Dub generate one, so retried jobs and double-submits converge on the same link instead of creating duplicates. Store the returned link id/key/shortLink on your model — the API is the source of truth, but you don't want a network call to render a share button.bulkCreateLinks takes an array, so chunk your collection (100–500 at a time), dispatch one job per chunk, and let the queue absorb partial failures. Log the failures per chunk rather than aborting the whole import.listLinks is paginated; drive it from a queued sync job that walks pages and upserts locally, rather than calling it in a request. This is how you keep a local short_links table in sync for search, filtering, and joins.analytics, analyticsByCountry, and analyticsByDevice are read-only and change slowly — cache for 5–15 minutes keyed on link + range, and render dashboards from cache. Never call them per row in a table loop; batch by fetching per-link aggregates in a job and store them in your own table if you need per-row numbers.updateLink for destination/UTM/tag changes keeps the short URL and its accumulated analytics intact; deleting and recreating resets history and breaks links already in the wild.DUB_API_KEY in phpunit.xml and keep destructive integration tests off production keys.Dub::fake()). If it doesn't, the practical escape hatch is Http::fake() on the underlying Guzzle/Laravel HTTP calls and asserting the outgoing payload — brittle, but workable. This is the strongest argument for wrapping the client early.DubRateLimitException is a package-specific exception, not an Illuminate\Http\Client one. Catch it explicitly before any broad catch (\Throwable); a swallowed 429 looks like a silent data-loss bug. In jobs, release with a delay based on the Retry-After value the exception carries rather than a fixed backoff — the package has already parsed it for you, so use it.Retry-After.DUB_TIMEOUT applies per request. Bulk and list calls are the ones that actually brush up against it; raise it for those jobs if you chunk large payloads. A timeout is not a "didn't happen" signal — the link may have been created server-side, which is another reason to use deterministic keys.env() is only read at config load. After php artisan config:cache in production, changing DUB_API_KEY in .env does nothing until you re-cache. Classic "why is it still hitting the old workspace" moment.deleteLink removes the link and makes the short URL dead — no soft-delete from your side unless you track it. Prefer deactivating/disabling links when users "remove" a share, and reserve deleteLink for cleanup jobs and test data.How can I help you explore Laravel packages today?