ibrand/laravel-active
Laravel package for managing active/inactive status on Eloquent models. Adds an activation flag with query scopes, toggle helpers, and filters so you can quickly enable, disable, or query only active records for products, users, categories, and similar entities.
ibrand/laravel-active is a small Eloquent helper for adding an "active/inactive" state to models — scopes to filter active records and helper methods to toggle state without deleting rows.
Install and wire it up in three steps:
composer require ibrand/laravel-active
1. Add the boolean column. The package assumes an active (or is_active, depending on config) column on the table. Generate a migration:
Schema::table('products', function (Blueprint $table) {
$table->boolean('active')->default(true)->index();
});
2. Use the trait on your model:
use Ibrand\Active\Traits\Active;
class Product extends Model
{
use Active;
}
3. Start using the scopes. The first and most common use case is filtering listing pages so disabled records never leak into public queries:
Product::active()->get(); // only active rows
Product::inactive()->get(); // only disabled rows
Where to look first: open src/Traits/Active.php in vendor/ibrand/laravel-active before relying on it. Because the package is tiny (3 stars, 0 dependents), the source is the real documentation — confirm the exact column name, trait namespace, and method names (activate() / deactivate() / toggleActive()) rather than trusting assumptions from other packages.
Soft "publish/unpublish" for catalogs and CMS entries. Use active instead of deleted_at when a record should be hidden but still editable:
$product->activate();
$product->deactivate();
This pairs naturally with a deleted_at soft delete: deactivation hides from storefronts, deletion removes from admin history.
Compose with other scopes. Keep the active filter as the first constraint on every public-facing query builder method:
public function scopePublished(Builder $q)
{
return $q->active()->where('published_at', '<=', now());
}
Route model binding with an implicit constraint. Prevent direct URL access to inactive records:
Route::get('/products/{product}', fn (Product $product) => view('product', compact('product')))
->scopeBindings(); // or bind manually:
// Route::bind('product', fn ($id) => Product::active()->findOrFail($id));
Admin toggles. Wire a single endpoint for the common "switch on/off" toggle:
Route::patch('/products/{product}/toggle', function (Product $product) {
$product->toggleActive();
return back();
});
Mass toggling. For bulk admin actions, prefer a query update over per-model loops — the trait's methods trigger events/saves per instance, which is slow at scale:
Product::whereIn('id', $ids)->update(['active' => false]);
Cached counts. If you expose "X active listings" on a dashboard, cache the result and bust it on activate/deactivate via an observer — don't call Product::active()->count() on every request.
It is unmaintained. Zero dependents and 3 stars mean no community fixes and no Laravel 11+ guarantee. Before adopting, run it against your target Laravel version in a scratch app; if it breaks, the trait is ~20 lines you can copy into app/Models/Concerns/Active.php and own outright. For most teams that is the honest recommendation.
Column name mismatch is the #1 failure. The trait and its scopes hard-code the column it reads. If your schema uses is_active or status, either rename in a migration or override the constant/getActiveColumn() method the trait exposes. Check the source — don't guess.
Mass assignment. Add active to $fillable (or $guarded) if you want form-driven toggles to persist; otherwise update(['active' => ...]) silently no-ops.
Type casting. Cast the column to bool (protected $casts = ['active' => 'boolean']) so JSON responses and Blade conditionals behave predictably — MySQL returns 0/1, which will render as "1" in APIs otherwise.
Index the column. Every scope call filters on active; without an index, Product::active()->where('category_id', $x) will table-scan once the catalog grows. Add a composite index covering your most common filter pair.
Default values are your responsibility. The package does not backfill existing rows. When adding the column to a live table, set ->default(true) or run an update, or every existing record becomes invisible to active() queries.
Debugging empty listings. When a page suddenly shows nothing, suspect the active() scope first: ->toSql() on the query to confirm the where active = 1 clause, and log Product::withoutGlobalScope(...) output if you use the trait alongside global scopes.
Extension point: if you need richer states (draft/published/archived), don't fight the boolean — keep active for the boolean toggle and add a separate status enum. The trait is thin enough that wrapping it in a Publishable concern that also manages published_at is cleaner than patching the vendor code.
How can I help you explore Laravel packages today?