tomshaw/laravel-gravatar
Gravatar avatars, profiles, and QR codes for Laravel. Zero config to start, configurable when you need it, with a Blade directive and component for rendering, a facade and fluent builder for URLs, plus model support, profiles, QR codes, and hashing.
Architecture fit. The package is a thin, stateless adapter over Gravatar's public HTTP endpoints — one service provider, one facade, one Blade component/directive, one opt-in Eloquent concern (HasGravatar). No migrations, no queues, no global scopes, no forced base classes. That is the right shape: it can be dropped into an existing Laravel app without touching the domain model. Two details signal real Laravel fluency rather than a wrapper thrown together: the trait deliberately prefixes every method (gravatarUrl(), not gravatar()) to avoid Eloquent's attribute-resolution LogicException, and the component distinguishes default (rendered by Gravatar) from fallback (shown when the request fails outright). Both are the kind of thing you usually only learn from production incidents.
Where the value actually is. Roughly 80% of the surface — src() and the fluent builder — is a SHA256 of a lowercased email plus a query string. Any mid-level developer reproduces that in an afternoon. The durable value is the periphery: the component's 2x srcset and onerror fallback, the cached profile client (misses cached too), QR generation, and Gravatar::fake() for deterministic tests. Judge the package on those, not the URL builder.
Integration feasibility. Composer-only, auto-discovered, zero config required, config and views publishable. The blocker is the hard platform floor: PHP 8.5 and Laravel 13. That excludes essentially every maintained estate today. Earlier tags (0.2.x) supported Laravel 12, so the version matrix — not the package — determines feasibility. Confirm the tag that matches your runtime before committing.
Technical risk.
InvalidArgumentException. Failing loudly beats a dead URL in dev — but a bad value in config/gravatar.php becomes a page-wide 500 in production.Key questions for the maintainer: Is the PHP 8.5 floor intentional (narrower CI matrix) or incidental? Will features be backported to the Laravel 12 line? What's the support window for Laravel 14? Do has()/profile() expose timeouts, retries, and 429s, or swallow them? Is there a security policy? Any intent to support alternative providers (Libravatar, self-hosted proxies) for on-prem or restricted-network deployments?
Stack fit. Works in any Laravel 13 deployment: Blade/Livewire use the component or directive directly; Inertia and SPA front-ends call the facade server-side and pass URLs down as props. Compatible with any cache store, which matters for the profile client in multi-node setups.
Migration path. Two common origins:
md5(strtolower(trim($email))) in a User::avatar() accessor). Swap to $user->gravatarUrl(); the trait's configurable gravatarEmailColumn() plus per-model override handles non-standard columns.thomaswelton/laravel-gravatar, creativeorange/gravatar). Map Gravatar::get() → Gravatar::src() / Gravatar::for()->url(). Keep hash: 'md5' during cutover so existing URLs, CDN cache keys, and any avatar_url columns denormalized in the database still resolve, then flip to SHA256 as a separate, independently revertible change.Compatibility checks before install: PHP/Laravel floors against your runtime; Blade component namespace collision on <x-gravatar>; model method-name collisions with any existing HasGravatar or avatar accessor; cache prefix isolation if you reuse a shared Redis; egress allowlists for secure.gravatar.com and the QR endpoint (Gravatar is blocked on some corporate and national networks).
Sequencing.
fallback pointing at a local asset from day one.Avatar service of your own (see mitigation below).jobTitle or location.Gravatar::fake() into the test suite so CI never depends on Gravatar's availability.Every step is independently revertible because URL building is stateless and the profile path is additive.
Maintenance. Small surface, MIT, trivially forkable. Assign a named owner and a dependency-watch policy, knowing the platform floor couples package upgrades to framework upgrades. Budget one to two days a year, plus a fork contingency. The highest-leverage mitigation is architectural, not procedural: wrap the facade in your own two-method Avatar service and call that everywhere. It costs an hour and makes the package replaceable by a 50-line internal class if the repo goes dark.
Support. Community-only, no SLA, GitHub issues. Gravatar itself is Automattic-run with no SLA either — you are inheriting two layers of unsupported dependency. Pin exact versions and vendor the source if procurement requires provenance.
Scaling. URL construction is O(1), free, and adds no load. The profile client is the only scaling consideration: one outbound HTTP call per uncached email at a 3600s default TTL. Caching misses is the smart design choice here — the majority of users have no Gravatar account, and without negative caching every page load for those users is a wasted round trip. Real risks are cold-cache stampede across pods after a deploy (warm the cache or add jitter/locking) and template-level misuse — a profile() call inside a Blade partial over a user list turns one page into N synchronous outbound requests. Prefetch asynchronously or restrict to detail views.
Failure modes.
fallback + onerror solves, and exactly what default does not solve, since default requires Gravatar to be reachable. Train the team on that distinction — it is the single most consequential piece of tribal knowledge in this package.Ramp-up. A competent Laravel developer is productive in under an hour given the zero-config default and the README's parameter-reference table and testing section — documentation quality is genuinely above average here. The training burden is not the API; it's four decision points: default vs fallback, SHA256 vs MD5, never call HTTP methods from a view, and validate config at boot rather than at render.
How can I help you explore Laravel packages today?