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

Filament Banhammer Laravel Package

gerenuk/filament-banhammer

Filament Banhammer brings ban management to Filament admin panels, letting you ban and unban users, schedule bans, and block banned accounts from accessing your app. Manage moderation directly from Filament with dedicated resources and controls.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Check your Filament version first. v2.0.0 bumps the Filament requirement from ^3.2 to ^5.7. This is a hard breaking change — you cannot install v2 on a Filament v3 or v4 panel. If you're still on v3/v4, stay on v1.2.0 and pin it; if you're on v5, use ^2.0. Either way, pin an explicit version in composer.json — this is still a very low-adoption package (~12 stars, 0 dependents).
  2. Install: composer require gerenuk/filament-banhammer:^2.0 (see version note above).
  3. Publish and run the migration: php artisan vendor:publish --tag="filament-banhammer-migrations" then php artisan migrate. Read the migration before running it — the v2 feature set (IP/country blocking) likely means additional tables or columns beyond the original banned_at / banned_until / ban_reason fields. Note which table holds IP and country entries.
  4. Add the trait to the model you want to ban — usually App\Models\User. Look in src/ for the trait name and confirm the helpers it exposes (isBanned(), banned() scope, and anything new for IP/country checks).
  5. Wire it into Filament: register the package's table action in table()->actions([...]) and the bulk action in table()->bulkActions([...]) on the new v5 API. Add the provided column and filter if shipped.
  6. First use case: open the Users table, click Ban on a row, supply a reason and optional expiry, and confirm the record reflects the banned state. Then test the three new enforcement dimensions independently — model ban, IP ban, and country ban — because they take different code paths and one working does not mean the others are wired up.

Look first at the migration (what columns/tables exist), the trait (what methods exist), the Filament action classes (form fields rendered), and the new authorization/IP/country classes. Those define the usable API.

Implementation Patterns

Row + bulk actions on the users table. Add the ban action as both a row action and a bulk action so admins can ban one or many users from the list view. Pair with the unban action so the same table supports reversal without editing the record.

Header action on the edit page. Add to getHeaderActions() so an admin viewing a specific user can ban/unban in context, with the reason field visible next to the profile data.

Status column and filter. Add the package's column for at-a-glance state, and a ternary filter (banned / not banned). If the package doesn't ship one, a TextColumn::make('banned_at')->since() plus a Filter on whereNotNull('banned_at') is trivial.

Model-level enforcement is still your job. Filament actions only change data. Unless v2's authorization layer covers your surfaces, enforce in:

  • canAccessPanel() on the user model, returning false when banned.
  • Middleware on web routes for non-panel surfaces.
  • A Login event listener (or login controller check) that rejects banned users.

Also consider the new authorization hook the release notes mention — check src/ for how it's meant to plug into your app, since that's likely the intended enforcement point.

IP blocking. v2 adds IP blocking, which is a different axis from user bans: it doesn't care who the user is. Decide where it's enforced — typically a global middleware ($middleware->prepend(...) in bootstrap/app.php on Laravel 11+) so it runs before auth. Confirm whether the package ships the middleware or just stores the IP records; the release notes are ambiguous. Be careful with trustedProxies — if you read the wrong IP behind a load balancer, you'll block your own proxy.

Country blocking. Same pattern as IP: storage plus an enforcement point. You need a geo-IP source to resolve country (the package may or may not bundle one — check dependencies). Decide whether blocking is "deny listed countries" or "allow only listed countries"; getting that inverted locks out everyone. Consider the privacy/GDPR implications of logging geolocation against users.

Time-limited bans. If banned_until is supported, either check expiry at access time (banned_at && (banned_until === null || banned_until->isFuture())) or schedule a command to clear expired bans. Check-at-access avoids cron drift; scheduled clearing is better if downstream systems read the column directly.

Reuse on other models. The trait is model-agnostic — apply it to Comment, Review, or Organization if you want the same ban UI for non-user records. Publish a second migration or copy the columns.

Notifications and audit. Listen for the model events the trait fires (or override the action's after() callback) to email the banned user, notify moderators, or write to an audit log. This is where the reason field feeds your internal admin notes.

Testing. Use the trait in factories (User::factory()->banned() if a state is provided) and assert canAccessPanel() returns false. Filament action tests via livewire()->callTableAction() confirm the wiring. Add dedicated tests for IP and country blocks — they're the least familiar paths and the easiest to get wrong.

Gotchas and Tips

  • v2.0.0 is a hard break, not a soft one. The Filament constraint moved from ^3.2 to ^5.7. Upgrading a v3 or v4 panel is not possible without first upgrading Filament. Plan the Filament upgrade as the real project; the banhammer bump is a footnote on top of it.
  • v1 actions were reportedly broken. The release notes say "fix broken actions." If you're on v1.2.0 and the ban action silently no-ops, that's likely why — confirm whether your symptoms match and whether upgrading (or copying the fixed action class) is the remedy.
  • Three blocking mechanisms, three failure modes. A ban can fail at the model layer, the IP layer, or the country layer independently. When a banned user still gets in, check all three rather than assuming the action didn't fire.
  • The package is barely adopted. With ~12 stars and 0 dependents, treat the README as a starting point. Read src/ and the migration before assuming method names, column names, or behavior. Pin the version and read the v1.2.0→v2.0.0 diff for anything the release notes omitted.
  • Banning does not revoke sessions or tokens. A user already logged in stays logged in until session expiry. Explicitly log them out (Auth::logoutOtherDevices, session invalidation) or enforce per-request middleware. Sanctum/API tokens are not revoked by a banned_at column — add tokens()->delete() to your ban flow if you expose an API.
  • Global scopes hide banned users from admins. If the trait adds a global scope excluding banned records, your Filament resource stops showing them — defeating the ban management UI. Call withoutGlobalScope(...) in getEloquentQuery() or don't enable the scope.
  • IP resolution behind proxies. Make sure TrustProxies is configured before deploying IP blocking; resolving to your load balancer's IP bans everyone or no one depending on the direction of the check.
  • Country data source and licensing. If the package bundles a geo-IP database, check its refresh policy and license — country mappings drift and stale data blocks the wrong people. If it doesn't bundle one, budget for a service (MaxMind, ipinfo, etc.).
  • Column name collisions. banned_at / banned_until may already exist from another moderation package. Rename via the published migration before running it and update the trait's expectations.
  • Timezone handling on banned_until. Cast to datetime in $casts and store UTC. Comparing local-time against now() is a classic "ban expired early/late" bug.
  • Queued jobs and scheduled tasks still run. A ban doesn't cancel dispatched jobs. Guard sensitive job handle() methods with an isBanned() check.
  • Migration ordering. If you publish after other packages touching users, check timestamp ordering; column-order assumptions in seeders can bite.
  • Debug tip. If the action appears but does nothing, check authorize() on the action and the resource policy — Filament silently hides or no-ops actions the current user isn't authorized for. With the new authorization feature, also confirm you've granted the ban ability to the roles that need it, or admins will lose access to controls they had before.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource