PHP code example of jerome / filterable

1. Go to this page and download the library: Download jerome/filterable library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

jerome / filterable example snippets


'providers' => [
    // Other service providers...
    Filterable\Providers\FilterableServiceProvider::class,
],

namespace App\Filters;

use Filterable\Filter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Contracts\Cache\Repository as Cache;
use Psr\Log\LoggerInterface;

class PostFilter extends Filter
{
    protected array $filters = ['status', 'category'];

    /**
     * Enable specific features for this filter.
     */
    public function __construct(Request $request, ?Cache $cache = null, ?LoggerInterface $logger = null)
    {
        parent::__construct($request, $cache, $logger);

        // Enable the features you need
        $this->enableFeatures([
            'validation',
            'caching',
            'logging',
            'performance',
        ]);
    }

    protected function status(string $value): Builder
    {
        return $this->builder->where('status', $value);
    }

    protected function category(int $value): Builder
    {
        return $this->builder->where('category_id', $value);
    }
}

protected array $filters = ['last_published_at'];

protected function lastPublishedAt(string $value): Builder
{
    return $this->builder->where('last_published_at', $value);
}

namespace App\Models;

use Filterable\Interfaces\Filterable as FilterableInterface;
use Filterable\Traits\Filterable as FilterableTrait;
use Illuminate\Database\Eloquent\Model;

class Post extends Model implements FilterableInterface
{
    use FilterableTrait;
}

use App\Models\Post;
use App\Filters\PostFilter;

$filter = new PostFilter(request(), cache(), logger());
$posts = Post::filter($filter)->get();

use App\Models\Post;
use App\Filters\PostFilter;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index(Request $request, PostFilter $filter)
    {
        $query = Post::filter($filter);

        $posts = $request->has('paginate')
            ? $query->paginate($request->query('per_page', 20))
            : $query->get();

        return response()->json($posts);
    }
}

return [
    // Other service providers...
    Filterable\Providers\FilterableServiceProvider::class,
];



namespace App\Http\Controllers;

use App\Models\Post;
use App\Filters\PostFilter;
use Illuminate\Http\Request;

class PostIndexController extends Controller
{
    public function __invoke(Request $request, PostFilter $filter)
    {
        $query = Post::filter($filter);

        $posts = $request->has('paginate')
            ? $query->paginate($request->query('per_page', 20))
            : $query->get();

        return response()->json($posts);
    }
}

use App\Http\Controllers\PostIndexController;

Route::get('/posts', PostIndexController::class);

// Enable individual features
$filter->enableFeature('validation');
$filter->enableFeature('caching');

// Enable multiple features at once
$filter->enableFeatures([
    'validation',
    'caching',
    'logging',
    'performance',
]);

// Disable a feature
$filter->disableFeature('caching');

// Check if a feature is enabled
if ($filter->hasFeature('caching')) {
    // Do something
}

// Enable all features
$filter->enableFeatures([
    'validation',
    'permissions',
    'rateLimit',
    'caching',
    'logging',
    'performance',
    'optimization',
    'memoryManagement',
    'filterChaining',
    'valueTransformation',
]);

$filter->forUser($request->user());

$filter->registerPreFilters(function (Builder $query) {
    return $query->where('published', true);
});

$filter->setValidationRules([
    'status' => ':categories,id',
]);

// Add custom validation messages
$filter->setValidationMessages([
    'status.in' => 'Status must be either active or inactive',
]);

$filter->setFilterPermissions([
    'admin_only_filter' => 'admin',
    'editor_filter' => ['editor', 'admin'],
]);

// Implement the permission check in your filter class
protected function userHasPermission(string|array $permission): bool
{
    if (is_array($permission)) {
        return collect($permission)->contains(fn ($role) => $this->forUser->hasRole($role));
    }

    return $this->forUser->hasRole($permission);
}

// Set the maximum number of filters that can be applied at once
$filter->setMaxFilters(10);

// Set the maximum complexity score for all filters combined
$filter->setMaxComplexity(100);

// Define complexity scores for specific filters
$filter->setFilterComplexity([
    'complex_filter' => 10,
    'simple_filter' => 1,
]);

// Process a query with lazy loading
$posts = $filter->lazy()->each(function ($post) {
    // Process each post with minimal memory usage
});

// Use chunking for large datasets
$filter->chunk(1000, function ($posts) {
    // Process posts in chunks of 1000
});

// Map over query results without loading all records
$result = $filter->map(function ($post) {
    return $post->title;
});

// Filter results without loading all records
$result = $filter->filter(function ($post) {
    return $post->status === 'active';
});

// Reduce results without loading all records
$total = $filter->reduce(function ($carry, $post) {
    return $carry + $post->views;
}, 0);

// Get a lazy collection with custom chunk size
$lazyCollection = $filter->lazy(500);

// Process each item with minimal memory usage
$filter->lazyEach(function ($item) {
    // Process item
}, 500);

// Create a generator to iterate with minimal memory
foreach ($filter->cursor() as $item) {
    // Process item
}

// Select only needed columns
$filter->select(['id', 'title', 'status']);

// Eager load relationships
$filter->with(['author', 'comments']);

// Set chunk size for large datasets
$filter->chunkSize(1000);

// Use a database index hint
$filter->useIndex('idx_posts_status');

// Set cache expiration time (in minutes)
$filter->setCacheExpiration(60);

// Manually clear the cache
$filter->clearCache();

// Use tagged cache for better invalidation
$filter->cacheTags(['posts', 'api']);

// Enable specific caching modes
$filter->cacheResults(true);
$filter->cacheCount(true);

// Get the number of items with caching
$count = $filter->count();

// Clear related caches when models change
$filter->clearRelatedCaches(Post::class);

// Get SQL query without executing it
$sql = $filter->toSql();

// Set a custom logger
$filter->setLogger($customLogger);

// Get the current logger
$logger = $filter->getLogger();

// Log at different levels
$filter->logInfo("Applying filter", ['filter' => 'status']);
$filter->logDebug("Filter details", ['value' => $value]);
$filter->logWarning("Potential issue", ['problem' => 'description']);

// Logging is automatically handled if enabled
// You can also add custom logging in your filter methods:
protected function customFilter($value): Builder
{
    $this->logInfo("Applying custom filter with value: {$value}");

    return $this->builder->where('custom_field', $value);
}

// Get performance metrics after applying filters
$metrics = $filter->getMetrics();

// Add custom metrics
$filter->addMetric('custom_metric', $value);

// Get execution time
$executionTime = $filter->getExecutionTime();

$filter->where('status', 'active')
       ->whereIn('category_id', [1, 2, 3])
       ->whereNotIn('tag_id', [4, 5])
       ->whereBetween('created_at', [$startDate, $endDate])
       ->orderBy('created_at', 'desc');

// Register a transformer for a filter
$filter->registerTransformer('date', function ($value) {
    return Carbon::parse($value)->toDateTimeString();
});

// Register a transformer for an array of values
$arrayTransformer = function($values) {
    return array_map(fn($value) => strtolower($value), $values);
};
$filter->registerTransformer('tags', $arrayTransformer);

// Only apply a filter if a condition is met
$filter->when($request->has('status'), function ($filter) use ($request) {
    $filter->where('status', $request->status);
});

// Apply one filter or another based on a condition
$filter->when($request->has('sort'),
    function ($filter) use ($request) {
        $filter->orderBy($request->sort);
    },
    function ($filter) {
        $filter->orderBy('created_at', 'desc');
    }
);

// Check the current state
if ($filter->getDebugInfo()['state'] === 'applied') {
    // Process results
}

// Reset the filter to its initial state
$filter->reset();

$debugInfo = $filter->getDebugInfo();

// Debug info  features
// - Query options
// - SQL query and bindings
// - Performance metrics (if enabled)

class MyFilter extends Filter
{
    protected function handleFilteringException(Throwable $exception): void
    {
        // Log the exception
        $this->logWarning('Filter exception', [
            'message' => $exception->getMessage(),
            'trace' => $exception->getTraceAsString(),
        ]);

        // Optionally rethrow specific exceptions
        if ($exception instanceof MyCustomException) {
            throw $exception;
        }

        // Otherwise, let the parent handle it
        parent::handleFilteringException($exception);
    }
}

use App\Models\Post;
use App\Filters\PostFilter;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index(Request $request, PostFilter $filter)
    {
        // Enable features
        $filter->enableFeatures([
            'validation',
            'caching',
            'logging',
            'performance',
        ]);

        // Set validation rules
        $filter->setValidationRules([
            'status' => 'sometimes|in:active,inactive',
            'category_id' => 'sometimes|integer|exists:categories,id',
        ]);

        // Apply user scope
        $filter->forUser($request->user());

        // Apply pre-filters
        $filter->registerPreFilters(function ($query) {
            return $query->where('published', true);
        });

        // Set caching options
        $filter->setCacheExpiration(30);
        $filter->cacheTags(['posts', 'api']);

        // Apply custom filter chain
        $filter->where('is_featured', true)
               ->orderBy('created_at', 'desc');

        // Apply filters to the query
        $query = Post::filter($filter);

        // Get paginated results
        $posts = $request->has('paginate')
            ? $query->paginate($request->query('per_page', 20))
            : $query->get();

        // Get performance metrics if needed
        $metrics = null;
        if ($filter->hasFeature('performance')) {
            $metrics = $filter->getMetrics();
        }

        return response()->json([
            'data' => $posts,
            'metrics' => $metrics,
        ]);
    }
}

namespace Tests\Unit;

use Tests\TestCase;
use App\Models\Post;
use App\Filters\PostFilter;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;

class PostFilterTest extends TestCase
{
    use RefreshDatabase;

    public function testFiltersPostsByStatus(): void
    {
        $activePost = Post::factory()->create(['status' => 'active']);
        $inactivePost = Post::factory()->create(['status' => 'inactive']);

        $filter = new PostFilter(new Request(['status' => 'active']));
        $filteredPosts = Post::filter($filter)->get();

        $this->assertTrue($filteredPosts->contains($activePost));
        $this->assertFalse($filteredPosts->contains($inactivePost));
    }

    public function testRateLimitingRejectsComplexQueries(): void
    {
        // Create a filter with too many parameters
        $filter = new PostFilter(new Request([
            'param1' => 'value1',
            'param2' => 'value2',
            // ... many more parameters
        ]));

        $filter->enableFeature('rateLimit');
        $filter->setMaxFilters(5);

        // Apply the filter and check if rate limiting was triggered
        $result = Post::filter($filter)->get();

        // Assert that no results were returned due to rate limiting
        $this->assertEmpty($result);
    }
}