hindbiswas / laravel-model-utils
A lightweight Laravel package for Model traits and utilities
Package info
github.com/hind-sagar-biswas/laravel-model-utils
pkg:composer/hindbiswas/laravel-model-utils
Fund package maintenance!
Requires
- php: ^8.3
- illuminate/contracts: ^11.0||^12.0||^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2026-04-20 22:35:39 UTC
README
A lightweight Laravel package that adds practical Eloquent model traits and utilities.
Installation
composer require hindbiswas/laravel-model-utils
Requirements
- PHP 8.3+
- Laravel 11, 12, or 13
Included Features
BelongsToAuth: auto-assignuser_idwhen an authenticated user creates a model.Optionable: convert model records tovalue/labeloption arrays.Filterable: dynamic exact filters plus relationship-aware search with dot notation.HasSlug: generate slugs from model attributes with optional uniqueness and update behavior.Archivable: archive/restore records with query scopes for active and archived models.EnumUtil: convert PHP enums to arrays, CSV, associative maps, and options.
Usage
BelongsToAuth Trait
use HindBiswas\ModelUtils\Traits\BelongsToAuth; use Illuminate\Database\Eloquent\Model; class Post extends Model { use BelongsToAuth; protected $guarded = []; }
Behavior:
- If
user_idis empty and a user is authenticated,user_idis filled fromAuth::id(). - If
user_idis already present, it is not overridden.
Optionable Trait
use HindBiswas\ModelUtils\Traits\Optionable; use Illuminate\Database\Eloquent\Model; class Category extends Model { use Optionable; protected $guarded = []; }
Category::options(); // [ // ['value' => 1, 'label' => 'Books'], // ['value' => 2, 'label' => 'Games'], // ]
You can override the columns used for options:
protected static function optionValue(): string { return 'code'; } protected static function optionLabel(): string { return 'title'; }
Filterable Trait
use HindBiswas\ModelUtils\Traits\Filterable; use Illuminate\Database\Eloquent\Model; class Article extends Model { use Filterable; protected $guarded = []; protected array $filterable = [ 'status', 'author.name', 'author.organization.name', ]; protected array $searchable = [ 'title', 'author.name', 'author.organization.name', ]; }
$articles = Article::query()->filter([ 'status' => 'published', 'author.name' => 'Alice', 'search' => 'Laravel', ])->get();
Notes:
- Exact filters use
whereandwhereHas. - Search uses grouped
orWhereandorWhereHaswith%search%matching. - Dot notation supports nested relations like
owner.user.name.
HasSlug Trait
use HindBiswas\ModelUtils\Traits\HasSlug; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasSlug; protected $guarded = []; protected function slugSource(): array { return ['title']; } protected function slugUniqueScope(): array|bool { return true; } protected function updateSlugOnUpdate(): bool { return true; } }
Available hooks:
slugField(): slug column name (default:slug).slugSource(): source attributes used to build slug.slugUniqueScope(): controls uniqueness.false: no uniqueness checks.true: unique across table.['tenant_id']: unique within a scope.
maxSlugLength(): maximum slug length (default:255).updateSlugOnUpdate(): regenerate slug when source fields change.
getRouteKeyName() returns slugField().
Archivable Trait
use HindBiswas\ModelUtils\Traits\Archivable; use Illuminate\Database\Eloquent\Model; class Document extends Model { use Archivable; protected $guarded = []; }
Your table should have an archived_at nullable datetime column.
Schema::table('documents', function (Blueprint $table) { $table->dateTime('archived_at')->nullable(); });
$document->archive(); $document->restoreArchive(); $document->isArchived(); Document::query()->onlyArchived()->get(); Document::query()->withArchived()->get();
Behavior:
- Active records are returned by default because a global scope excludes archived rows.
onlyArchived()fetches only archived records.withArchived()fetches both active and archived records.archived_atis cast todatetimeautomatically.
EnumUtil Helper
use HindBiswas\ModelUtils\Utils\EnumUtil; enum OrderStatus: string { case Draft = 'draft'; case Published = 'published'; } EnumUtil::toArray(OrderStatus::class); // ['draft', 'published'] EnumUtil::toCSV(OrderStatus::class); // 'draft,published' EnumUtil::toAssocArray(OrderStatus::class); // ['draft' => 'Draft', 'published' => 'Published'] EnumUtil::toOptions(OrderStatus::class); // [ // ['value' => 'draft', 'label' => 'Draft'], // ['value' => 'published', 'label' => 'Published'], // ]
If enum cases implement a label() method, EnumUtil uses it. Otherwise, labels are generated using Str::headline(...).
Changelog
Please see CHANGELOG for details.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.