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 Favorite Laravel Package

christiankuri/laravel-favorite

A Laravel package that adds favorite functionality to Eloquent models. Use a simple trait to let users favorite, unfavorite, toggle, and check favorites on any model, with counts, polymorphic relationships, and query scopes.

Deep Wiki
Context7

Getting Started

Install with Composer and let Laravel 5.5+ package discovery register the service provider:

composer require christiankuri/laravel-favorite
php artisan vendor:publish --provider="ChristianKuri\LaravelFavorite\FavoriteServiceProvider"
php artisan migrate

Publish the config file too if you need to override the Favorite model or table name. On Laravel < 5.5, add ChristianKuri\LaravelFavorite\FavoriteServiceProvider::class to config/app.php.

Add the two traits: ChristianKuri\LaravelFavorite\Traits\CanFavorite on the model that performs the action (usually User), and ChristianKuri\LaravelFavorite\Traits\Favoriteable on every model that can be favorited (Post, Comment, Product, …).

Your first use case is a three-line controller:

$user->toggleFavorite($post);
$post->favoriteCount;              // accessor
$user->hasFavorited($post);        // bool

Look first at src/Traits/CanFavorite.php and src/Traits/Favoriteable.php — the entire public API lives there. Then read src/Models/Favorite.php and the published migration stub so you know the exact column names before writing queries or building endpoints.

Caveat before you commit: the last tagged release is 2019. Verify composer.json constraints against your Laravel version; on Laravel 9/10 it usually works because it is plain Eloquent, but you may need a fork or composer require --ignore-platform-reqs, and you should not expect PHP 8.2 deprecation fixes upstream.

Implementation Patterns

Toggle endpoint. The package ships no routes or controllers, so you own the HTTP layer. A typical pattern:

// POST /posts/{post}/favorite
public function store(Post $post)
{
    $this->authorize('favorite', $post);
    auth()->user()->toggleFavorite($post);

    return back()->with('favoriteCount', $post->fresh()->favoriteCount);
}

Returning the fresh count lets a Blade/Alpine button update without a reload.

Blade button. Keep it a partial so you can reuse it across models:

<button data-id="{{ $post->id }}" class="favorite {{ $post->isFavoritedBy(auth()->user()) ? 'active' : '' }}">
    {{ $post->favoriteCount }}
</button>

API resources. Avoid calling hasFavorited once per row — that is one query per item. Instead eager load and derive the flag:

$posts = Post::withCount('favorites')
    ->with(['favorites' => fn ($q) => $q->where('user_id', auth()->id())])
    ->paginate();

// in the resource:
'is_favorited' => $this->favorites->isNotEmpty(),
'favorite_count' => $this->favorites_count,

Leaderboards / sorting. withCount('favorites') composes cleanly with ordering:

Post::withCount('favorites')->orderByDesc('favorites_count')->take(10)->get();

Domain events. The package does not fire events, so wrap writes in an action class and dispatch your own PostFavorited / PostUnfavorited events from there. That is also the right place to bust cache keys and notify the post author.

Caching counts. favoriteCount is convenient but hits the database. For hot pages, cache post.{id}.favorites and invalidate inside the same action class that calls favorite()/unfavorite().

Combining with other packages. If you also use a follow or bookmark package, keep the trait sets separate and let your action classes orchestrate them — do not try to merge the favorites table with another package's.

Gotchas and Tips

  • Unmaintained since 2019. Pin the version, read the published migration and traits before deploying, and be ready to fork. There is no upgrade path if a future Laravel changes polymorphic behavior.
  • Migration assumptions. The shipped migration is written for a users table with integer keys. If your auth model is not User, or you use UUID/ULID primary keys, edit the published migration and the Favorite model's relation key before running php artisan migrate.
  • N+1 on counts. favoriteCount and isFavoritedBy() each issue a query. On index pages this is the single biggest performance trap. Replace with withCount('favorites') and a filtered eager load.
  • favorites() returns Favorite pivot models, not your target models. Use $user->favorites(Post::class) when you want the actual posts. Mixing these up is the most common source of "why is my collection full of empty rows?"
  • No authorization layer. Anyone can favorite anything the route resolves. Add a Gate/Policy check per model type — otherwise users can favorite private or soft-deleted records by guessing IDs.
  • Toggle races and duplicates. If the published migration does not include a unique index across (user_id, favoriteable_id, favoriteable_type), concurrent toggles can create duplicate rows. Add the index in a follow-up migration, and wrap toggle calls in DB::transaction().
  • Soft-deleted targets. Favorite rows survive when a Post is deleted or soft-deleted. Filter with whereHas('favoriteable') in listing queries, and schedule a cleanup job for orphaned favorites.
  • Config caching. After publishing config/favorite.php or swapping the Favorite model, run php artisan config:clear and composer dump-autoload; otherwise you will debug a stale binding for an hour.
  • Extension points. Swap the Favorite model via config, subclass the traits, and register an observer on Favorite (created/deleted) if you want lifecycle hooks without forking. That observer is the cleanest place to fire domain events and update counters.
  • Testing. Use RefreshDatabase and assert on the favorites table (assertDatabaseHas) rather than on the collection API — it makes failures much easier to read.
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