Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Url Shortener Bundle Laravel Package

mremi/url-shortener-bundle

Symfony bundle that adds URL shortening to your application. It integrates the mremi/url-shortener library with Doctrine-backed storage, configurable short-link generation, and redirect handling, making it easy to create and serve compact URLs from your Symfony project.

View on GitHub
Deep Wiki
Context7

Getting Started

Do not install the bundle. composer require mremi/url-shortener-bundle pulls in Symfony's DI extension, a YAML config tree, and Doctrine-oriented service definitions — none of which Laravel's container can compile or resolve. The reusable asset is the underlying library:

composer require mremi/url-shortener

That package is framework-agnostic PHP: provider clients (Bitly, Google, TinyURL), a local/storage-backed shortener strategy, and a ShortenerInterface plus a storage interface. Pin an exact version in composer.json — with 19 stars and zero dependents, you are the QA.

Where to look first, in this order:

  1. vendor/mremi/url-shortener/src/ShortenerInterface.php — the one contract you actually care about (shorten() returns a ShortenedUrl value object).
  2. src/Provider/* — the third-party provider clients. Check auth, endpoints, and whether they still match the live APIs before trusting them.
  3. src/Storage/* and the Doctrine implementations — read them to model your own Eloquent storage, not to copy them verbatim.
  4. The bundle repo's Resources/config/services.yaml — it is documentation for how the library was intended to be wired. Read it, don't ship it.

First use case, half-day spike. Prove a round trip in php artisan tinker or a throwaway route:

$shortener = app(\Mremi\UrlShortener\Provider\Bitly\BitlyProvider::class);
$short = $shortener->shorten('https://example.com/a/very/long/campaign/url');
dump((string) $short);

Then confirm the read path: you can resolve a stored code back to its target. If both work, you have validated the only two operations you need. Everything after this is glue.

The use case to start with: internal, regenerable links — transactional email, referral codes, "share this dashboard" links. Do not start with customer-facing permanent URLs on packaging; the maintenance risk profile of a single-maintainer package does not support that.

Implementation Patterns

Wrap it behind a first-party interface. This is the whole design decision.

interface ShortLinkService
{
    public function create(string $targetUrl, ?string $ownerId = null, ?Carbon $expiresAt = null): ShortLink;
    public function resolve(string $code): ?ShortLink;
}

Bind it in a UrlShortenerServiceProvider. Your controllers, mailables, and jobs type-hint ShortLinkService only — never the library's classes. When upstream goes quiet (assume it will), the swap is one binding, not a refactor.

Make the package a codec, not the system of record. Ship your own short_links migration: code (unique index), target_url, owner_id, expires_at, hit_count, timestamps. Store the link yourself; use the library only to generate codes. This side-steps the bundle's Doctrine storage entirely and means an abandoned dependency costs you the encoder, not the data.

Eloquent-backed storage if you use the library's storage interface. Implement the library's storage contract as a thin adapter over an Eloquent model, then register it in the service provider. Do not attempt to port the Doctrine mappings — that is where the Symfony coupling actually lives.

Read path first, and cache it. Short links are read-skewed: one create, thousands of redirects. A redirect controller resolved from Redis keyed by code with a short TTL (minutes) is the right default. The database stays out of the hot path, and cache is strictly derived from it.

Route::get('/s/{code}', RedirectController::class)
    ->withoutMiddleware(['web'])          // no CSRF/session on redirects
    ->where('code', '[A-Za-z0-9]{4,10}');

Exclude the redirect route from session, CSRF, and any localization/trailing-slash middleware that could mutate the path. Default to 302, not 301 — 301 caches in browsers and makes retargeting an existing code impossible.

Provider interaction belongs on the write path only. Never call Bitly (or any external API) during a redirect. This is the single biggest architectural guardrail: a provider outage should break new link creation with a clear error, while every existing link keeps resolving locally.

Wire config through Laravel, not Symfony YAML. Publish config/url-shortener.php with the driver (internal, bitly, tinyurl), credentials from env(), and the default domain. Map this into the service provider's bindings; do not hand-transcribe the bundle's config tree.

Collision retry with a bounded loop. On the write path, insert with a unique index on code, catch the constraint violation, regenerate, retry — capped at three attempts, then a 500 with an alert. Six characters of base62 is ~56 billion combinations; you will not exhaust it, but you will lose the race occasionally under concurrency.

Optional: an artisan command for bulk/administrative creation. php artisan shortlink:create {url} --owner= --expires= is the lowest-friction write path for ops and saves you building a UI on day one.

Gotchas and Tips

  • The bundle cannot be autoloaded by Laravel. Full stop. If a PR or a StackOverflow answer tells you to register it, that guidance was written for Symfony. You are installing mremi/url-shortener and writing the wiring yourself — budget the 1–3 days honestly.
  • Check the composer.json PHP constraint before anything else. A stale "php": "^7.x" constraint is the most likely hard blocker on Laravel 10/11 / PHP 8.2+, and it is invisible until composer refuses to install.
  • No migrations ship for Laravel. The bundle assumes Doctrine. Your short_links table is yours to design — unique index on code is non-negotiable.
  • Provider SDKs rot fastest. Bitly/Google auth flows and endpoints change independently of this package. Pin the transitive dependencies and monitor them; treat a provider integration as a small, replaceable adapter, not foundational infrastructure.
  • No analytics, no admin UI, no rate limiting, no abuse handling, no custom domains, no multi-tenant namespaces. "URL shortener" in the name does not imply any of these exist. Each is product surface you build or buy separately — scope accordingly before promising a stakeholder a "link dashboard."
  • Debugging has no community trail to follow. Zero dependents means zero filed issues matching your bug. Your escalation path is reading vendor/mremi/url-shortener/src and adding Log::debug() around the provider call. Factor that into on-call expectations.
  • Expired or broken targets should land on a branded 404, not a dead link. Add an expiry sweeper as a follow-on job; do not put expiry logic in the redirect hot path.
  • Cache invalidation on update: if you ever retarget a code, invalidate Redis in the same transaction. Short TTLs (minutes) are your safety net for divergence.
  • Extension points that actually matter: the ShortenerInterface and the storage interface. Everything else — the Symfony config tree, the compiler passes, the service aliases — is bundle-specific and irrelevant to you. Design your adapter so those two interfaces are the only contact surface.
  • Have the Plan B ready. A ~60-line local base62 encoder over an auto-increment ID covers the internal-shortener case with zero third-party code. If the library proves unworkable, switching is an afternoon, not a sprint — precisely because you wrapped it.
  • Own the fork decision up front. Assign a named owner, budget a few hours per quarter to review upstream, and decide now whether you would vendor the library into app/ rather than depend on it. With zero dependents, upstream is unlikely to be responsive to your issues.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package