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

liquidstyle/laravel-likeable

Add like and unlike functionality to any Eloquent model with this lightweight Laravel package. It stores likes in a polymorphic table, offers simple trait-based integration, and lets you query liked models and counts with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up

composer require <vendor>/<wishlist-package>:^2.2
php artisan vendor:publish --provider="<Vendor>\Wishlist\WishlistServiceProvider"
php artisan migrate

Publish both the config file and migrations in one pass; the wishlist table typically ships with a polymorphic wishlistable relation plus a unique composite index on (user_id, wishlistable_type, wishlistable_id), and you want that index in place before you start writing toggles in a loop.

Where to look first

  1. config/wishlist.php — model bindings, table names, guest-list behaviour, and the route/middleware toggles. This is the file that answers most "why isn't it saving?" questions.
  2. The HasWishlist (or similarly named) trait — this is the contract you attach to User (and optionally to Product for reverse lookups). Read its method list before writing any controller code; it almost certainly already exposes add/remove/toggle/has/clear.
  3. The migration stub — confirm the morph type column length if you use fully-qualified class names rather than a morph map.

First use case

The 80% case is: authenticated user toggles a product in and out of their wishlist from a product card, then sees the list on /wishlist.

// User model
use <Vendor>\Wishlist\Concerns\HasWishlist;

class User extends Authenticatable
{
    use HasWishlist;
}

// Controller
public function toggle(Product $product)
{
    auth()->user()->toggleWishlist($product);

    return back();
}

// Blade
@if (auth()->user()->hasInWishlist($product))
    <form method="POST" action="{{ route('wishlist.toggle', $product) }}">@csrf ...</form>
@endif

Get that loop working end-to-end before touching guests, merging, or API responses — it validates your trait wiring, migration, and config in one shot.

Implementation Patterns

Thin controller, fat trait. Resist wrapping every call in a repository. The package's value is that $user->addToWishlist($product) is already the domain action. Put your policy/authorisation in a FormRequest or policy, not in an adapter layer around the package.

Toggles belong in POST, not GET. A wishlist toggle is state-changing, so it should be a POST/DELETE with CSRF, returning back() for HTML or a small JSON payload for AJAX. Pattern that scales well:

public function toggle(Request $request, Product $product)
{
    $added = $request->user()->toggleWishlist($product);

    return $request->expectsJson()
        ? response()->json(['wishlisted' => $added, 'count' => $request->user()->wishlist()->count()])
        : back();
}

Eager-load the relation on listing pages. $user->wishlist() returns a morph-to-many; on an index page you almost always want ->with('wishlistable') so the product data comes along. If your package exposes a query builder, use it rather than ->wishlist()->get() followed by lazy loads.

Check-in-list rendering. For grids of 24+ products you do not want one query per card. Either load the user's wishlisted IDs once into a collection and use ->contains('id', $product->id), or use the package's hasInWishlist only where the count is small. This is the single biggest performance decision in a wishlist feature.

Guest wishlists → merge on login. If the config allows session-backed lists, the standard workflow is: guest adds items to a session list, then on Login/Registered events you merge the session list into the persisted list. Wire this in an event listener, not in the login controller — it keeps socialite/API login flows covered too. Decide the conflict policy up front (union vs. replace) and document it.

API surface. For a JSON API, return the wishlist as a resource collection with a stable shape (id, product_id, added_at) rather than serialising the raw pivot. Clients that key off array indices will break the first time you change ordering.

Testing. Use the trait's public methods to seed state in factories/test setup rather than inserting rows directly — that way a schema or method-name change in a minor release shows up as a failing test rather than a silent production bug. Add one feature test per route and one test for the guest-merge path.

Gotchas and Tips

The v2.2.6 "typo fix" needs a diff check. A typo fix in "Wishlist Methods" is usually a docblock or exception-message correction — harmless. But if the typo was in a public method name (e.g. addToWhishlist → addToWishlist), any calling code using the old spelling breaks. Before bumping: read the commit, not just the release note. Then grep -rn "Whishlist\|wishlist\b" app/ resources/ tests/ for the misspelling, and let your test suite (which, per above, calls the real methods) confirm nothing is orphaned. Pin with ^2.2.6 rather than ^2.2 if you want certainty about which typo state you're on.

Unique constraint collisions. Double-submits (double-clicked toggle buttons) will throw a QueryException on the composite unique index. Either use insertOrIgnore-style semantics in your controller, disable the button on submit, or catch and treat as idempotent success. Do not silently swallow all query exceptions — only the duplicate-key one.

Morph map surprises. If you register a morph map (Relation::enforceMorphMap([...])), older rows written with FQCNs will no longer resolve. Migrate existing wishlistable_type values in the same deploy as the morph map registration, and add the wishlist's own model to the map if it appears in any polymorphic relation.

Soft deletes and orphans. If Product is soft-deletable, wishlist rows survive and your listing query will either error or silently drop items depending on how the morph relation is loaded. Add a whereHas('wishlistable') (or a scheduled prune) and decide whether a deleted product should remove the wishlist entry.

Guest sessions and cookie lifetime. Session-backed guest lists evaporate when the session expires — usually fine, but communicate it in the UI ("sign in to save your list") rather than letting users lose items without warning. Also confirm the package's guest mode isn't enabled in an API-only context where there's no session.

Extension points, in order of preference. (1) Config-level model swap — subclass the package's wishlist model and rebind in config/wishlist.php; (2) model events on your own subclass (created, deleted) for analytics/notifications; (3) publishing and editing the migration only before first deploy. Avoid editing vendor files or forking for anything short of a genuine bug — this package is shipping patch releases, so staying on the upstream branch is worth more than the flexibility.

Debugging checklist for "it won't save": is the trait on the model the package expects (User, not a custom guard model)? Is the morph type column wide enough? Is the item you're passing actually a persisted model instance (not new Product)? Is guest mode off? That covers roughly every report you'll see in the first week.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
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