Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/api/facade.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ use Filterable;
// Create a new filterable instance
$filterable = Filterable::create();

// Create an initialized filterable query
$filterable = Filterable::for(User::class);

// Apply filters to a query builder
$results = Filterable::create()
->setModel(User::class)
Expand Down Expand Up @@ -136,6 +139,9 @@ Filterable::create()->withoutSanitizers();
// Use specific engine
Filterable::create()->useEngine('expression'); // or 'tree', 'ruleset', etc.

// Fluent alias when building a query
Filterable::for(User::class)->using('ruleset');

// Enable header-driven mode
Filterable::create()->withHeaderDrivenMode([
'header_name' => 'X-Filter-Engine',
Expand Down Expand Up @@ -164,6 +170,7 @@ The facade provides access to all public methods of the Filterable class, organi
### Static Factory Methods

- `create()` - Create new Filterable instance
- `for()` - Create an initialized instance for a model or builder
- `withRequest()` - Create new Filterable instance with custom Request

### Core Filtering Methods
Expand Down Expand Up @@ -197,7 +204,7 @@ The facade provides access to all public methods of the Filterable class, organi
- `setData()` - Set manual data injection
- `getData()` - Get current data
- `setSource()` - Set request source
- `get()` - Retrieve input item from request
- `getFromRequest()` - Retrieve an input item from the configured request source

And many more methods for advanced configuration and customization.

Expand Down
24 changes: 21 additions & 3 deletions docs/api/filterable.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ $users = $invoker->get();

Alias of `apply()`.

#### Automatic Builder execution

Instances created with `for()` apply their filters automatically before forwarding any dynamic Builder method. The returned `Invoker` keeps subsequent Builder calls fluent, so Laravel methods and macros work without maintaining a package-side method list.

```php
$posts = Filterable::for(Post::class, $request)
->using('ruleset')
->setAllowedFields(['status'])
->where('published', true)
->paginate(15);
```

This applies consistently to query construction, retrieval, aggregates, pagination, streaming, mutations, and custom Builder macros.

#### `shouldReturnQueryBuilder(): static`

Force `apply()` to return the Eloquent Builder instead of the Invoker wrapper.
Expand Down Expand Up @@ -191,9 +205,9 @@ Return current working data. If a `filterKey` is set (via traits), returns that

Set the request source: `query`, `input`, or `json`. Throws when unsupported.

#### `get(string $key): mixed`
#### `getFromRequest(string $key): mixed`

Retrieve an input value from the configured source.
Retrieve an input value from the configured request source. The `get()` method is reserved for Eloquent Builder execution and therefore triggers automatic filter application.

---

Expand All @@ -203,6 +217,10 @@ Retrieve an input value from the configured source.

Override the engine for this instance. Accepts an engine instance or a supported engine key.

#### `using(Engine|string $engine): static`

Fluent alias for `useEngine()`, useful when building a query with `for()`.

#### `getEngine(): Engine`

Return the current engine instance.
Expand Down Expand Up @@ -414,7 +432,7 @@ Set and return class aliases as a collection.

#### `__get($property): mixed`

Proxy missing properties to the request source via `get($property)` when not present on the instance.
Proxy missing properties to the request source via `getFromRequest($property)` when not present on the instance.

---

Expand Down
12 changes: 8 additions & 4 deletions src/Engines/Invokable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace Kettasoft\Filterable\Engines;

use Illuminate\Contracts\Database\Eloquent\Builder;
use ReflectionMethod;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\ForwardsCalls;
use Kettasoft\Filterable\Engines\Foundation\Attributes\AttributeContext;
use Kettasoft\Filterable\Engines\Foundation\Attributes\AttributePipeline;
use Kettasoft\Filterable\Engines\Foundation\Engine;
Expand All @@ -16,8 +16,6 @@

class Invokable extends Engine
{
use ForwardsCalls;

/**
* Engine name.
* @var string
Expand Down Expand Up @@ -85,7 +83,13 @@ protected function applyFilterMethod(string $key, string $method, Payload $paylo
$process = $pipeline->process($this->context, $method);

$process->then(function () use ($method, $payload) {
$this->forwardCallTo($this->context, $method, [$payload]);
$result = (new ReflectionMethod($this->context, $method))
->invoke($this->context, $payload);

if ($result instanceof Builder) {
$this->builder = $result;
$this->context->setBuilder($result);
}
})
->catch(function ($e) {
throw $e;
Expand Down
3 changes: 2 additions & 1 deletion src/Facades/Filterable.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
*
* Engine Configuration:
* @method static \Kettasoft\Filterable\Filterable useEngine(\Kettasoft\Filterable\Engines\Foundation\Engine|string $engine) Override the default engine for this filterable instance.
* @method static \Kettasoft\Filterable\Filterable using(\Kettasoft\Filterable\Engines\Foundation\Engine|string $engine) Alias for useEngine.
* @method static \Kettasoft\Filterable\Engines\Foundation\Engine getEngine() Get current engine.
*
* Request & Data Management:
Expand All @@ -53,7 +54,7 @@
* @method static mixed getData() Get current data.
* @method static array getFilterAttributes() Fetch all relevant filters from the filter API class.
* @method static \Kettasoft\Filterable\Filterable setSource(string $source) Set request source.
* @method static mixed get(string $key) Retrieve an input item from the request.
* @method static mixed getFromRequest(string $key) Retrieve an input item from the request.
*
* Sanitization:
* @method static \Kettasoft\Filterable\Filterable setSanitizers(array $sanitizers, bool $override = true) Set a new sanitizers classes.
Expand Down
24 changes: 18 additions & 6 deletions src/Filterable.php
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,17 @@ public function useEngine(Engine|string $engine): static
return $this;
}

/**
* Alias for {@see useEngine()}.
*
* @param Engine|class-string<Engine>|string $engine
* @return static
*/
public function using(Engine|string $engine): static
{
return $this->useEngine($engine);
}

/**
* Get current engine.
* @return Engine
Expand Down Expand Up @@ -1052,11 +1063,12 @@ public function toSql(Builder|null $builder = null, $withBindings = false): stri
}

/**
* Retrieve an input item from the request.
* @param string $key
* @return mixed
* Retrieve an input item from the configured request source.
* @param string $key The key to retrieve from the request
* @return mixed The value from the request source
* @throws RequestSourceIsNotSupportedException
*/
public function get(string $key)
public function getFromRequest(string $key): mixed
{
if (!in_array($source = $this->requestSource ?? config('filterable.request_source', 'query'), ['query', 'input', 'json'])) {
throw new RequestSourceIsNotSupportedException($source);
Expand Down Expand Up @@ -1112,7 +1124,7 @@ public function __get($property): mixed
return $this->{$property};
}

return $this->get($property);
return $this->getFromRequest($property);
}

/**
Expand All @@ -1123,6 +1135,6 @@ public function __get($property): mixed
*/
public function __call($method, $parameters)
{
return $this->handleFluentReturn($method, $parameters);
return $this->forwardCallTo($this->apply(), $method, $parameters);
}
}
7 changes: 7 additions & 0 deletions tests/Feature/Profiler/FilterProfilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@

class FilterProfilerTest extends TestCase
{
public function tearDown(): void
{
Profiler::dispatcher()->flush();

parent::tearDown();
}

public function test_it_triggers_slow_query_event()
{
$triggered = false;
Expand Down
Loading
Loading