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.
1.2.0 is a feature release, not a maintenance bump. It adds pluggable author / reviewable resources to ReviewResource, changes the shape of the rendered type fields, and tightens query behaviour around review collections. Treat 1.2.x as the new baseline and start here:
Install / upgrade.
composer require roundly/<package>:"^1.2"
# or, in an existing app:
composer update roundly/<package> --with-dependencies
The sibling floors from 1.1.1 still apply (package-toolkit ^1.3.0, enums ^1.1.0, media-library ^1.1.2); the dev floor for roundly/testing has moved to ^1.6. --with-dependencies keeps the family coherent.
Look first at the service provider and the reviews.resources config block. The service provider is still the map of what is bindable and publishable, but the thing that actually changed in 1.2.0 lives in config: reviews.resources.author and reviews.resources.reviewable. Read those keys first — they decide what every review payload looks like.
Publish assets once, then diff — the stubs changed.
php artisan vendor:publish --provider="Roundly\\...\\ServiceProvider"
The published reviews-resources stubs now use ReviewResource::authorOf() / reviewableOf() and the new type shape. If you have already published them, diff rather than overwrite: your local copies are now out of date in a way that produces inconsistent JSON.
First use case. Render a single review and one of its responses, confirm author and reviewable both appear as {id, type} with type being User / Post (the class basename, not the FQCN). Then swap reviews.resources.author for your own JsonResource subclass and confirm the extra fields (name, avatar) show up. That exercises the whole feature in two steps.
Sanity-check resolved versions before debugging anything else:
composer show roundly/package-toolkit roundly/enums roundly/media-library
If roundly/testing resolves below 1.6 in dev, you are not actually running the 1.2.0 dependency set.
Pin the floors in your own manifest. Keeping sibling constraints aligned with the package's own floors avoids the "works locally, fails in CI" split that appears when a lock file is regenerated on a different machine:
{
"require": {
"roundly/package-toolkit": "^1.3.0",
"roundly/enums": "^1.1.0",
"roundly/media-library": "^1.1.2"
},
"require-dev": {
"roundly/testing": "^1.6"
}
}
Customise author/reviewable through config, not subclassing. The intended extension point is exactly one line of config:
// config/reviews.php
'resources' => [
'author' => \App\Http\Resources\ReviewAuthorResource::class,
'reviewable' => \App\Http\Resources\ReviewableResource::class,
],
Subclass ReviewAuthorResource / ReviewableResource (or the base JsonResource) and add your fields there. The package resolves these from the container at render time, so a config change is the whole integration — no provider override needed.
Build your own resource output from authorOf() / reviewableOf(). If you write a custom review resource, call ReviewResource::authorOf($review, $request) and reviewableOf($review, $request) rather than reaching into $review->author yourself. That keeps your resource consistent with the configurable types and with the basename type convention.
Eager-load author and reviewable whenever you render reviews yourself. ReviewCollection already batches authors and subjects (including those of loaded responses) into one query per morph type, but new ReviewResource($review) and ReviewResource::collection() do not magic that up. Add ->with(['author', 'reviewable', 'responses.author', 'responses.reviewable']) to the query backing the resource. This is the single biggest day-to-day performance lever in this release.
Standardise on the basename type in your own code and clients. Since 1.2.0, type is User, never App\Models\User, with or without a morph map. Anywhere you map type to an icon, route, or label, key off the basename (or better, off a config-driven lookup) so the change is a non-event for you next time.
Enums as the single source of truth. With enums ^1.1.0 in play, expect the package's options — collection names, types, statuses, config keys — to be expressed as backed enums rather than magic strings. Pass enum cases into package APIs, store .value at the persistence boundary, and use Rule::enum(...) for validation.
Media attachments through the library, not bespoke columns. media-library is a hard floor rather than an optional suggest, so register collections/conversions at the package's expected configuration point and use the library's disk/queue configuration instead of duplicating it.
Use the testing package for feature tests. roundly/testing ^1.6 is dev-only. Wire it into your tests/ bootstrap once and reuse it; do not import from it in application code, since it will not be installed in production.
Scoped upgrades in CI. Bump the sibling constraints in the same commit as the package version so a single composer update resolves the family coherently. Where you need determinism, commit the lock file and update it deliberately rather than letting the runner resolve freely.
type shape change is the one thing that can break consumers. author.type and reviewable.type used to be the FQCN; they are now the class basename. Anything comparing against App\Models\User — front-end switches, API assertions, stored JSON snapshots — needs updating. If you cannot change the consumer, swap in your own resource via reviews.resources.* and emit whatever shape you need.author and reviewable are always present and can be null. A related row that no longer exists renders null rather than a dangling {id, type} or a missing key. Make nullability explicit in your clients and in any TypeScript/JSON-schema contracts.JsonResource subclass throws InvalidConfigurationException naming the offending key — so check the message first, it tells you exactly which of the two keys is wrong. Interface-only or abstract class references will fail here.reviews.resources.*: php artisan config:clear && php artisan cache:clear, then composer dump-autoload -o if you moved classes. Toolkit-based packages frequently cache merged config, and a stale merge is the usual explanation.ReviewCollection batches; hand-built ReviewResource instances do not. If review counts grew in a list endpoint and queries scaled linearly, this is why.reviews-resources stubs. Stale published stubs predate authorOf()/reviewableOf() and the basename type, and will drift from the package's rendering.package-toolkit or enums, not the package you were upgrading. Read the conflict tree top-down and relax your own pins first; do not reach for --ignore-platform-reqs.composer require without an update, or a CI cache keyed on a stale lock, can leave you on the old set while composer.json claims 1.2.0. Verify with composer show.roundly/testing is require-dev. If it appears in a production build, check for a composer install --no-dev omission or an accidental require in your own manifest.enums first, then media-library), then bisect your reviews.resources config against the defaults. That isolates the culprit far faster than reading package source.How can I help you explore Laravel packages today?