Releases: kettasoft/filterable
v2.15.0
What's Changed
Invokable Engine – Attribute Annotations
This release introduces a new attribute-based annotation system for the Invokable engine, allowing common transformations, validations, and behaviors to be declared directly on filter methods.
The goal is to reduce boilerplate, improve readability, and provide a structured pipeline for handling payload values.
New Features
Attribute Pipeline
Filter method attributes are now executed through a staged pipeline:
CONTROL → TRANSFORM → VALIDATE → BEHAVIOR
This ensures predictable execution order when combining multiple attributes.
New Attributes
Control
Attributes responsible for execution control.
- Authorize — ensures the filter method is authorized before execution
- Required — ensures the payload value exists
- SkipIf — conditionally skips execution using Payload
is*helpers (supports negation and repeatable usage)
Transform
Attributes that normalize or transform incoming values.
- Trim — trims whitespace (left, right, both) with optional custom characters
- Sanitize — applies sanitization rules (
lowercase,uppercase,ucfirst,strip_tags,slug, etc.) - Cast — casts values to a specific type (
int,float,bool,string, etc.) - Explode — splits strings into arrays using a delimiter
- DefaultValue — provides a fallback value when none is provided
- MapValue — maps incoming values to different values (supports strict mode)
Validate
Validation attributes executed after transformations.
- Between — validates numeric range (
min/max) - Regex — validates values against a regular expression
- In — ensures the value exists in a defined set
Behavior
Attributes that modify query behavior.
- Scope — applies an Eloquent scope to the query builder
Documentation
Documentation for the Invokable engine and annotations has been added:
- Invokable engine overview
- Attribute pipeline explanation
- Individual documentation pages for each attribute
- Annotations index page
Tests
A comprehensive test suite has been added covering:
- Each attribute
- Typical usage scenarios
- Edge cases
- Attribute interaction behavior
Example
#[Trim]
#[Sanitize('lowercase')]
#[MapValue(['active' => 1, 'inactive' => 0], strict: true)]
public function status(Payload $payload)
{
$this->builder->where('status', '=', $payload->value);
}Notes
- No breaking changes.
- Attributes are opt-in and only affect methods where they are used.
v2.14.1
v2.14.1 — Execution Lifecycle Fixes
This patch restores the intended filter execution lifecycle and improves
attribute behavior consistency.
Fixed
Attribute execution control
Control attributes are now able to properly halt filter method execution.
Previously, attributes designed to interrupt execution (such as
Required, authorization checks, or other control attributes)
were executed but did not prevent the filter method from running.
Execution flow now correctly respects control attributes.
Finalization lifecycle
The finally() hook is now executed when returning a query builder
through ShouldReturnQueryBuilder or $shouldReturnQueryBuilder.
Previously, finalization logic only ran when using the default
invoker return flow, leading to inconsistent behavior.
Notes
These changes restore the expected execution guarantees without
introducing breaking changes.
v2.14.0
v2.13.3
Relax version constraints for illuminate/database and illuminate/support to support multiple Laravel versions
Full Changelog: v2.13.2...v2.13.3
v2.13.2
Update composer.json and composer.lock for Laravel 12
Full Changelog: v2.13.1...v2.13.2
v2.13.1
Overview
This release updates all internal type hints from:
Illuminate\Database\Eloquent\Builderto:
Illuminate\Contracts\Database\Eloquent\Builderin order to provide full compatibility with all Eloquent builder implementations, including relationship builders such as HasMany, HasOne, BelongsTo, and others.
This ensures the package can handle any valid Eloquent query builder instance without throwing binding or instantiation conflicts.
Key Changes
1. Switched to Database Builder Contract
All classes now rely on the Illuminate\Contracts\Database\Eloquent\Builder interface instead of the concrete Illuminate\Database\Eloquent\Builder class.
This improves flexibility and aligns the package with Laravel’s contract-based design.
2. Relationship Builders Are Now Fully Supported
Previously, calling:
$hasMany = User::find(1)->posts();
Filterable::create()->setBuilder($hasMany);resulted in a type mismatch error because HasMany does not extend the concrete Eloquent Builder class.
After this release, relationship builders and their variants work seamlessly.
3. Improved Developer Experience
Developers can now pass:
- model query builders
- relation builders
- scoped queries
- morph relations
- and any builder-compatible object
without needing workarounds such as $relation->getQuery().
BC (Backward Compatibility)
This release does not introduce breaking changes.
Existing usage remains fully functional, and the package becomes more flexible and aligned with native Laravel behavior.
Upgrade Instructions
No special actions needed.
Simply update to the latest version:
Closing Notes
This release enhances interoperability and stability when working with complex Eloquent relationships, ensuring the package behaves consistently across all builder contexts.
v2.13.0
🚀 Improved Exception Handling
This release introduces a redesigned and extensible exception handler
for Filterable. The goal is to provide a more predictable, customizable,
and developer-friendly approach without adding any breaking changes.
✨ What's New
- Added a new
exceptionsconfiguration section. - Introduced a pluggable exception handler class:
handlernow accepts any class implementing
Kettasoft\Filterable\Exceptions\Contracts\ExceptionHandlerInterface.
- Added global
strictmode to enforce throwing exceptions instead of skipping. - Unified exception flow across all filtering engines.
- Simplified internal architecture for clean and consistent behavior.
⚙️ New Configuration
'exceptions' => [
'handler' => Kettasoft\Filterable\Exceptions\Handlers\DefaultHandler::class,
'strict' => env('FILTERABLE_EXCEPTION_STRICT', false),
]🛠 Internal Improvements
- Extracted exception logic out of engines into a central layer.
- Cleaned up ~31 commits worth of restructuring and architectural refactoring.
- No public API breaks.
- Improved maintainability and added future flexibility for custom handlers.
🔮 Coming Next
- Optional exception reporters.
- Logging toggles.
- Per-engine overrides.
v2.12.0
What’s New?
This release brings a major expansion to the Payload API, focusing on improving readability, expressiveness, and developer ergonomics. New helpers make value checking, string manipulation, date handling, and extensibility dramatically smoother.
✨ New Features
Value Checking
in()/notIn()for membership checksis()/isAny()for rule-based comparisons with negationisEmptyString()for empty-string detectionisNotNullOrEmpty()for null-or-empty validationregex()for validating values using regular expressions
String & Array Helpers
asSlug()to convert any value into a URL-friendly slugexplode()/split()for fast string-to-array transformation
Date & Time
isDate()for validating date stringsisTimestamp()for detecting valid UNIX timestampsasCarbon()for converting values into Carbon instances
Extensibility
- Added
Macroabletrait to allow dynamic runtime extension of thePayloadclass
🧪 Tests
A comprehensive test suite has been added and expanded to cover all existing and new helpers.
What's Changed
- Add Applied Filters Tracking to Filterable Engine by @kettasoft in #34
- Refactor Engine config access to unified pattern by @kettasoft in #35
- Enhance Payload Utility & Add Comprehensive Value Helpers by @kettasoft in #36
Full Changelog: v2.11.0...v2.12.0
v2.11.0
Added
- Introduced two new lifecycle hooks in the Filterable class:
initially(): Runs before any filters are applied, allowing pre-filter modifications to the query builder.finally(): Runs after all filters have been applied, allowing post-filter adjustments or cleanup.
These hooks provide a clean and flexible way to manipulate the query builder.
Example usage:
class PostFilter extends Filterable
{
protected function initially()
{
$this->builder->where('is_active', true);
}
protected function finally()
{
$this->builder->orderBy('created_at', 'desc');
}
}v2.10.0
Filterable Caching Subsystem (v2.10.0)
This release introduces a full-featured caching subsystem for Filterable, designed to dramatically improve query performance while ensuring safe, consistent cache invalidation.
Key Features:
- Deterministic Cache Keys — consistent keys for identical filter inputs.
- Tagged Caching — bulk invalidation support for Redis/Memcached.
- Scoped Caching — user, tenant, or custom scopes to prevent data leakage.
- Cache Profiles — reusable configurations for environment-specific strategies.
- Automatic Invalidation — observer clears affected caches on Eloquent model changes.
- Invoker Integration — caches terminal-method results consistently.
- Fluent API —
HasFilterableCachetrait withcache,cacheForever,cacheTags,scopeByUser,scopeByTenant,cacheProfile,cacheWhen/unless,flush. - Configuration & Docs — cache section in
filterable.phpand full documentation (getting-started,strategies,auto-invalidation,profiles,scoping,monitoring,API reference,examples).
Benefits:
- Significant performance improvements for repeated filter queries (20x–200x).
- Centralized, maintainable cache management.
- Easy setup and profiling with detailed docs and examples.
Notes:
- Tag-based invalidation requires Redis or Memcached.
- Global cache prefix/version allows force-wide invalidation.
- Ensure models are listed in
filterable.phpfor auto-invalidation.