roundly-consulting/reviews-for-laravel
Add reviews and ratings to any Laravel model with this ready-made package. Ship migrations, traits, and helpers for creating, moderating, and displaying reviews, so you can wire up star ratings and customer feedback in minutes instead of building it from scratch.
author/reviewable shape was the package's call. The new reviews.resources.author and reviews.resources.reviewable config keys move that decision to us: we can ship {id, type} only, or extend with a display name, avatar, or title by pointing the config at our own JsonResource. That is a product decision about what a client needs, not an engineering detail — it determines whether a review list renders in one request or needs a second hydration call.author.type / reviewable.type now render the related model's class basename (User) and never the FQCN (App\Models\User), with or without a morph map. Type strings stop churning when we reorganize namespaces, and stop leaking internal structure to third-party or mobile clients. This is the kind of decision that normally surfaces as a mid-quarter client bug; here it is a documented, one-time shape change.null, and both keys are always present. That turns a "review author was deleted" edge case from a 500 or a missing field into a stable, documented schema the client can branch on — which is what legal/privacy deletion requests actually need to be safe.ReviewCollection eager-loads authors and subjects for its reviews and their already-loaded responses in one query per morph type, and a single ReviewResource does the same for loaded responses. That sets the floor for a product listing page. The caveat is real and belongs in the plan: when we render reviews ourselves, we must eager-load author and reviewable or we reintroduce a query per review. N+1 is now a code-review rule, not a package guarantee.Adopt when:
author and reviewable to be yours. The reviews.resources.* keys are the deciding feature: if a review card needs "Jane D. · 4★" with an avatar, you write one JsonResource per relation and the package composes it.JsonResource value in those config keys throws InvalidConfigurationException naming the offending key, and a blank value falls back to the default — misconfiguration surfaces at boot/render with a pointed message rather than as a silently wrong payload in production.Look elsewhere, or budget for migration, when:
type shape. Anything parsing author.type as App\Models\User breaks on upgrade. Inventory consumers (mobile app releases you cannot force-update, partner integrations, cached responses, search indexes) before adopting 1.2.0; if you cannot coordinate the cutover, stay pinned and plan it as a versioned API change.reviews table and two endpoints may be cheaper than a dependency plus its config surface.To executives: We need reviews on our catalog, and we were going to spend a quarter building it. This package now covers the parts that are expensive to get right — attaching reviews to any entity, threaded responses, and a serialized API shape we control through two config keys. It also makes two things we get asked about in reviews of our product decisions cheap: deleted users and deleted subjects render as null with a stable schema instead of breaking a page, and the type field carries User rather than our internal namespace, so we stop exposing and churning implementation detail in a public contract. The headline risk is one payload change: any client currently reading App\Models\User must move to User. That is a known, finite piece of coordination work, not an open-ended one.
To engineering: 1.2.0 is mostly a good trade. reviews.resources.author / .reviewable let you swap in your own JsonResource for a name, avatar, or title; blank falls back to the default, and a non-JsonResource throws InvalidConfigurationException naming the key, so bad config fails loudly. ReviewResource::authorOf() / reviewableOf() and the published stubs are the intended extension points — use them. Two behaviours to internalize: author/reviewable are built from the related models, so a deleted row renders null with both keys always present (good — it means no null-check gymnastics on the client, but do not assume the object is there); and eager-loading is handled for ReviewCollection and for loaded responses on a single ReviewResource, not for hand-rolled rendering — new ReviewResource($review) or ReviewResource::collection() without with(['author', 'reviewable']) is a query per review. The rest is maintenance (dev testing ^1.6, CI on tmpfs for the Postgres/MySQL legs) with no product impact.
Recommendation: Adopt, as a minor upgrade with a known breaking field change. Sequence it as: (1) inventory consumers of the type string, (2) publish the intended payload shape from your custom resources before the version bump, (3) upgrade, (4) add an eager-loading check to review for any hand-rolled review endpoint. Pin the current version until step 2 is agreed with client owners.
How can I help you explore Laravel packages today?