diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 32782aa..23e76cf 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -2,9 +2,9 @@ name: run-tests
on:
push:
- branches: [main]
+ branches: [ main ]
pull_request:
- branches: [main]
+ branches: [ main ]
jobs:
test:
@@ -13,13 +13,16 @@ jobs:
fail-fast: true
matrix:
os: [ ubuntu-latest ]
- php: [ 8.2 ]
- laravel: [ 10.* ]
+ php: [ 8.2, 8.3 ]
+ laravel: [ 10.*, 11.* ]
stability: [ prefer-stable ]
include:
- laravel: 10.*
testbench: 8.*
carbon: ^2.63
+ - laravel: 11.*
+ testbench: 9.*
+ carbon: ^2.63
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
@@ -29,7 +32,7 @@ jobs:
env:
MYSQL_USER: user
MYSQL_PASSWORD: secret
- MYSQL_DATABASE: laravel-query-builder-powered
+ MYSQL_DATABASE: testing
MYSQL_ROOT_PASSWORD: root
ports:
- 3306
diff --git a/README.md b/README.md
index 56da6db..c8c92dd 100644
--- a/README.md
+++ b/README.md
@@ -36,14 +36,77 @@ return [
## Usage
+### Pagination
+
+This package uses Laravel pagination by default; However, it allows you to specify through parameters of
+queries, the number of records to obtain, you can even obtain all the records.
+
+```php
+GET /books?per_page=30
+```
+
+It is recommended to use the `result` method instead of `paginate` or `get`. Since `result` encapsulates the logic of
+both yes, it is requested to show all, below `result` will use `get` but if you want to see a number of records
+below will use `paginate` with the amount provided.
+
+```php
+use Spatie\QueryBuilder\AllowedFilter;
+use TeamQ\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\Filters\TextFilter;
+
+$query = QueryBuilder::for(Book::class);
+
+$query->result();
+```
+
### Filters
-| Filter | Class |
-|--------|-------------------------------------------|
-| Text | `TeamQ\QueryBuilder\Filters\TextFilter` |
-| Number | `TeamQ\QueryBuilder\Filters\NumberFilter` |
-| Date | `TeamQ\QueryBuilder\Filters\DateFilter` |
-| Global | `TeamQ\QueryBuilder\Filters\GlobalFilter` |
+| Filter | Class | Operators |
+|--------|-------------------------------------------|-----------|
+| Text | `TeamQ\QueryBuilder\Filters\TextFilter` | Text |
+| Number | `TeamQ\QueryBuilder\Filters\NumberFilter` | Number |
+| Date | `TeamQ\QueryBuilder\Filters\DateFilter` | Number |
+| Global | `TeamQ\QueryBuilder\Filters\GlobalFilter` | - |
+
+
+ Text comparison operators
+
+| Comparison operator | Key |
+|---------------------|-----|
+| Equal | 1 |
+| Not Equal | 2 |
+| Start With | 3 |
+| Not Start With | 4 |
+| End With | 5 |
+| Not End With | 6 |
+| Contains | 7 |
+| Not Contains | 8 |
+| In | 9 |
+| Not In | 10 |
+| Filled | 11 |
+| Not Filled | 12 |
+
+
+
+
+ Number comparison operators
+
+| Comparison operator | Key |
+|-----------------------|-----|
+| Equal | 1 |
+| Not Equal | 2 |
+| Greater Than | 3 |
+| Greater Than Or Equal | 4 |
+| Less Than | 5 |
+| Less Than Or Equal | 6 |
+| Between | 7 |
+| Not Between | 8 |
+| In | 9 |
+| Not In | 10 |
+| Filled | 11 |
+| Not Filled | 12 |
+
+
You can use advanced filters that have support for multiple comparison operators.
The available comparison operators are located in `TeamQ\QueryBuilder\Enums\Comparators`
@@ -56,7 +119,7 @@ GET /books?filter[isbn][value]=54213&filter[isbn][operator]=5
```php
use Spatie\QueryBuilder\AllowedFilter;
-use Spatie\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\QueryBuilder;
use TeamQ\QueryBuilder\Filters\TextFilter;
QueryBuilder::for(Book::class)
@@ -70,7 +133,7 @@ and pass the type of `join` to use.
```php
use Spatie\QueryBuilder\AllowedFilter;
-use Spatie\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\QueryBuilder;
use TeamQ\QueryBuilder\Filters\TextFilter;
use TeamQ\QueryBuilder\Enums\JoinType;
@@ -80,7 +143,89 @@ QueryBuilder::for(Book::class)
]);
```
-#### Global Filter
+#### _Text Filter_
+
+The following example uses the comparison operator `5`, which is equivalent to asking if there is a book where
+isbn `End With` 54213
+
+```php
+GET /books?filter[isbn][value]=54213&filter[isbn][operator]=5
+```
+
+```sql
+select *
+from `books`
+where lower(`isbn`) like '%54213'
+```
+
+```php
+use Spatie\QueryBuilder\AllowedFilter;
+use TeamQ\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\Filters\TextFilter;
+
+QueryBuilder::for(Book::class)
+ ->allowedFilters([
+ AllowedFilter::custom('isbn', new TextFilter()),
+ ]);
+```
+
+#### _Number Filter_
+
+The following example uses the comparison operator `9`, which is equivalent to asking if there is a book where
+id `In` 1, 5 or 9
+
+For this example an array of values was used. Arraying values is supported by all types of operators (text and number).
+
+```php
+GET /books?filter[id][value][0]=1&filter[id][value][1]=5&filter[id][value][2]=9&filter[id][operator]=9
+```
+
+```sql
+select *
+from `books`
+where id in (1, 5, 9)
+```
+
+```php
+use Spatie\QueryBuilder\AllowedFilter;
+use TeamQ\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\Filters\NumberFilter;
+
+QueryBuilder::for(Book::class)
+ ->allowedFilters([
+ AllowedFilter::custom('id', new NumberFilter()),
+ ]);
+```
+
+#### _Date Filter_
+
+The following example uses the comparison operator `8`, which is equivalent to asking if there is a book where
+created at `Not Between` 2019-08-01 and 2019-08-10
+
+For this example an array of values was used. Arraying values is supported by all types of operators (text and number).
+
+```php
+GET /books?filter[created_at][value][0]=2019-08-01&filter[created_at][value][1]=2019-08-10&filter[id][operator]=8
+```
+
+```sql
+select *
+from `books`
+where created_at not between '2019-08-01' and '2019-08-10'
+```
+
+```php
+use Spatie\QueryBuilder\AllowedFilter;
+use TeamQ\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\Filters\DateFilter;
+
+QueryBuilder::for(Book::class)
+ ->allowedFilters([
+ AllowedFilter::custom('created_at', new DateFilter()),
+ ]);
+```
+
+#### _Global Filter_
The global filter implements general search functionality for the model and its relationships.
@@ -91,7 +236,7 @@ To use this filter, you must pass the model fields to be filtered or their relat
```php
use Spatie\QueryBuilder\AllowedFilter;
-use Spatie\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\QueryBuilder;
use TeamQ\QueryBuilder\Filters\GlobalFilter;
QueryBuilder::for(Book::class)
@@ -103,6 +248,8 @@ QueryBuilder::for(Book::class)
]);
```
+---
+
### Sorts
| Sort | Class |
@@ -110,14 +257,14 @@ QueryBuilder::for(Book::class)
| Relation | `TeamQ\QueryBuilder\Sorts\RelationSort` |
| Case | `TeamQ\QueryBuilder\Sorts\CaseSort` |
-#### RelationSort
+#### _RelationSort_
To sort by fields of related tables you must use `join`, there is no easy way to do it from eloquent,
so you can use `RelationSort`, this class receives the type of `join` as a parameter.
```php
use Spatie\QueryBuilder\AllowedFilter;
-use Spatie\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\QueryBuilder;
use TeamQ\QueryBuilder\Sorts\RelationSort;
QueryBuilder::for(Book::class)
@@ -126,7 +273,7 @@ QueryBuilder::for(Book::class)
])
```
-#### CaseSort
+#### _CaseSort_
If you use enums or states, where each enum or state is represented by a number, you may want to sort by name
of that enum or state and not by the number, then you can use `CaseSort`.
@@ -138,7 +285,7 @@ related model. By default, it is `Inner Join`.
```php
use Spatie\QueryBuilder\AllowedFilter;
-use Spatie\QueryBuilder\QueryBuilder;
+use TeamQ\QueryBuilder\QueryBuilder;
use TeamQ\QueryBuilder\Sorts\CaseSort;
QueryBuilder::for(Book::class)
diff --git a/composer.json b/composer.json
index f9e5d6e..62fb6a7 100644
--- a/composer.json
+++ b/composer.json
@@ -18,24 +18,16 @@
}
],
"require": {
- "php": "^8.2",
- "illuminate/contracts": "^10.0",
+ "php": "^8.2|^8.3",
"kirschbaum-development/eloquent-power-joins": "^3.2",
"spatie/laravel-package-tools": "^1.14.0",
- "spatie/laravel-query-builder": "^5.2"
+ "spatie/laravel-query-builder": "^5.2|^6.0"
},
"require-dev": {
- "roave/security-advisories": "dev-latest",
"laravel/pint": "^1.0",
- "nunomaduro/collision": "^7.9",
- "nunomaduro/larastan": "^2.0.1",
- "orchestra/testbench": "^8.0",
+ "orchestra/testbench": "^8.0|^9.0",
"pestphp/pest": "^2.0",
- "pestphp/pest-plugin-arch": "^2.0",
- "pestphp/pest-plugin-laravel": "^2.0",
- "phpstan/extension-installer": "^1.1",
- "phpstan/phpstan-deprecation-rules": "^1.0",
- "phpstan/phpstan-phpunit": "^1.0"
+ "pestphp/pest-plugin-laravel": "^2.0"
},
"autoload": {
"psr-4": {
@@ -49,7 +41,6 @@
},
"scripts": {
"post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi",
- "analyse": "vendor/bin/phpstan analyse",
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage",
"format": "vendor/bin/pint"
diff --git a/docker-compose.yml b/docker-compose.yml
index 3eb976b..f27d1f5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,23 +1,24 @@
-version: "3.6"
-
services:
app:
build:
context: .
dockerfile: docker/Dockerfile
- container_name: laravel-query-builder-powered
+ container_name: laravel-datatable-api
volumes:
- ".:/var/www/html"
depends_on:
- mysql
+ networks:
+ - laravel-datatable-net
mysql:
image: 'mysql/mysql-server:8.0'
+ container_name: laravel-datatable-db
ports:
- '${FORWARD_DB_PORT:-3306}:3306'
environment:
MYSQL_ROOT_PASSWORD: 'root'
MYSQL_ROOT_HOST: "%"
- MYSQL_DATABASE: 'laravel-query-builder-powered'
+ MYSQL_DATABASE: 'testing'
MYSQL_USER: 'laravel'
MYSQL_PASSWORD: 'laravel'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
@@ -28,3 +29,8 @@ services:
- ping
retries: 3
timeout: 5s
+ networks:
+ - laravel-datatable-net
+networks:
+ laravel-datatable-net:
+ driver: bridge
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5d87581..003e8fb 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,5 +1,5 @@
# Base image
-FROM php:8.2-alpine
+FROM php:8.3-alpine
# Create app folder
RUN mkdir -p /var/www/html/
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index c8ff637..03c3365 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -16,7 +16,7 @@
backupStaticProperties="false"
>
-
+
tests
@@ -31,7 +31,7 @@
-
+
diff --git a/src/Filters/DateFilter.php b/src/Filters/DateFilter.php
index 380ab51..2399a8f 100644
--- a/src/Filters/DateFilter.php
+++ b/src/Filters/DateFilter.php
@@ -13,6 +13,10 @@
*/
class DateFilter extends Filter
{
+ /**
+ * Operators that receive values as arrays or should be filtered as an array.
+ * Ex: In, NotIn, Between, etc.
+ */
protected const ARRAY_OPERATORS = [
Comparators\Number::Between,
Comparators\Number::NotBetween,
diff --git a/src/Filters/Filter.php b/src/Filters/Filter.php
index 374385a..19749dc 100644
--- a/src/Filters/Filter.php
+++ b/src/Filters/Filter.php
@@ -16,6 +16,10 @@ abstract class Filter implements IFilter
{
use HasPropertyRelationship;
+ /**
+ * Operators that receive values as arrays or should be filtered as an array.
+ * Ex: In, NotIn, Between, etc.
+ */
protected const ARRAY_OPERATORS = [];
/**
@@ -71,9 +75,14 @@ public function __invoke(Builder $query, $value, string $property): void
if ($value !== false) {
if (is_array($value)) {
+ // If the value received is an array of values and the operator we receive is an operator
+ // of arrays, then this follows the normal flow.
if (in_array($operator, static::ARRAY_OPERATORS, true)) {
$this->handle($query, $value, $property, $operator);
} else {
+ // But if it is not an array operator, then we encapsulate the query logic in
+ // a subquery, applying the comparison operator provided for each of the
+ // y values connected by "or".
$query->where(function (Builder $query) use ($value, $property, $operator) {
foreach ($value as $item) {
$this->handle($query, $item, $property, $operator, 'or');
diff --git a/src/Filters/NumberFilter.php b/src/Filters/NumberFilter.php
index 5b3cf78..87826b4 100644
--- a/src/Filters/NumberFilter.php
+++ b/src/Filters/NumberFilter.php
@@ -13,6 +13,10 @@
*/
class NumberFilter extends Filter
{
+ /**
+ * Operators that receive values as arrays or should be filtered as an array.
+ * Ex: In, NotIn, Between, etc.
+ */
protected const ARRAY_OPERATORS = [
Comparators\Number::Between,
Comparators\Number::NotBetween,
diff --git a/src/Filters/TextFilter.php b/src/Filters/TextFilter.php
index 2f4541a..cc67b4d 100644
--- a/src/Filters/TextFilter.php
+++ b/src/Filters/TextFilter.php
@@ -13,6 +13,10 @@
*/
class TextFilter extends Filter
{
+ /**
+ * Operators that receive values as arrays or should be filtered as an array.
+ * Ex: In, NotIn, Between, etc.
+ */
protected const ARRAY_OPERATORS = [
Comparators\Text::In,
Comparators\Text::NotIn,