Skip to content

Commit c78ed0b

Browse files
adryyy10adriafigueressoyuka
authoredDec 13, 2024··
feat(laravel): boolean filter (#6806)
* feat: Adding boolean filter * refactor: return early if wrong values * tests: boolean filter test * refactor(BooleanFilter): cs fixer * add test --------- Co-authored-by: adriafigueres <adria.figueres@m10c.com> Co-authored-by: soyuka <soyuka@users.noreply.github.com>
1 parent c59e275 commit c78ed0b

File tree

9 files changed

+101
-3
lines changed

9 files changed

+101
-3
lines changed
 

‎src/Laravel/ApiPlatformProvider.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
use ApiPlatform\Laravel\Controller\EntrypointController;
8282
use ApiPlatform\Laravel\Eloquent\Extension\FilterQueryExtension;
8383
use ApiPlatform\Laravel\Eloquent\Extension\QueryExtensionInterface;
84+
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
8485
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
8586
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
8687
use ApiPlatform\Laravel\Eloquent\Filter\FilterInterface as EloquentFilterInterface;
@@ -422,7 +423,7 @@ public function register(): void
422423

423424
$this->app->bind(OperationMetadataFactoryInterface::class, OperationMetadataFactory::class);
424425

425-
$this->app->tag([EqualsFilter::class, PartialSearchFilter::class, DateFilter::class, OrderFilter::class, RangeFilter::class], EloquentFilterInterface::class);
426+
$this->app->tag([BooleanFilter::class, EqualsFilter::class, PartialSearchFilter::class, DateFilter::class, OrderFilter::class, RangeFilter::class], EloquentFilterInterface::class);
426427

427428
$this->app->bind(FilterQueryExtension::class, function (Application $app) {
428429
$tagged = iterator_to_array($app->tagged(EloquentFilterInterface::class));

‎src/Laravel/Eloquent/Extension/FilterQueryExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function apply(Builder $builder, array $uriVariables, Operation $operatio
4040
$context['operation'] = $operation;
4141

4242
foreach ($operation->getParameters() ?? [] as $parameter) {
43-
if (!($values = $parameter->getValue()) || $values instanceof ParameterNotFound) {
43+
if (null === ($values = $parameter->getValue()) || $values instanceof ParameterNotFound) {
4444
continue;
4545
}
4646

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Laravel\Eloquent\Filter;
15+
16+
use ApiPlatform\Metadata\JsonSchemaFilterInterface;
17+
use ApiPlatform\Metadata\Parameter;
18+
use Illuminate\Database\Eloquent\Builder;
19+
use Illuminate\Database\Eloquent\Model;
20+
21+
final class BooleanFilter implements FilterInterface, JsonSchemaFilterInterface
22+
{
23+
use QueryPropertyTrait;
24+
25+
private const BOOLEAN_VALUES = [
26+
'true' => true,
27+
'false' => false,
28+
'1' => true,
29+
'0' => false,
30+
];
31+
32+
/**
33+
* @param Builder<Model> $builder
34+
* @param array<string, mixed> $context
35+
*/
36+
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
37+
{
38+
if (!\is_string($values) || !\array_key_exists($values, self::BOOLEAN_VALUES)) {
39+
return $builder;
40+
}
41+
42+
return $builder->{$context['whereClause'] ?? 'where'}($this->getQueryProperty($parameter), $values);
43+
}
44+
45+
public function getSchema(Parameter $parameter): array
46+
{
47+
return ['type' => 'boolean'];
48+
}
49+
}

‎src/Laravel/Metadata/ParameterValidationResourceMetadataCollectionFactory.php

+4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ private function addSchemaValidation(Parameter $parameter): Parameter
144144
$assertions[] = 'array';
145145
}
146146

147+
if (isset($schema['type']) && 'boolean' === $schema['type']) {
148+
$assertions[] = 'boolean';
149+
}
150+
147151
if (!$assertions) {
148152
return $parameter;
149153
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Laravel\Tests\Eloquent\Filter;
15+
16+
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
17+
use ApiPlatform\Metadata\QueryParameter;
18+
use Illuminate\Database\Eloquent\Builder;
19+
use PHPUnit\Framework\TestCase;
20+
21+
final class BooleanFilterTest extends TestCase
22+
{
23+
public function testOperator(): void
24+
{
25+
$f = new BooleanFilter();
26+
$builder = $this->createStub(Builder::class);
27+
$this->assertEquals($builder, $f->apply($builder, ['is_active' => 'true'], new QueryParameter(key: 'isActive', property: 'is_active')));
28+
}
29+
}

‎src/Laravel/Tests/EloquentTest.php

+11
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,15 @@ public function testWithAccessor(): void
406406
$res = $this->get('/api/with_accessors/1', ['Accept' => ['application/ld+json']]);
407407
$this->assertArraySubset(['name' => 'test'], $res->json());
408408
}
409+
410+
public function testBooleanFilter(): void
411+
{
412+
BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
413+
$res = $this->get('/api/books?published=notabool', ['Accept' => ['application/ld+json']]);
414+
$this->assertEquals($res->getStatusCode(), 422);
415+
416+
$res = $this->get('/api/books?published=0', ['Accept' => ['application/ld+json']]);
417+
$this->assertEquals($res->getStatusCode(), 200);
418+
$this->assertEquals($res->json()['totalItems'], 0);
419+
}
409420
}

‎src/Laravel/workbench/app/Models/Book.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace Workbench\App\Models;
1515

16+
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
1617
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
1718
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
1819
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
@@ -71,12 +72,13 @@
7172
property: 'name'
7273
)]
7374
#[QueryParameter(key: 'properties', filter: PropertyFilter::class)]
75+
#[QueryParameter(key: 'published', filter: BooleanFilter::class)]
7476
class Book extends Model
7577
{
7678
use HasFactory;
7779
use HasUlids;
7880

79-
protected $visible = ['name', 'author', 'isbn', 'publication_date', 'is_available'];
81+
protected $visible = ['name', 'author', 'isbn', 'publication_date', 'published', 'is_available'];
8082
protected $fillable = ['name', 'is_available'];
8183
protected $casts = [
8284
'is_available' => 'boolean',

‎src/Laravel/workbench/database/factories/BookFactory.php

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function definition(): array
3939
'publication_date' => fake()->optional()->date(),
4040
'is_available' => 1 === random_int(0, 1),
4141
'internal_note' => fake()->text(),
42+
'published' => fake()->boolean(100),
4243
];
4344
}
4445
}

‎src/Laravel/workbench/database/migrations/2023_07_15_231244_create_book_table.php

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function up(): void
3434
$table->date('publication_date')->nullable();
3535
$table->boolean('is_available')->default(true);
3636
$table->text('internal_note')->nullable();
37+
$table->boolean('published')->nullable();
3738
$table->integer('author_id')->unsigned();
3839
$table->foreign('author_id')->references('id')->on('authors');
3940
$table->timestamps();

0 commit comments

Comments
 (0)
Please sign in to comment.