corepine/actions

Polymorphic action tracking for Laravel (likes, dislikes, reactions) with synced counters.

Maintainers

Package info

github.com/corepine/actions

pkg:composer/corepine/actions

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.1.0 2026-03-08 20:28 UTC

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.php cast 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 + type
  • action_counts: aggregate count per actionable + type