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 Laravel Package

mremi/url-shortener

A framework-agnostic PHP library for shortening URLs through pluggable providers. It wraps popular URL-shortening services behind a simple API, supports multiple backends, and is easy to extend or integrate into existing PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a framework-agnostic PHP library, not a Laravel package — there is no service provider, facade, or config file shipped with it. That means your first step is wiring it into the container yourself.

  1. Install it:

    composer require mremi/url-shortener
    

    It pulls in an HTTP client (Symfony HttpClient in recent versions). Check composer.lock to confirm which one, since constructor signatures differ between the 1.x (Guzzle/username+key) and 2.x (Symfony HttpClient/OAuth token) lines.

  2. Read the source, not just the README. The README is short and the API is small. Open src/Provider/ — that's where the real surface area lives. You'll see a ProviderInterface with shorten(Link $link) / expand(Link $link) and concrete providers like BitlyProvider and GoogleProvider, plus a Link model in src/Model/.

  3. Get one credential and one provider working first. Bitly is the only provider worth using in production today; Google's goo.gl was shut down for new links. Put BITLY_ACCESS_TOKEN in .env and never in code.

  4. Smoke-test in Tinker before touching app code:

    $client = new \Symfony\Component\HttpClient\HttpClient();
    $provider = new \Mremi\UrlShortener\Provider\Bitly\BitlyProvider($client, config('services.bitly.token'));
    $link = new \Mremi\UrlShortener\Model\Link();
    $link->setLongUrl('https://example.com/some/very/long/path');
    $provider->shorten($link);
    dump($link->getShortUrl());
    

Your first real use case will almost always be: shorten a URL for SMS/social sharing and persist the short form alongside the original record.

Implementation Patterns

Bind the provider once, resolve by interface. Register a singleton in a dedicated service provider and let the rest of the app depend on ProviderInterface, not the concrete class:

// app/Providers/UrlShortenerServiceProvider.php
public function register(): void
{
    $this->app->singleton(ProviderInterface::class, function ($app) {
        return new BitlyProvider(
            HttpClient::create(),
            config('services.bitly.token')
        );
    });
}

Add services.bitly.token to config/services.php so config:cache works. Do not call env() from inside the binding closure.

Persist the short URL, don't recompute it. Add short_url (and optionally shortener_provider) columns to the model. Shortening is an external HTTP call; treating it as a pure function you call on every page render is a latency and rate-limit trap.

Shorten asynchronously when possible. For bulk imports or user-triggered creation, dispatch a queued job:

class ShortenUrlJob implements ShouldQueue
{
    public int $tries = 3;

    public function __construct(public int $linkId) {}

    public function handle(ProviderInterface $shortener): void
    {
        $model = Link::findOrFail($this->linkId);
        $link = (new ShortenerLink())->setLongUrl($model->url);
        $shortener->shorten($link);
        $model->update(['short_url' => $link->getShortUrl()]);
    }
}

Wrap the library in your own thin service. A UrlShortener application class that takes a ProviderInterface, logs, caches, and catches transport exceptions gives you one place to change when you swap providers:

class UrlShortener
{
    public function __construct(private ProviderInterface $provider) {}

    public function shorten(string $url): string
    {
        return Cache::remember("short:".md5($url), now()->addMonth(), function () use ($url) {
            $link = (new ShortenerLink())->setLongUrl($url);
            $this->provider->shorten($link);
            return $link->getShortUrl();
        });
    }
}

The cache is important: Bitly returns the same short link for the same long URL, and it also means duplicate submissions cost you nothing.

Implement ProviderInterface for anything the package doesn't ship (Short.io, Rebrandly, Dub). The interface is small — you only need shorten and expand. This is the intended extension point and keeps your app decoupled from a library that hasn't shipped since 2021.

Test against the interface, not the network. Bind a fake ProviderInterface in tests:

$this->app->instance(ProviderInterface::class, new class implements ProviderInterface {
    // return a canned Link with https://bit.ly/fake
});

Never let integration tests hit Bitly; reserve one optional live test gated on BITLY_ACCESS_TOKEN being present.

Form requests for the input side. Validate the URL with url + a scheme allowlist before it ever reaches the provider, and render the short link back to the user immediately (from cache) while the queued job does the actual write if you choose the async path.

Gotchas and Tips

  • The package is unmaintained (last release 2021). It works, but treat it as a piece of infrastructure you own. Pin the version, and expect to fork if Bitly's API or the Symfony HttpClient major version drifts.
  • GoogleProvider is dead. goo.gl shutdown means this code path will 4xx. Only use it as a reference implementation for building your own provider.
  • Constructor signatures vary by major version. Older versions take (ClientInterface, $username, $apiKey); newer ones take (HttpClientInterface, $accessToken, array $options). Read the constructor of whatever version composer resolved rather than copying a snippet from a blog post.
  • No Laravel auto-discovery. If you forget the provider registration or config entry, you'll get BindingResolutionException rather than a package-specific error — don't waste time debugging the library.
  • Every call is a network call that can fail. Wrap shorten()/expand() in try/catch for the underlying HTTP client's exceptions, log the response body on failure (Bitly returns useful error JSON), and add a job-level retryUntil rather than blindly retrying in a foreach.
  • Bitly rate limits are per-token and easy to hit during imports. Chunk jobs (Bus::batch with ->chunk(...)) and consider RateLimited middleware with Limit::perMinute().
  • The Link model is mutable and stateful. You construct it, call shorten(), then read getShortUrl() off the same object. expand() populates getLongUrl(). If you accidentally reuse a Link instance across calls you'll read stale values — always create a fresh one per operation.
  • Don't inject the concrete BitlyProvider into controllers. You lose the ability to fake it in tests and to swap providers, and you expose Bitly-specific assumptions (e.g. the 2,048-char URL cap) to your domain code.
  • Watch for composer conflicts. The library depends on a specific Symfony HttpClient major version. If your app pins a different one, you'll get resolution errors — check composer why-not symfony/http-client before assuming the library is broken.
  • Never log the access token, and rotate it if a stack trace with the request headers ever surfaces. Store it in .env and add it to your secrets manager for deployed environments.
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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk