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

Gravatar Bundle Laravel Package

mluex/gravatar-bundle

A Symfony bundle that integrates Gravatar avatars into your application. It provides Twig helpers and services to generate Gravatar URLs from email addresses, with support for default images, size, rating, and secure HTTPS URLs, making it easy to display user avatars consistently.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framing: mluex/gravatar-bundle is a Symfony 5 bundle, not a Laravel package. There is no service provider, no facade, no publishable config, no auto-discovery. Two stars, one release (March 2020), zero dependents. Treat this as a reference implementation you read, not a dependency you install.

Minimal steps

  1. Don't composer require it into a Laravel app. It declares Symfony framework dependencies that will drag symfony/framework-bundle and friends into your vendor/ for what amounts to a 30-line URL builder.
  2. Read it on GitHub instead. Three files matter:
    • src/Gravatar.php — the entire public surface (getUrl() and helpers)
    • src/Twig/GravatarExtension.php — how it's exposed to templates
    • src/DependencyInjection/Configuration.php — the config keys (default, size, rating)
  3. Copy the algorithm into your own app. That's the whole package:
// app/Support/Gravatar.php
namespace App\Support;

class Gravatar
{
    public static function url(string $email, int $size = 80, string $default = 'mp', string $rating = 'g'): string
    {
        $hash = md5(strtolower(trim($email)));

        return sprintf(
            'https://www.gravatar.com/avatar/%s?s=%d&d=%s&r=%s',
            $hash,
            min($size, 2048),
            urlencode($default),
            $rating
        );
    }
}

First use case: replace a hardcoded placeholder avatar in a Blade view (<img src="{{ App\Support\Gravatar::url($user->email) }}">). That is the entire day-one value proposition, and it takes about ten minutes.

Implementation Patterns

Bind it as a singleton, config-driven. Mirror what the bundle's DI extension does, using Laravel's config system:

// config/gravatar.php
return [
    'size'    => env('GRAVATAR_SIZE', 80),
    'default' => env('GRAVATAR_DEFAULT', 'mp'),  // mp|identicon|monsterid|wavatar|retro|robohash|404|<url>
    'rating'  => env('GRAVATAR_RATING', 'g'),    // g|pg|r|x
];
// app/Providers/AppServiceProvider.php
$this->app->singleton(Gravatar::class, fn () => new Gravatar(
    config('gravatar.size'),
    config('gravatar.default'),
    config('gravatar.rating'),
));

Per-call overrides beat global config. The bundle's global size setting is nearly useless in practice — you need 24px in a comment list, 40px in a navbar, 128px on a profile page. Expose size as the first argument and let the config supply the fallback.

Blade component for consistency. This is where the Symfony Twig gravatar filter maps onto Laravel:

{{-- resources/views/components/avatar.blade.php --}}
@props(['user', 'size' => 40])
<img
    src="{{ app(App\Support\Gravatar::class)->url($user->email, $size) }}"
    width="{{ $size }}" height="{{ $size }}"
    alt="{{ $user->name }}"
    loading="lazy"
    class="rounded-full"
>

Persist the existence check, don't re-check per request. Use d=404 as a one-time probe when a user registers or changes email, and store the result:

$exists = Http::get(Gravatar::url($email, 80, '404'))->successful();
$user->update(['has_gravatar' => $exists]);

Then render from the local avatar/initials fallback when has_gravatar is false — no third-party round trip on page load.

Proxy through your own route. Gravatar is a third-party request on every page view: it leaks your users' IPs to Automattic and adds a DNS/TLS hop. Cache the bytes and serve them yourself:

Route::get('/avatar/{user}', fn (User $user) => Cache::remember(
    "avatar:{$user->id}", now()->addDay(),
    fn () => Http::get(Gravatar::url($user->email, 128))->body()
))->middleware('throttle:60,1');

Default-image strategy matters more than the URL code. mp (mystery person) is the safe corporate choice; identicon gives deterministic per-user variety; retro is the 8-bit set. A custom d= URL lets you point at your own branded placeholder — remember it must be URL-encoded.

Gotchas and Tips

The Symfony-bundle mismatch is the headline gotcha. No extra.laravel block means no discovery. If you install it anyway and try to resolve Mluex\GravatarBundle\Gravatar from the container, you'll get a BindingResolutionException — the service is registered by GravatarExtension::load(), which only runs inside a Symfony kernel. The Twig extension is likewise inert in Blade.

Last release 2020-03-21. It was a Symfony 5 fork of ornicar/gravatar-bundle, and both are effectively unmaintained. There will be no PHP 8.3/8.4 fixes, no Symfony 6/7 support, no security patches. Since it's ~200 lines of md5() and sprintf(), there is nothing to patch — which is exactly why copying beats depending.

Email normalization is the #1 bug source. Gravatar hashes md5(strtolower(trim($email))). If your registration form doesn't trim, or you hash the raw $request->email, you get a silently different hash and a mystery-person avatar that looks like "Gravatar is broken." Normalize once at the model level (a mutator) and never think about it again.

The s parameter caps at 2048. Larger values are silently clamped server-side, so a retina s=512 is fine but s=4096 is wasted bytes in your markup.

d=404 returns an actual 404. If you're proxying with Http::get(), ->body() on a 404 gives you Gravatar's error page, not an image. Always check ->successful() before caching, and make sure the cached entry has a short TTL so a user who just signed up for Gravatar isn't stuck with a placeholder.

HTTPS is the only scheme now. Gravatar dropped plain HTTP; older tutorials (and possibly this bundle's defaults) mention a secure flag. Don't add one — hardcode https://.

GDPR/privacy. An MD5 of an email address is pseudonymized PII, not anonymized, and Gravatar's terms make the hash effectively enumerable against known email lists. If you have EU users, the proxy pattern above isn't optional — it's the difference between "we send a hash" and "we send a hash and the user's IP to a US processor on every page view."

Debugging checklist when an avatar is wrong:

  1. Paste the generated URL in a browser with &d=404 — 404 means no Gravatar exists for that hash.
  2. Recompute md5(strtolower(trim($email))) in php artisan tinker and diff it against the hash in the URL. Nine times out of ten it's whitespace or casing.
  3. Confirm you didn't double-encode the email before hashing.

Extension points are essentially nonexistent. There are no events, no DI tags, no compiler passes worth using. The only seam is the Twig extension — and in Laravel the equivalent seam is the Blade component, which you own.

If you want a maintained Laravel-native option, creativeorange/gravatar is the conventional choice and gives you a facade and config publishing for roughly the same amount of code you'd write by hand. But honestly: the 30-line App\Support\Gravatar class above has no dependencies, no upgrade path, and no surprises — which is more than can be said for a two-star Symfony fork from 2020.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle