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

pyrrah/gravatar-bundle

Symfony bundle that simplifies Gravatar integration, providing services and Twig helpers to generate avatar URLs from email addresses with configurable size, default image, rating, and secure hashing. Lightweight and easy to configure.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framing: pyrrah/gravatar-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no extra.laravel block, and no auto-discovery. If you're in a Laravel codebase, treat it as a source of two things: an MD5-hash-to-URL builder class and a set of config defaults. You will wire it yourself.

Version note (v1.6.0): this release only adds Symfony 8 support and drops everything below Symfony 7.4. That is a hard support-window cut, not a feature change. Practically:

Your situation Branch to install
Symfony 6.0 – 7.3 in the same tree ^1.5
Symfony 7.4 / 8.x components ^1.6
Laravel app, no Symfony framework either — you're only using the builder class

Because v1.6.0's symfony/* constraints are now ^7.4 || ^8.0, and Symfony 8 requires PHP 8.4, Composer's resolution changes based on your PHP version. On PHP 8.2/8.3 you'll get Symfony 7.4 components dragged in; on PHP 8.4+ you may get the full Symfony 8 stack. Neither is what you want in a Laravel app.

Minimal steps:

  1. Check composer.json require before anything else. v1.6.0 widens to Symfony 8 and narrows the floor to 7.4. Laravel itself pins a spread of symfony/* components (console, http-foundation, finder, error-handler…), often at ^7.0. A ^7.4 || ^8.0 requirement from this bundle can fight Laravel's own constraints on older minors, and composer require will either fail or silently upgrade half your framework. If Composer fights you, skip the package and copy the ~50-line URL builder instead — it has not meaningfully changed in years.
  2. Pin explicitly. Install pyrrah/gravatar-bundle:^1.5 if you're on Symfony 6–7.3 anywhere in the tree, ^1.6 otherwise. Don't use * or dev-main; the 1.5 → 1.6 jump is a breaking support cut, and the next one will be too.
  3. In a Symfony app: register the bundle in config/bundles.php (Flex does this), then configure it under the pyrrah_gravatar key in config/packages/ (confirm the exact key in DependencyInjection/Configuration.php).
  4. In Laravel: open src/Gravatar.php (or whatever the URL builder is named) and read its constructor. If it only takes config scalars, it instantiates cleanly in a service provider.
  5. Look at Twig/GravatarExtension.php for the call signature — typically gravatar(email, size, rating, default, secure).

First use case: a user avatar on a profile page. Pass the raw email, get back a URL, drop it in an <img>:

// app/Providers/AppServiceProvider.php
$this->app->singleton(\Pyrrah\GravatarBundle\Gravatar::class, fn () => new \Pyrrah\GravatarBundle\Gravatar(
    defaultSize: 80,
    defaultRating: 'g',
    defaultImage: 'mp',
    secure: true,
));

Then app(Gravatar::class)->getUrl($user->email).

Implementation Patterns

Wrap it, don't scatter it. The bundle's API is a pure function of (email, size, rating, default). Expose it through one place so you can swap providers later — and so the v1.5 → v1.6 constraint churn never reaches your call sites:

// app/Services/Avatar.php
final class Avatar
{
    public function __construct(private \Pyrrah\GravatarBundle\Gravatar $gravatar) {}

    public function url(?string $email, int $size = 80, string $default = 'mp'): string
    {
        if (! $email) {
            return $this->fallback($size, $default);
        }
        return $this->gravatar->getUrl($email, $size, 'g', $default, true);
    }
}

Blade surface. Add a directive so views never touch the class:

Blade::directive('gravatar', fn ($expr) => "<?php echo e(app(\App\Services\Avatar::class)->url($expr)); ?>");

@gravatar($user->email, 80) reads better than the Symfony Twig analogue and keeps the upgrade path to a different provider trivial.

Model accessor + srcset. Because the URL is deterministic, cache it on the model and emit retina variants:

protected function avatarUrl(): Attribute
{
    return Attribute::get(fn () => app(Avatar::class)->url($this->email, 160));
}
<img src="{{ $user->avatar_url }}" srcset="{{ $user->avatar_url }} 2x" width="80" height="80" alt="">

Use the d= parameter instead of branching. mp (mystery person), identicon, retro, robots, and 404 are all supported remote defaults. Pushing the fallback into the query string means one code path for users with and without email.

Cache the hash, not the HTTP response. Cache::remember("avatar:{$user->id}", now()->addDay(), ...) is fine, but the win is avoiding the service call in hot loops (comment lists, activity feeds). Preload avatar_url with ->withCasts() or append it to API resources so you're not resolving a singleton per row.

Config over code. Whatever defaults the bundle exposes (size, rating, default image, secure) should live in config/services.php or a dedicated config/avatar.php, not as literals in controllers. The whole value of the bundle over a one-liner is centralized defaults.

Treat the version as infrastructure, not a dependency. Because v1.6.0 is a support-window release rather than a feature release, budget for it the way you'd budget for a PHP minor bump: it will periodically force a Symfony component upgrade you didn't ask for. A composer.json note or a CI job asserting the resolved symfony/* versions is cheaper than discovering it during a deploy.

Gotchas and Tips

Hashing rules are not optional. Gravatar's key is md5(strtolower(trim($email))). If the bundle takes the raw email, pass it raw — don't half-normalize upstream with strtolower in one place and not another, or users will get mystery-person avatars with no error to debug. Verify with md5(strtolower(trim($email))) against the URL the package returns.

s= maxes at 2048. Requesting 2x for a 80px slot means s=160, not s=2048. Over-requesting wastes bandwidth; under-requesting gives you mush on retina.

Privacy is a real objection, not a footnote. Gravatar sends a hash of your user's email to Automattic on page load. MD5 is not anonymization — common addresses are trivially reversed. If you have GDPR exposure, either proxy the fetch server-side, use d=404 and fall back to initials locally, or make Gravatar opt-in per user. Document it.

Availability. gravatar.com is unreliable or blocked in some regions (notably mainland China). For anything user-facing at scale, put a route in front of it (/avatar/{user}) that fetches once, caches to disk/S3, and serves locally. The bundle won't do this for you — it only builds strings.

Bundle config keys are Symfony-shaped. pyrrah_gravatar config lives under Symfony's semantic config tree. You cannot reuse the YAML in Laravel; you must re-map each key to constructor args. Read DependencyInjection/Configuration.php rather than guessing from the README.

v1.6.0 raised the floor to Symfony 7.4 — audit your lockfile after upgrading. The release notes are explicit: "versions prior to Symfony 7.4 are no longer supported… for projects using Symfony 6 to 7.3, please use the v1.5.x branch." In a Laravel app this usually surfaces as Composer resolving symfony/console, symfony/http-foundation, or symfony/error-handler to 7.4 without you asking. Diff the lockfile. If your app is on PHP 8.2/8.3 you'll land on 7.4; on PHP 8.4+ you can get Symfony 8 — either way, confirm nothing else in your stack pinned ^7.0/^7.1.

Watch the Twig dependency. The Twig extension class will reference Twig\Extension\AbstractExtension. Unused classes don't break autoload, but if twig/twig gets installed into a Laravel app it's dead weight — and if the bundle declares it as a hard require rather than require-dev/suggest, you're paying for it. Check composer.json each time you bump the major support window.

Debugging checklist when an avatar doesn't render: (1) is the email non-null and trimmed; (2) does the URL hit secure.gravatar.com/avatar/<32 hex chars>; (3) is d= a valid token or a URL-encoded absolute URL; (4) does the account actually have a Gravatar (many don't — you're seeing the default, which is correct behavior); (5) after a v1.6 bump, did Composer resolve the classes you actually import, or silently swap the branch?

Extension points are thin by design. There's no event dispatcher, no URL-generator interface to decorate. If you need signed URLs, a CDN prefix, or multi-provider fallback (Gravatar → local → initials), write your own Avatar service and use the bundle only as a reference implementation. At a handful of stars and zero dependents, treat it as a well-organized snippet, not a foundation you build on — pin the version, note which branch matches your Symfony ceiling, and be prepared to vendor the class if it goes stale.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels