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 Eloquent Where Not Laravel Package

protonemedia/laravel-eloquent-where-not

Adds whereNot and orWhereNot methods to Laravel Eloquent, letting you negate simple conditions, nested closures, and relationships cleanly. A small, expressive package for readable inverse WHERE clauses without raw SQL or awkward operator flipping.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package with Composer and verify the macro is registered by the package's service provider:

composer require protonemedia/laravel-eloquent-where-not

There is no config file or publish step. The first place to look is the package README and src/ — specifically the service provider that registers the whereNot macro on Illuminate\Database\Eloquent\Builder. Confirm which macros are actually registered in your installed version before designing around them.

Version 1.2.0 adds Laravel 9 support, so the package now installs cleanly on Laravel 9 projects. That is the practical ceiling: there is no release covering Laravel 10 or later, so treat 1.2.0 as the last known-good version for L9 and below.

The first use case is inverting a local scope without rewriting its conditions. Given:

// app/Models/User.php
public function scopeActive(Builder $query): void
{
    $query->where('status', 'active');
}

you can query the complement directly:

$inactive = User::whereNot('active')->get();
// SQL roughly: where not (status = 'active')

The same macro accepts a closure for ad-hoc constraints:

$users = User::whereNot(function (Builder $query) {
    $query->where('status', 'active')
          ->whereNotNull('email_verified_at');
})->get();

Treat this as a query-construction convenience, not a replacement for every != or whereDoesntHave. Reach for it when the condition you want to invert is already expressed as a scope or a grouped closure.

Implementation Patterns

Invert an existing scope instead of duplicating it. The highest-value pattern is keeping one canonical definition of a filter and deriving its complement:

// One source of truth
public function scopePublished(Builder $query): void
{
    $query->where('published_at', '<=', now())
          ->whereNull('archived_at');
}

// Complement, without restating the conditions
$unpublished = Post::whereNot('published')->get();

This is useful for admin screens, cleanup jobs, and "everything except this segment" reports.

Wrap compound constraints in a closure. When the condition involves multiple where calls, pass a closure so the whole group is negated rather than only the first clause:

$eligible = User::whereNot(function (Builder $query) {
    $query->where('status', 'active')
          ->where('plan', 'pro');
})->get();

Use it in query objects or repository helpers. If your app centralizes filters, expose intent-revealing methods rather than leaking the macro everywhere:

class UserQuery
{
    public function excludingActive(): Builder
    {
        return User::query()->whereNot('active');
    }
}

This keeps call sites readable and gives you one place to migrate to Laravel's native whereNot later.

Combine with relationship constraints carefully. For "does not have" logic, prefer native whereDoesntHave() — it is clearer and better understood by other developers. If you must negate a whereHas, group it explicitly and test the generated SQL.

Test both sides of the negation. Assert that a matching row is excluded and a non-matching row is included. Use toSql() and getBindings() in a unit test if the generated grouping matters.

Gotchas and Tips

The package is archived. It receives no maintenance, and 1.2.0 (Laravel 9 support) is the final supported target — expect dependency constraints to block Laravel 10 and newer. On modern Laravel the query builder includes a native whereNot, so the package is usually unnecessary. If you are on Laravel 9 or older and need it, pin to ^1.2 and plan a migration; for anything newer, use the native builder method, fork the package, or copy the macro into your own service provider.

Check your Laravel/PHP matrix before upgrading. 1.2.0 broadens illuminate/* constraints to include Laravel 9, but the package's own PHP constraint and test suite were written for that generation. If you are on a mixed-version monorepo or a PHP version outside its supported range, verify with composer why-not rather than assuming the new tag covers you.

SQL three-valued logic will surprise you. whereNot('active') produces not (status = 'active'), which does not include rows where status is NULL (the expression evaluates to NULL, not true). If "not active" should include nulls, write the condition explicitly:

User::where(function (Builder $query) {
    $query->where('status', '!=', 'active')
          ->orWhereNull('status');
})->get();

Watch orWhereNot precedence. As with any orWhere, an ungrouped OR condition can silently widen the result set. Always wrap OR negations in a nested where group:

User::where('tenant_id', $tenantId)
    ->where(function (Builder $query) {
        $query->whereNot('active');
    })
    ->get();

Only where constraints are meaningfully invertible. A scope that also adds select, with, join, orderBy, or limit will not behave as a clean negation. Keep negation-able scopes focused on where clauses, and split side effects into separate scopes.

Global scopes are not affected. whereNot negates the constraint you pass; it does not disable SoftDeletes or other global scopes. Combine with withTrashed() or withoutGlobalScope() when your intended complement includes excluded rows.

Debug by inspecting the SQL. Call ->toSql() and ->getBindings() on the builder before executing. If the result looks wrong, the usual cause is missing grouping or an unexpected nested orWhere from the scope you inverted.

Extension point: if you need additional behavior (for example, a custom orWhereNot or a builder mixin), register your own macro in a service provider rather than editing vendor code — the archived package is unlikely to receive fixes, and a local macro keeps the upgrade path open.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings