corepine / actions
Polymorphic action tracking for Laravel (likes, dislikes, reactions) with synced counters.
Requires
- php: ^8.2
- illuminate/contracts: ^11.0|^12.0
- illuminate/database: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
This package is not auto-updated.
Last update: 2026-03-09 09:08:18 UTC
README
corepine/actions is a Laravel package for polymorphic user actions (upvotes, downvotes, reactions) with synced aggregate counters.
Install
composer require corepine/actions php artisan actions:install
This publishes:
config/corepine-actions.php- actions migrations
app/Casts/ActionType.phpcast stub
Optional flags:
php artisan actions:install --migrate php artisan actions:install --force
Quick Start (Service / Facade)
use Corepine\Actions\Facades\Actions; $isUpvoted = Actions::for($comment)->by($user)->upvote(); // true = set, false = removed $isDownvoted = Actions::for($comment)->by($user)->downvote(); Actions::for($comment)->by($user)->reaction('👋'); Actions::for($comment)->by($user)->reaction(null); // remove reaction $upvotes = Actions::for($comment)->count('upvote'); $downvotes = Actions::for($comment)->count('downvote');
Custom Action Types
Option A: add extra types directly in config:
'action_types' => ['bookmark'],
Option B: publish app/Casts/ActionType.php, override types(), and point config:
'action_type_cast' => \App\Casts\ActionType::class,
If you omit action_type_cast, the package uses Corepine\Actions\Casts\ActionType by default.
Then use custom actions:
Actions::for($comment)->by($user)->toggle('bookmark'); Actions::for($comment)->count('bookmark');
HasActions Concern
Use the built-in concern on your actionable models:
use Corepine\Actions\Models\Concerns\HasActions; class Comment extends Model { use HasActions; }
Then call helpers directly from the model:
$comment->upvoteBy($user); $comment->downvoteBy($user); $comment->reactBy($user, '👋'); $comment->upvotedBy($user); $comment->upvotesCount(); $comment->formattedUpvotesCount(); $comment->syncAllActionCounts(); // Manual cleanup for this actionable: $comment->clearActionsAndCounts(); // Alias: $comment->deleteActionsAndCounts();
When the actionable model is deleted, HasActions auto-cleans related actions and action_counts rows.
Reaction Groups (Render Ready)
For emoji reaction chips like 👋 6 or ❤️ 2.5K, use grouped reactions:
$groups = $comment->reactionGroups(); // Each item has: // ['reaction' => '👋', 'count' => 6, 'formatted_count' => '6'] // ['reaction' => '❤️', 'count' => 2500, 'formatted_count' => '2.5K']
If you already have a precomputed count, format it directly:
$comment->formattedReactionsCount(2500); // 2.5K
Counter Consistency
Counters are stored in action_counts and updated automatically from Action model created/deleted events (including service writes).
If you need to rebuild counters for a specific model:
Actions::for($comment)->syncAllCounts();
If you want syncAllCounts() to include custom zero-bucket types, append them:
Actions::for($comment)->syncAllCounts(['bookmark']);
If you need to delete everything for one actionable and keep tables in sync:
Actions::for($comment)->clear();
Tables
actions: one row per actor + actionable + typeaction_counts: aggregate count per actionable + type