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

Laravel Reaction Laravel Package

jobmetric/laravel-reaction

jobmetric/laravel-reaction is a Laravel package that adds a flexible reaction system to Eloquent models, letting users like, dislike, or react to posts and other records through configurable reaction types and simple polymorphic integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require <vendor>/laravel-reaction:^3.0
php artisan vendor:publish --tag="reaction-config"
php artisan vendor:publish --tag="reaction-migrations"
php artisan migrate

Verify the published service provider name before running anything: 3.0.0 renames the provider to match reaction, so if you ever registered the old provider manually in bootstrap/providers.php or config/app.php, remove it and re-run php artisan config:clear / composer dump-autoload.

Wire up both sides of the relationship

use Vendor\Reaction\Concerns\HasReaction;

class Post extends Model
{
    use HasReaction;   // the "reactable" side
}
use Vendor\Reaction\Concerns\CanReact;

class User extends Model
{
    use CanReact;      // the "reactor" side
}

First use case — react to a post

$post->react($user);            // create a reaction
$post->reactions()->count();    // read raw rows
$post->isReactedBy($user);      // existence check
$post->unreact($user);          // remove

Exact method names on HasReaction changed in 3.0.0 — open src/Concerns/HasReaction.php first and confirm the signatures before writing them into controllers. That trait is the single most important file in the package; Reaction model, the migration, and the config file are the next three.

Files to read in this order

  1. config/reaction.php — table name, prune_days, headers, and reaction type/source definitions.
  2. src/Concerns/HasReaction.php and src/Concerns/CanReact.php — the public API surface.
  3. src/Models/Reaction.php — fillable fields, casts, relationships.
  4. The published migration — reactable_id / reactable_type columns and indexes.
  5. src/Events/* — hook notifications and counters here, not in controllers.

Implementation Patterns

Idempotent toggle endpoint. Wrap react/unreact in a single action with a unique constraint behind it:

public function toggle(Request $request, Post $post)
{
    $request->user()->hasReactedTo($post)
        ? $request->user()->unreactTo($post)
        : $request->user()->reactTo($post);

    return back();
}

Use firstOrCreate semantics rather than a raw create() so double-submits don't 500 on the unique index.

Reaction types. The LikeType enums were removed in 3.0.0, so type/variant handling is now your responsibility. Two sane options:

  • Pass a string/backed-enum value through reactTo($post, type: ReactionKind::Heart->value) and validate it against your own enum.
  • Keep the set in config/reaction.php and validate with Rule::in(config('reaction.types')), so ops can change it without a deploy.

Either way, don't rebuild the removed enum — the package intentionally stopped owning that vocabulary.

Counting without N+1. Aggregate counts in the query, not per-model loops:

Post::withCount('reactions')->paginate();

For high-traffic feeds, denormalize into a reactions_count column updated from the package's reaction-created / reaction-deleted events, and read the column on index pages.

Events as the integration seam. 3.0.0 adds a redesigned event set. Listen for them to send notifications, bump counters, or write audit rows — this keeps your controllers package-agnostic and survives a future major bump better than trait call sites do. Register listeners in EventServiceProvider and assert the event class names against src/Events/ since they were all renamed in this release.

Custom reactor/reactable sources. The new InvalidReactionSourceException is thrown when a model isn't a valid source for a reaction. That's your guardrail, not a bug: register/allow the intended models (via the config and the CanReact / HasReaction traits) and catch the exception at your HTTP boundary to convert it into a 422 rather than a 500.

Pruning old reactions. prune_days in config plus the package's prune command (verify the signature in the README) means reactions older than N days get deleted. Schedule it:

Schedule::command('reaction:prune')->daily();

This is the pattern for engagement data you don't want growing forever — but it is destructive, so set it deliberately (or null/0 to disable) per environment.

Localized labels. The new language line plus hasTranslation on the service provider means reaction type labels can be returned translated. Publish translations (--tag="reaction-translations") and resolve labels through the translator instead of hardcoding strings in Blade.

API responses and headers. The new headers config key controls headers emitted on package responses. If you front this with a SPA or a different origin, check that key before debugging CORS by hand.

Testing. A reaction factory ships in 3.0.0 — use it in feature tests instead of hand-building rows, and note the factory changed field names alongside the model rename:

$reaction = Reaction::factory()->for($post, 'reactable')->create();

Gotchas and Tips

3.0.0 is a rename-everything release — treat it as a hard cutover. HasLike → HasReaction, Like → Reaction, the package itself is now laravel-reaction, the service provider was renamed, and the LikeType enums were deleted. Grep your codebase for Like, likeable, and the old namespace before bumping; nothing here is aliased for backward compatibility.

The migration renames columns, not just tables. likeable_id / likeable_type became reactable_*, and the table name changed. Re-publishing migrations is not an upgrade path — for existing data you need a hand-written migration that renames the table and columns and preserves rows, then a rollback plan. Test on a database copy; the polymorphic type column also holds fully-qualified class names, so update any stored values if you rename your own models in the same release.

Config cache bites after publishing. New keys (prune_days, headers, table name) won't appear until php artisan config:clear. In CI, a stale cached config from a prior install is the most common cause of "table not found" after the rename.

Morph maps and the renamed type column. If you use Relation::enforceMorphMap(), the reactable_type values must match your morph aliases — otherwise HasReaction lookups silently return empty collections rather than erroring. Set the morph map before running seeders.

Duplicate reactions. The package gives you the relation, not the uniqueness policy. Add a composite unique index on (reactor_id, reactable_id, reactable_type) in a follow-up migration, and handle the resulting QueryException (or use the package's toggle helper if one exists). Without it, concurrent taps will produce duplicate rows and inflated counts.

Pruning is irreversible and timezone-sensitive. prune_days compares against a timestamp column; if your app writes UTC but your DB session timezone differs, you can prune a day too early or too late. Confirm both before enabling the schedule in production.

EnumMacros replaced the shipped enums. If you were calling enum helpers (EnumToArray etc.) from the old enums directory, rewrite those calls to the macro equivalents or just inline the array — the helper no longer lives where it did.

Old event listeners fail silently. Laravel only errors on unresolvable event classes at dispatch, and only if you're strict about it. All event files were renamed; audit EventServiceProvider::$listen and any Event::listen calls, and run one reaction end-to-end with logging on to confirm listeners actually fire.

InvalidReactionSourceException is usually a trait problem. Nine times out of ten it means the target model is missing HasReaction (or the reactor is missing CanReact), not that the config is wrong. Check trait composition first.

Check the core-package bump. 3.0.0 pins the underlying package core to 1.20. If another dependency in your app pins an older major of that same core, you'll get an unsatisfiable constraint — resolve it before starting the rename work, since it can force you onto a different PHP or Laravel minor.

Upgrade order that works: bump the dependency → mechanical class/trait/namespace rename → data-migration for the table and columns → config republish and diff → event listener audit → run the factory-based tests → enable pruning last. Doing the data migration last means every failed step is reversible with a git checkout.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast