turahe/laravel-likeable

Trait for Laravel Eloquent models to allow easy implementation of a 'like' or 'favorite' or 'remember' feature.

v1.2.0 2025-07-26 15:31 UTC

This package is auto-updated.

Last update: 2025-07-26 15:38:46 UTC


README

Latest Stable Version Total Downloads License Tests Release Version Bump Code Coverage PHP Version Laravel Version Conventional Commits Semantic Versioning Tests

Laravel Likeable simplifies management of Eloquent model's likes & dislikes. Make any model likeable & dislikeable in a minute!

Contents

Features

  • Designed to work with Laravel Eloquent models
  • Using contracts to keep high customization capabilities
  • Using traits to get functionality out of the box
  • Most part of the logic is handled by the LikeableService
  • Has Artisan command likeable:recount {model?} {type?} to re-fetch likes counters
  • Likeable model can have Likes and Dislikes
  • Likes and Dislikes for one model are mutually exclusive
  • Get Likeable models ordered by likes count
  • Events for like, unlike, dislike, undislike methods
  • Comprehensive test coverage (79 tests, 0 failures)
  • Following PHP Standard Recommendations:
  • Fully tested with PHPUnit
  • Continuous Integration with GitHub Actions
  • Code style checks with PHP CS Fixer
  • Static analysis with Larastan
  • Automated release workflows with conventional commits
  • Semantic versioning and changelog generation

Installation

First, pull in the package through Composer.

$ composer require turahe/laravel-likeable

If you are using Laravel 5.5+ you can skip the register package part.

Register package on Laravel 5.4 and lower

Include the service provider within app/config/app.php.

'providers' => [
    Turahe\Likeable\LikeableServiceProvider::class,
],

Perform Database Migration

At last, you need to publish and run database migrations.

$ php artisan vendor:publish --provider="Turahe\Likeable\LikeableServiceProvider" --tag=migrations
$ php artisan migrate

Usage

Prepare likeable model

Use Likeable contract in model which will get likes behavior and implement it or just use Likeable trait.

use Turahe\Likeable\Contracts\Likeable as LikeableContract;
use Turahe\Likeable\Traits\Likeable;
use Illuminate\Database\Eloquent\Model;

class Article extends Model implements LikeableContract
{
    use Likeable;
}

Available methods

Likes

Like model
$article->like(); // current user
$article->like($user->id);
Remove like mark from model
$article->unlike(); // current user
$article->unlike($user->id);
Toggle like mark of model
$article->likeToggle(); // current user
$article->likeToggle($user->id);
Get model likes count
$article->likesCount;
Get model likes counter
$article->likesCounter;
Get likes relation
$article->likes();
Get iterable Illuminate\Database\Eloquent\Collection of existing model likes
$article->likes;
Boolean check if user liked model
$article->liked; // current user
$article->liked(); // current user
$article->liked($user->id);

Checks in eager loaded relations likes & likesAndDislikes first.

Get collection of users who liked model
$article->collectLikers();
Delete all likes for model
$article->removeLikes();

Dislikes

Dislike model
$article->dislike(); // current user
$article->dislike($user->id);
Remove dislike mark from model
$article->undislike(); // current user
$article->undislike($user->id);
Toggle dislike mark of model
$article->dislikeToggle(); // current user
$article->dislikeToggle($user->id);
Get model dislikes count
$article->dislikesCount;
Get model dislikes counter
$article->dislikesCounter;
Get dislikes relation
$article->dislikes();
Get iterable Illuminate\Database\Eloquent\Collection of existing model dislikes
$article->dislikes;
Boolean check if user disliked model
$article->disliked; // current user
$article->disliked(); // current user
$article->disliked($user->id);

Checks in eager loaded relations dislikes & likesAndDislikes first.

Get collection of users who disliked model
$article->collectDislikers();
Delete all dislikes for model
$article->removeDislikes();

Likes and Dislikes

Get difference between likes and dislikes
$article->likesDiffDislikesCount;
Get likes and dislikes relation
$article->likesAndDislikes();
Get iterable Illuminate\Database\Eloquent\Collection of existing model likes and dislikes
$article->likesAndDislikes;

Scopes

Find all articles liked by user
Article::whereLikedBy($user->id)
    ->with('likesCounter') // Allow eager load (optional)
    ->get();
Find all articles disliked by user
Article::whereDislikedBy($user->id)
    ->with('dislikesCounter') // Allow eager load (optional)
    ->get();
Fetch Likeable models by likes count
$sortedArticles = Article::orderByLikesCount()->get();
$sortedArticles = Article::orderByLikesCount('asc')->get();

Uses desc as default order direction.

Fetch Likeable models by dislikes count
$sortedArticles = Article::orderByDislikesCount()->get();
$sortedArticles = Article::orderByDislikesCount('asc')->get();

Uses desc as default order direction.

Events

On each like added \Turahe\Likeable\Events\ModelWasLiked event is fired.

On each like removed \Turahe\Likeable\Events\ModelWasUnliked event is fired.

On each dislike added \Turahe\Likeable\Events\ModelWasDisliked event is fired.

On each dislike removed \Turahe\Likeable\Events\ModelWasUndisliked event is fired.

Console commands

Recount likes and dislikes of all model types
$ php artisan likeable:recount
Recount likes and dislikes of concrete model type (using morph map alias)
$ php artisan likeable:recount --model="article"
Recount likes and dislikes of concrete model type (using fully qualified class name)
$ php artisan likeable:recount --model="App\Models\Article"
Recount only likes of all model types
$ php artisan likeable:recount --type="like"
Recount only likes of concrete model type (using morph map alias)
$ php artisan likeable:recount --model="article" --type="like"
Recount only likes of concrete model type (using fully qualified class name)
$ php artisan likeable:recount --model="App\Models\Article" --type="like"
Recount only dislikes of all model types
$ php artisan likeable:recount --type="dislike"
Recount only dislikes of concrete model type (using morph map alias)
$ php artisan likeable:recount --model="article" --type="dislike"
Recount only dislikes of concrete model type (using fully qualified class name)
$ php artisan likeable:recount --model="App\Models\Article" --type="dislike"

Extending

You can override core classes of package with your own implementations:

  • Models\Like
  • Models\LikeCounter
  • Services\LikeableService

Note: Don't forget that all custom models must implement original models interfaces.

To make it you should use container binding interfaces to implementations in your application service providers.

Use model class own implementation
$this->app->bind(
    \Turahe\Likeable\Contracts\Like::class,
    \App\Models\CustomLike::class
);
Use service class own implementation
$this->app->singleton(
    \Turahe\Likeable\Contracts\LikeableService::class,
    \App\Services\CustomService::class
);

After that your CustomLike and CustomService classes will be instantiable with helper method app().

$model = app(\Turahe\Likeable\Contracts\Like::class);
$service = app(\Turahe\Likeable\Contracts\LikeableService::class);

Change log

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Testing

This package includes comprehensive test coverage with 79 tests covering all functionality:

  • Common Use Tests - Basic like/unlike functionality
  • Counter Tests - Like/dislike counter management
  • Dislike Tests - Dislike-specific functionality
  • Enum Tests - LikeType enum functionality
  • Event Tests - Event dispatching
  • Exception Tests - Custom exception handling
  • Service Tests - Service layer functionality
  • Toggle Tests - Like/dislike toggle operations
  • Console Command Tests - Artisan command functionality

Running Tests

You can run the tests with:

$ vendor/bin/phpunit

Or use the composer script:

$ composer test

Test Coverage

Run tests with coverage:

$ composer test:coverage

This will generate HTML coverage reports in the coverage-html directory.

Continuous Integration

This package uses GitHub Actions for continuous integration. The CI pipeline includes:

Test Matrix

  • PHP Versions: 8.3, 8.4
  • Laravel Versions: 11.x, 12.x
  • Test Coverage: Generated for PHP 8.3+ + Laravel 11/12.x

CI Jobs

  1. Tests: Runs the full test suite across all PHP/Laravel combinations with code coverage
  2. Static Analysis: Code style checking with PHP CS Fixer and Larastan across all PHP/Laravel combinations
  3. Lint: PHP syntax validation
  4. Coverage: Detailed code coverage reporting with HTML reports

Local Development

You can run the same checks locally:

# Run all checks
$ composer check

# Run specific checks
$ composer test          # Run tests
$ composer test:coverage # Run tests with coverage
$ composer lint          # Check code style
$ composer analyse       # Run static analysis

Code Coverage

Code coverage reports are generated and uploaded to Codecov.

Release Process

This package uses automated release workflows for version management and deployment.

Automated Release Workflows

Version Bump Workflow

  • Trigger: Pushes to main branch with conventional commits
  • Actions:
    • Runs tests, static analysis, and linting
    • Updates CHANGELOG.md with new entries
    • Creates release preparation PRs for new features
    • Follows Conventional Commits format

Release Workflow

  • Trigger: Pushing tags with v* pattern (e.g., v1.2.3)
  • Actions:
    • Validates code quality (tests, analysis, linting)
    • Generates changelog from git history
    • Creates GitHub release with assets
    • Uploads composer.json, CHANGELOG.md, and README.md as release assets

Creating a Release

  1. Prepare Changes: Ensure all changes follow conventional commit format
  2. Create Tag: Create and push a new version tag
    git tag v1.2.3
    git push origin v1.2.3
  3. Automated Process: The release workflow will automatically:
    • Run all quality checks
    • Generate release notes
    • Create GitHub release
    • Upload release assets

Conventional Commits

This project follows the Conventional Commits specification:

  • feat: - New features (triggers release preparation)
  • fix: - Bug fixes
  • docs: - Documentation changes
  • style: - Code style changes
  • refactor: - Code refactoring
  • perf: - Performance improvements
  • test: - Test additions or changes
  • build: - Build system changes
  • ci: - CI/CD changes
  • chore: - Maintenance tasks

Security

If you discover any security related issues, please email wachid@outlook.com instead of using the issue tracker.

Contributors

Alternatives

License

The MIT License (MIT). Please see License File for more information.

About Turahe

Turahe is a software development company focused on creating high-quality Laravel packages and applications. Visit us at turahe.id.