{"id":20941086,"url":"https://github.com/lmc-eu/api-filter","last_synced_at":"2025-05-13T23:31:06.654Z","repository":{"id":32735403,"uuid":"138990975","full_name":"lmc-eu/api-filter","owner":"lmc-eu","description":"Parser/builder for filters from API query parameters","archived":false,"fork":false,"pushed_at":"2022-02-27T00:13:03.000Z","size":264,"stargazers_count":7,"open_issues_count":0,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-03-26T14:28:48.660Z","etag":null,"topics":["api","api-filter","filter","json-api","php","querystring"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lmc-eu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-28T08:41:22.000Z","updated_at":"2023-12-31T18:10:00.000Z","dependencies_parsed_at":"2022-08-07T18:01:11.150Z","dependency_job_id":null,"html_url":"https://github.com/lmc-eu/api-filter","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmc-eu%2Fapi-filter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmc-eu%2Fapi-filter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmc-eu%2Fapi-filter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmc-eu%2Fapi-filter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lmc-eu","download_url":"https://codeload.github.com/lmc-eu/api-filter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225156999,"owners_count":17429699,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-filter","filter","json-api","php","querystring"],"created_at":"2024-11-18T23:12:58.840Z","updated_at":"2024-11-18T23:12:59.339Z","avatar_url":"https://github.com/lmc-eu.png","language":"PHP","readme":"API Filter\n==========\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/lmc/api-filter.svg)](https://packagist.org/packages/lmc/api-filter)\n[![Build Status](https://travis-ci.com/lmc-eu/api-filter.svg?branch=master)](https://travis-ci.com/lmc-eu/api-filter)\n[![Coverage Status](https://coveralls.io/repos/github/lmc-eu/api-filter/badge.svg?branch=master)](https://coveralls.io/github/lmc-eu/api-filter?branch=master)\n\nParser/builder for filters from API query parameters.\n\nIt is just a parser/builder for filters, it is not a place for business logic so it should be wrapped by your class if you want to be more strict about filters.\nSame if you want different settings per entity/table, it should be done by a specific wrapper around this library.\n\n## Table of Contents\n- [Installation](#installation)\n- [Usage](#usage)\n    - [Initialization](#initialization)\n    - [With Doctrine Query Builder](#with-doctrine-query-builder-applicator)\n    - [With simple SQL](#with-sql-applicator)\n- [Supported filters](#supported-filters)\n    - [Equals](#equals---eq-)\n    - [Not Equals](#not-equals---neq-)\n    - [Greater than](#greater-than---gt-)\n    - [Greater than or Equals](#greater-than-or-equals---gte-)\n    - [Lower than](#lower-than---lt-)\n    - [Lower than or Equals](#lower-than-or-equals---lte-)\n    - [IN](#in)\n- [Tuples in filters](#tuples-in-filters)\n- [Examples](#examples)\n    - [IN + EQ](#in--eq-filter)\n    - [GT + LT _(between)_](#gt--lt-filter-between)\n    - [EQ `Tuple`](#eq-with-tuple)\n- [Functions in filters](#functions-in-filters)\n    - [Example for fullName function](#example-for-fullname-function)\n    - [Function parameters definition](#function-parameters-definition)\n        - [By string](#defined-as-string)\n        - [By array](#defined-as-array)\n        - [By object](#defined-as-object)\n        - [Combinations](#combinations)\n    - [Register and Execute function](#register-and-execute-function)\n- [Exceptions and error handling](#exceptions-and-error-handling)\n- [Development](#development)\n\n## Installation\n```bash\ncomposer require lmc/api-filter\n```\n\n\n## Usage\nFor example lets have query parameters from following request\n```http request\nGET http://host/endpoint/?field=value\n```\n\n### Initialization\n```php\n// in DI container/factory\n$apiFilter = new ApiFilter();\n$apiFilter-\u003eregisterApplicator(...);  // optional, when you want to use non-standard implementation\n\n// in service/controller/...\n$filters = $apiFilter-\u003eparseFilters($request-\u003equery-\u003eall());\n\n// [\n//     0 =\u003e Lmc\\ApiFilter\\Filter\\FilterWithOperator {\n//         private $title    =\u003e 'eq'\n//         private $operator =\u003e '='\n//         private $column   =\u003e 'field'\n//         private $value    =\u003e Lmc\\ApiFilter\\Entity\\Value {\n//             private $value =\u003e 'value'\n//         }\n//     }\n// ]\n```\n\n### With Doctrine `Query Builder Applicator`\n- requires `doctrine/orm` installed\n- applying filters uses **cloned** `QueryBuilder` -\u003e original `QueryBuilder` is **untouched**\n\n#### Example\n```php\n// in EntityRepository/Model\n$queryBuilder = $this-\u003ecreateQueryBuilder('alias');\n$queryBuilder = $apiFilter-\u003eapplyFilters($filters, $queryBuilder);\n\n// or one by one\nforeach ($filters as $filter) {\n    $queryBuilder = $apiFilter-\u003eapplyFilter($filter, $queryBuilder);\n}\n\n// get prepared values for applied filters\n$preparedValues = $apiFilter-\u003egetPreparedValues($filters, $queryBuilder); // ['field_eq' =\u003e 'value']\n\n// get query\n$queryBuilder\n    -\u003esetParameters($preparedValues)\n    -\u003egetQuery();\n```\n\n#### Shorter example (_same as ☝_)\n```php\n// in EntityRepository/Model\n$queryBuilder = $this-\u003ecreateQueryBuilder('alias');\n\n$apiFilter\n    -\u003eapplyFilters($filters, $queryBuilder)                                     // query builder with applied filters\n    -\u003esetParameters($apiFilter-\u003egetPreparedValues($filters, $queryBuilder)) // ['field_eq' =\u003e 'value']\n    -\u003egetQuery();\n```\n\n### With `SQL Applicator`\n- ❗it is just a **naive implementation** and should be used carefully❗\n- it still might be used on simple `SQL`s without `ORDER BY`, `GROUP BY` etc. because it simply adds filters as a `WHERE` conditions\n\n`SQL Applicator` must be registered explicitly\n```php\n// in DI container\n$apiFilter-\u003eregisterApplicator(new SqlApplicator(), Priority::MEDIUM);\n```\n\n#### Example\n```php\n// in Model/EntityRepository\n$sql = 'SELECT * FROM table';\n$sql = $apiFilter-\u003eapplyFilters($filters, $sql); // \"SELECT * FROM table WHERE 1 AND field = :field_eq\"\n\n// or one by one\nforeach ($filters as $filter) {\n    $sql = $apiFilter-\u003eapplyFilter($filter, $sql);\n}\n\n// get prepared values for applied filters\n$preparedValues = $apiFilter-\u003egetPreparedValues($filters, $sql); // ['field_eq' =\u003e 'value']\n\n// execute query\n$stmt = $connection-\u003eprepare($sql);\n$stmt-\u003eexecute($preparedValues);\n```\n\n#### Shorter example (_same as ☝_)\n```php\n// in EntityRepository/Model\n$sql = 'SELECT * FROM table';\n$stmt = $connection-\u003eprepare($apiFilter-\u003eapplyFilters($filters, $sql)); // SELECT * FROM table WHERE 1 AND field = :field_eq \n$stmt-\u003eexecute($apiFilter-\u003egetPreparedValues($filters, $sql));      // ['field_eq' =\u003e 'value']\n```\n\n## Supported filters\n\n### Equals - EQ (=)\n```http request\nGET http://host/endpoint/?field[eq]=value\nGET http://host/endpoint/?field=value\n```\n_Both examples ☝ are equal_\n\n### Not Equals - NEQ (!=)\n```http request\nGET http://host/endpoint/?field[neq]=value\n```\n\n### Greater Than - GT (\u003e)\n```http request\nGET http://host/endpoint/?field[gt]=value\n```\n\n### Greater Than Or Equals - GTE (\u003e=)\n```http request\nGET http://host/endpoint/?field[gte]=value\n```\n\n### Lower Than - LT (\u003c)\n```http request\nGET http://host/endpoint/?field[lt]=value\n```\n\n### Lower Than Or Equals - LTE (\u003c=)\n```http request\nGET http://host/endpoint/?field[lte]=value\n```\n\n### IN\n```http request\nGET http://host/endpoint/?type[in][]=one\u0026type[in][]=two\n```\n- `Tuples` are not allowed in `IN` filter\n\n### Function\n```http request\nGET http://host/endpoint?fullName=(Jon,Snow)\n```\n- there is much more options and possibilities with `functions` which you can see [here](#functions-in-filters)\n\n## `Tuples` in filters\n`Tuples`\n- are important in filters if you have some values, which **must** be sent together\n- are composed of two or more values (_`Tuple` of one value is just a value_)\n- items **must** be in `(` `)` and separated by `,`\n    - `array` in `Tuple` **must** be in `[` `]` and items separated by `;`\n- it is advised NOT to use a _space_ between values because of the _URL_ specific behavior\n- for more information about `Tuples` see https://github.com/MortalFlesh/MFCollectionsPHP#immutabletuple \n\n### Column with `Tuple`\nColumns declared by `Tuple` behaves the same as a single value but its value must be a `Tuple` as well.\nColumns can contain a filter specification for each value.\n- default filter is `EQ` for a single value and `IN` for an array of values (_in `Tuple`_)\n\n### Values with `Tuple`\nValues in the `Tuple` must have the same number of items as is the number of columns.\nValues can contain a filter specification for all values in a `Tuple`.\n\n❗**NOTE**: A filter specification **must not** be in both columns and values.\n\n### Usage\n```http request\nGET http://host/endpoint/?(first,second)[eq]=(one,two) \n```\n☝ means that you have two columns `first` and `second` and they must be sent together.\nColumn `first` must `equal` the value `\"one\"` and column `second` must `equal` the value `\"two\"`.\n\n## Examples\n❗For simplicity of examples, they are shown on the [`SQL Applicator`](#with-sql-applicator) which is NOT auto-registered❗\n\n### `IN` + `EQ` filter\n```http request\nGET http://host/person/?type[in][]=student\u0026type[in][]=admin\u0026name=Tom\n```\n\n```php\n$parameters = $request-\u003equery-\u003eall();\n// [\n//     \"type\" =\u003e [\n//         \"in\" =\u003e [\n//             0 =\u003e \"student\"\n//             1 =\u003e \"admin\"\n//         ]\n//     ],\n//     \"name\" =\u003e \"Tom\"\n// ]\n\n$filters = $apiFilter-\u003eparseFilters($parameters);\n$sql = 'SELECT * FROM person';\n\nforeach ($filters as $filter) {\n    $sql = $apiFilter-\u003eapplyFilter($filter, $sql);\n    \n    // 0. SELECT * FROM person WHERE 1 AND type IN (:type_in_0, :type_in_1) \n    // 1. SELECT * FROM person WHERE 1 AND type IN (:type_in_0, :type_in_1) AND name = :name_eq \n}\n\n$preparedValues = $apiFilter-\u003egetPreparedValues($filters, $sql);\n// [\n//     'type_in_0' =\u003e 'student',\n//     'type_in_1' =\u003e 'admin',\n//     'name_eq'   =\u003e 'Tom',\n// ]\n```\n\n### `GT` + `LT` filter (_between_)\n```http request\nGET http://host/person/?age[gt]=18\u0026age[lt]=30\n```\n\n```php\n$parameters = $request-\u003equery-\u003eall();\n// [\n//     \"age\" =\u003e [\n//         \"gt\" =\u003e 18\n//         \"lt\" =\u003e 30\n//     ],\n// ]\n\n$filters = $apiFilter-\u003eparseFilters($parameters);\n$sql = 'SELECT * FROM person';\n\n$sql = $apiFilter-\u003eapplyFilters($filters, $sql); // SELECT * FROM person WHERE 1 AND age \u003e :age_gt AND age \u003c :age_lt\n$preparedValues = $apiFilter-\u003egetPreparedValues($filters, $sql); // ['age_gt' =\u003e 18, 'age_lt' =\u003e 30]\n```\n\n### `EQ` with `Tuple`\n```http request\nGET http://host/person/?(firstname,surname)=(John,Snow)\n```\n\n```php\n$parameters = $request-\u003equery-\u003eall(); // [\"(firstname,surname)\" =\u003e \"(John,Snow)\"]\n\n$sql = 'SELECT * FROM person';\n$filters = $apiFilter-\u003eparseFilters($parameters);\n// [\n//     0 =\u003e Lmc\\ApiFilter\\Filter\\FilterWithOperator {\n//         private $title    =\u003e \"eq\"\n//         private $operator =\u003e \"=\"\n//         private $column   =\u003e \"firstname\"\n//         private $value    =\u003e Lmc\\ApiFilter\\Entity\\Value {\n//             private $value =\u003e \"John\"\n//         }\n//     },\n//     1 =\u003e Lmc\\ApiFilter\\Filter\\FilterWithOperator {\n//         private $title    =\u003e \"eq\"\n//         private $operator =\u003e \"=\"\n//         private $column   =\u003e \"surname\"\n//         private $value    =\u003e Lmc\\ApiFilter\\Entity\\Value {\n//             private $value =\u003e \"Snow\"\n//         }\n//     }\n// ]\n\n$sql = $apiFilter-\u003eapplyFilters($filters, $sql); // SELECT * FROM person WHERE 1 AND firstname = :firstname_eq AND surname = :surname_eq\n$preparedValues = $apiFilter-\u003egetPreparedValues($filters, $sql); // ['firstname_eq' =\u003e 'John', 'surname_eq' =\u003e 'Snow']\n```\n\n### More Examples\n\n#### Equals (_implicit and explicit_)\n```http request\nGET http://host/person/?fullName=Jon Snow\nGET http://host/person/?fullName[eq]=Jon Snow\n```\nResult:\n```yaml\n-   column: fullName\n    filters: eq\n    value: Jon Snow\n```\n\n#### Multiple filters (_implicit and explicit_)\nBy single values\n```http request\nGET http://host/person/?firstName=Jon\u0026surname=Snow\nGET http://host/person/?firstName[eq]=Jon\u0026surname[eq]=Snow\n```\n\nBy Tuples\n```http request\nGET http://host/person/?(firstName,surname)=(Jon,Snow)\nGET http://host/person/?(firstName,surname)[eq]=(Jon,Snow)\nGET http://host/person/?(firstName[eq],surname[eq])=(Jon,Snow)\n```\nResult:\n```yaml\n-   column: firstName\n    filters: eq\n    value: Jon\n\n-   column: surname\n    filters: eq\n    value: Snow\n```\n\n#### Multiple filters\nYou can mix all types of filters (_tuples, explicit, implicit_).\n\n##### _Perfect wife_ by generic filters\nBy single values\n```http request\nGET http://host/person/?age[gte]=18\u0026age[lt]=30\u0026category[in][]=serious\u0026category[in][]=marriage\u0026sense-of-humor=true\n```\n\nBy Tuples\n```http request\nGET http://host/person/?(age[gte],age[lt],category,sense-of-humor)=(18,30,[serious;marriage],true)\n```\nResult:\n```yaml\n-   column: age\n    filters: gte\n    value: 18\n\n-   column: age\n    filters: lt\n    value: 30\n\n-   column: category\n    filters: in\n    value: [ serious, marriage ]\n\n-   column: sense-of-humor\n    filters: eq\n    value: true\n```\n\n##### _Want to see movies_ by generic filters\nBy single values\n```http request\nGET http://host/movie/?year[gte]=2018\u0026rating[gte]=80\u0026genre[in][]=action\u0026genre[in][]=fantasy\n```\n\nBy Tuples\n```http request\nGET http://host/movie/?(year[gte],rating[gte],genre)=(2018,80,[action;fantasy])\n```\nResult:\n```yaml\n-   column: year\n    filters: gte\n    value: 2018\n\n-   column: rating\n    filters: gte\n    value: 80\n\n-   column: genre\n    filters: in\n    value: [ action, fantasy ]\n```\n\n## Functions in filters\nWith function you can handle all kinds of problems, which might be problematic with just a simple filters like `eq`, etc.\n\n### Example for `fullName` function\nLet's see how to work with functions and what is required to do. We will show it right on the example.\n\n#### Expected api\n```http request\nGET http://host/endpoint?fullName=(Jon,Snow)\n```\n☝️ example above shows what we want to offer to our consumers. It's easy and explicit enough.\n\nIt may even hide some inner differences, for example with simple filters, database column must have same name as field in filter, but with function, we can change it.\n\nLet's say that in database we have something like:\n```fs\ntype Person = {\n    first_name: string\n    lastname: string\n}\n```\n\n#### Initialization\nFirst of all, you have to define functions you want to use.\n```php\n// in DI container/factory\n$apiFilter = new ApiFilter();\n\n$apiFilter-\u003edeclareFunction(\n    'fullName',\n    [\n        new ParameterDefinition('firstName', 'eq', 'first_name'),   // parameter name and field name are different, so we need to define it\n        'lastname`,              // parameter name and field name are the same and we use the implicit `eq` filter, so it is defined simply \n    ]\n);\n```\nMethod `declareFunction` will create a function with filters based on parameters.  \n_There is also `registerFunction` method, which allows you to pass any function you want. This may be useful when you dont need filter functionality at all or have some custom storage, etc._\n\n#### Parsing and applying filters\nNow when request with `?fullName=(Jon,Snow)` come, `ApiFilter` can parse it to:\n```php\n// in service/controller/...\n$sql = 'SELECT * FROM person';\n\n$filters = $apiFilter-\u003eparseFilters($request-\u003equery-\u003eall());\n// [\n//      0 =\u003e Lmc\\ApiFilter\\Filter\\FilterFunction {\n//        private $title  =\u003e 'function'\n//        private $column =\u003e 'fullName'\n//        private $value  =\u003e Lmc\\ApiFilter\\Entity\\Value {\n//          private $value =\u003e Closure\n//        }\n//      },\n//\n//      1 =\u003e Lmc\\ApiFilter\\Filter\\FunctionParameter {\n//        private $title =\u003e 'function_parameter'\n//        private $column =\u003e 'firstName'\n//        private $value =\u003e Lmc\\ApiFilter\\Entity\\Value {\n//          private $value =\u003e 'Jon'\n//        }\n//      },\n//\n//      2 =\u003e Lmc\\ApiFilter\\Filter\\FunctionParameter {\n//        private $title =\u003e 'function_parameter'\n//        private $column =\u003e 'lastname'\n//        private $value =\u003e Lmc\\ApiFilter\\Entity\\Value {\n//          private $value =\u003e 'Snow'\n//        }\n//      }\n// ]\n\n$appliedSql = $apiFilter-\u003eapplyFilters($filters, $sql);\n// SELECT * FROM person WHERE first_name = :firstName_function_parameter AND lastname = :lastname_function_parameter\n\n$preparedValues = $apiFilter-\u003egetPreparedValues($filters, $sql);\n// [\n//      'firstName_function_parameter' =\u003e 'Jon',\n//      'lastname_function_parameter' =\u003e 'Snow',\n// ]\n```\n\n#### Supported function usage\nAll examples below results the same. We have that many options, so we can allow as many different consumers as possible.\n\n```http request\n### Explicit function call\nGET http://host/endpoint?fullName=(Jon,Snow)\n\n### Explicit function call with values \nGET http://host/endpoint?function=fullName\u0026firstName=Jon\u0026lastname=Snow\n\n### Implicit function call by values\nGET http://host/endpoint?firstName=Jon\u0026lastname=Snow\n\n### Explicit function call by tuple \nGET http://host/endpoint?(function,firstName,surname)=(fullName, Jon, Snow)\n\n### Implicit function call by tuple\nGET http://host/endpoint?(firstName,surname)=(Jon, Snow)\n\n### Explicit function call by filter parameter \nGET http://host/endpoint?filter[]=(fullName,Jon,Snow)\n```\n\n### Function Parameters Definition\nTo `declare` or `register` function, you have to define its parameters. There are many ways/needs to do it.\n\n#### Defined as string\nThis is the easiest way to do it. You just define a name.\n\nIt means:\n- you want `eq` filter (_or `IN` for array_) and the column name and parameter name are the same\n- the value for this parameter is mandatory\n\n```php\n$apiFilter-\u003edeclareFunction('fullName', ['firstName', 'surname']);\n```\n\n#### Defined as array\nThis allows you to pass more options for a paramater.\n\n##### Only one item\nIf you declare it just by giving the only item, it is the same as definition by string above.\n```php\n$apiFilter-\u003edeclareFunction('fullName', [['firstName'], ['surname']]);\n```\n\n##### More than one item\nIt means\n- `firstName` parameter uses `eq` filter, has `first_name` column in storage and is mandatory\n- `surname` parameter uses `eq` filter, has `lastname` column in storage and its value is `Snow` (_which will always be used and no value can override it_)\n```php\n$apiFilter-\u003edeclareFunction('fullName', [\n    ['firstName', 'eq', 'first_name'],\n    ['surname', 'eq', 'lastname', 'Snow']\n]);\n```\n\n#### Defined as object\nThis allows you to pass same options as with the array, but explicitly defined object. (_It even has some special constructor methods to simplify creation_)\n```php\n$apiFilter-\u003edeclareFunction('fullName', [\n    new ParameterDefinition('firstName', 'eq', 'first_name'),\n    new ParameterDefinition('surname', 'eq', 'lastname, new Value('Snow'))\n]);\n``` \n\n#### Combinations\nAll options can be combined to best suite the parameter.\n\n##### Declaration\n```php\n$apiFilter-\u003edeclareFunction('fullNameGrownMan', [\n    ['firstName', 'eq', 'first_name'],\n    'surname',\n    ['age', 'gte', 'age', 18],\n    ParameterDefinition::equalToDefaultValue('gender', new Value('male')),\n]);\n```\n\n##### Usage\n```http request\nGET http://endpoint/host?fullNameGrownMan=(Jon,Snow)\n```\n\n### Register and Execute function\nExample below is just for explicit demonstration, you should probably never allow execute SQL queries like this.\n\n#### Usage in PHP\n```php\n// in DI container/factory\n$apiFilter = new ApiFilter();\n\n$apiFilter-\u003eregisterFunction(\n    'sql',\n    ['query'],\n    function (\\PDO $client, FunctionParameter $query): \\PDOStatement {\n        return $client-\u003equery($query-\u003egetValue()-\u003egetValue());\n    }\n);\n\n// in service/controller/...\n$statement = $apiFilter-\u003eexecuteFunction('sql', $queryParameters, $client);    // \\PDOStatement\n\n$statement-\u003eexecute();\n// fetch result, etc...\n```\n\n#### Usage of the API\nAll examples below results the same. We have that many options, so we can allow as many different consumers as possible.\n\n```http request\n### Explicit function call\nGET http://endpoint/host?sql=SELECT * FROM person\n\n### Explicit function call with values\nGET http://host/endpoint?function=sql\u0026query=SELECT * FROM person\n\n### Implicit function call by values\nGET http://host/endpoint?query=SELECT * FROM person\n\n### Explicit function call by tuple\nGET http://host/endpoint?(function,query)=(sql, SELECT * FROM person)\n\n### Explicit function call by filter parameter\nGET http://host/endpoint?filter[]=(sql, SELECT * FROM person)\n```\n\n## Exceptions and error handling\n\n_Known_ exceptions occurring inside ApiFilter implements `Lmc\\ApiFilter\\Exception\\ApiFilterExceptionInterface`. The exception tree is:\n\n| Exception | Thrown when |\n| ---       | ---         |\n| ApiFilterExceptionInterface | Common interface of all ApiFilter exceptions |\n| └ InvalidArgumentException | Base exception for assertion failed |\n|   └ UnknownFilterException | Unknown filter is used in query parameters |\n|   └ UnsupportedFilterableException | This exception will be thrown when no _applicator_ supports given _filterable_. |\n|   └ UnsupportedFilterException | This exception should not be thrown on the client side. It is meant for developing an ApiFilter library - to ensure all Filter types are supported. |\n|   └ TupleException | Common exception for all problems with a `Tuple`. It also implements `MF\\Collection\\Exception\\TupleExceptionInterface` which might be thrown inside parsing. |\n\nPlease note if you register a custom _applicator_ to the ApiFilter (via `$apiFilter-\u003eregisterApplicator()`), it may throw other exceptions which might not implement `ApiFilterExceptionInterface`.\n\n## Development\n\n### Install\n```bash\ncomposer install\n```\n\n### Tests\n```bash\ncomposer all\n```\n\n## Todo\n- defineAllowed: (_this should be on DI level_)\n    - Fields (columns)\n    - Filters\n    - Values\n- add more examples:\n    - different configuration per entity/table\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flmc-eu%2Fapi-filter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flmc-eu%2Fapi-filter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flmc-eu%2Fapi-filter/lists"}