Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Query Filter Bundle Laravel Package

artprima/query-filter-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel (Symfony-inspired)

Since this is a Symfony bundle, we'll adapt it for Laravel using Laravel Doctrine (e.g., laravel-doctrine/orm) or a Symfony bridge like symfony/console-bridge. For simplicity, assume you're using Doctrine ORM in Laravel.

  1. Install Dependencies

    composer require artprima/query-filter-bundle
    composer require doctrine/orm doctrine/persistence symfony/dependency-injection symfony/config
    
  2. Register the Bundle (if using Symfony Bridge) In config/app.php, add the bundle to the config.bundles array (if using a Symfony bridge):

    Artprima\QueryFilterBundle\ArtprimaQueryFilterBundle::class => ['all' => true],
    
  3. Basic Controller Usage Create a controller to handle filtered queries:

    use Artprima\QueryFilterBundle\QueryFilter\Config\BaseConfig;
    use Artprima\QueryFilterBundle\Request\Request;
    use Artprima\QueryFilterBundle\QueryFilter\QueryFilter;
    use Artprima\QueryFilterBundle\Response\Response;
    
    class ItemController extends Controller
    {
        public function index(Request $request, ItemRepository $repository)
        {
            $config = new BaseConfig();
            $config->setSearchAllowedCols(['t.name']);
            $config->setAllowedLimits([10, 25, 50]);
            $config->setDefaultLimit(10);
            $config->setSortCols(['t.id'], ['t.id' => 'asc']);
            $config->setRequest(new Request($request));
            $config->setStrictColumns(true);
            $config->setRepositoryCallback([$repository, 'findByOrderBy']);
    
            $queryFilter = new QueryFilter(Response::class);
            $response = $queryFilter->getData($config);
            $data = $response->getData();
            $meta = $response->getMeta();
    
            return response()->json(['data' => $data, 'meta' => $meta]);
        }
    }
    
  4. Repository Setup Extend your repository to work with QueryFilterArgs:

    use Artprima\QueryFilterBundle\Query\ConditionManager;
    use Artprima\QueryFilterBundle\QueryFilter\QueryFilterArgs;
    use Artprima\QueryFilterBundle\QueryFilter\QueryResult;
    use Doctrine\ORM\Tools\Pagination\Paginator;
    
    class ItemRepository
    {
        private $pqbManager;
    
        public function __construct(ConditionManager $manager)
        {
            $this->pqbManager = $manager;
        }
    
        public function findByOrderBy(QueryFilterArgs $args): QueryResult
        {
            $qb = $this->createQueryBuilder('t')
                ->setFirstResult($args->getOffset())
                ->setMaxResults($args->getLimit());
    
            $proxyQb = $this->pqbManager->wrapQueryBuilder($qb);
            $qb = $proxyQb->getSortedAndFilteredQueryBuilder($args->getSearchBy(), $args->getSortBy());
            $query = $qb->getQuery();
            $paginator = new Paginator($query);
    
            return new QueryResult($paginator->getIterator()->getArrayCopy(), count($paginator));
        }
    }
    
  5. First API Call Test with a filtered request:

    GET /items?filter[t.name]=Doe&limit=25
    

    This will return results where t.name LIKE '%Doe%'.


Implementation Patterns

1. Configuration-Driven Filtering

  • Centralize Config: Define filter rules in a dedicated config class (e.g., ItemConfig) to avoid repeating logic in controllers.
    class ItemConfig extends BaseConfig
    {
        public function __construct()
        {
            $this->setSearchAllowedCols(['t.name', 't.description']);
            $this->setSortCols(['t.id', 't.created_at'], ['t.id' => 'desc']);
            $this->setAllowedLimits([10, 25, 50]);
        }
    }
    
  • Reuse Configs: Inject the same config across multiple controllers or actions.

2. Repository Abstraction

  • Standardize Query Methods: Ensure all repositories implement a method like findByOrderBy(QueryFilterArgs) to work with the bundle.
    public function findByOrderBy(QueryFilterArgs $args): QueryResult
    {
        // Build query with $args->getSearchBy(), $args->getSortBy(), etc.
    }
    
  • Dependency Injection: Inject ConditionManager into repositories to handle complex filtering logic.

3. Request Handling

  • Leverage Middleware: Create middleware to parse and validate filter requests before they reach controllers.
    public function handle(Request $request, Closure $next)
    {
        $request->merge([
            'filter' => $request->query->get('filter', []),
            'sortby' => $request->query->get('sortby'),
            'sortdir' => $request->query->get('sortdir', 'asc'),
        ]);
        return $next($request);
    }
    
  • Query Parameter Parsing: Use Artprima\QueryFilterBundle\Request\Request to normalize incoming filter parameters.

4. Pagination and Sorting

  • Default Values: Set defaults in config to avoid null checks:
    $config->setDefaultLimit(10);
    $config->setSortCols(['t.id'], ['t.id' => 'asc']);
    
  • Dynamic Limits: Allow clients to specify limits (e.g., ?limit=50) while enforcing allowed values:
    $config->setAllowedLimits([10, 25, 50, 100]);
    

5. Advanced Filtering

  • Custom Operators: Extend ConditionManager to support custom DQL operators (e.g., SIMILAR TO).
    // In a custom ConditionManager service
    public function addCustomCondition(string $field, string $operator, $value)
    {
        if ($operator === 'custom_op') {
            $qb->andWhere("$field SIMILAR TO :val")->setParameter('val', $value);
        }
    }
    
  • Logical Operators: Use connector in advanced mode to chain filters with AND/OR:
    GET /items?simple=0&filter[0][field]=t.name&filter[0][type]=like&filter[0][x]=Doe&filter[1][connector]=or&filter[1][field]=t.status&filter[1][type]=eq&filter[1][x]=active
    

6. Integration with Laravel Services

  • API Resources: Transform QueryResult into Laravel API Resources for consistent JSON responses.
    return ItemResource::collection($data)->additional(['meta' => $meta]);
    
  • Form Requests: Validate filter inputs using Laravel's FormRequest:
    public function rules()
    {
        return [
            'filter.*.field' => 'required|in:t.name,t.description',
            'filter.*.type' => 'required|in:eq,like,gt',
        ];
    }
    

Gotchas and Tips

1. Doctrine QueryBuilder Quirks

  • Alias Confusion: Ensure all fields in setSearchAllowedCols() and setSortCols() use the same alias (e.g., t.name, not name).
  • Proxy QueryBuilder: The ConditionManager wraps the original QueryBuilder. Always use $proxyQb->getSortedAndFilteredQueryBuilder() to apply filters/sorting.
    // WRONG: Skips filtering
    $qb = $this->createQueryBuilder('t')->getQuery();
    
    // RIGHT: Uses proxy
    $proxyQb = $this->pqbManager->wrapQueryBuilder($qb);
    $qb = $proxyQb->getSortedAndFilteredQueryBuilder($searchBy, $sortBy);
    

2. Performance Pitfalls

  • N+1 Queries: The bundle doesn’t fetch relationships by default. Use fetch="EAGER" or join in QueryBuilder:
    $qb->leftJoin('t.user', 'u')->addSelect('u');
    
  • Large Limits: Avoid unbounded limits (e.g., ?limit=10000). Enforce a max limit in config:
    $config->setAllowedLimits([10, 25, 50, 100]);
    

3. Configuration Strictness

  • Strict Columns: Enable setStrictColumns(true) to block invalid fields/sorts, but test thoroughly—it throws UnexpectedValueException on errors.
  • Default Fallbacks: If a client omits limit or page, the bundle uses defaults. Override in config:
    $config->set
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware