Skip to content

Commit

Permalink
feat(laravel): boolean filter (#6806)
Browse files Browse the repository at this point in the history
* feat: Adding boolean filter

* refactor: return early if wrong values

* tests: boolean filter test

* refactor(BooleanFilter): cs fixer

* add test

---------

Co-authored-by: adriafigueres <[email protected]>
Co-authored-by: soyuka <[email protected]>
  • Loading branch information
3 people authored Dec 13, 2024
1 parent c59e275 commit c78ed0b
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/Laravel/ApiPlatformProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
use ApiPlatform\Laravel\Controller\EntrypointController;
use ApiPlatform\Laravel\Eloquent\Extension\FilterQueryExtension;
use ApiPlatform\Laravel\Eloquent\Extension\QueryExtensionInterface;
use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
use ApiPlatform\Laravel\Eloquent\Filter\FilterInterface as EloquentFilterInterface;
Expand Down Expand Up @@ -422,7 +423,7 @@ public function register(): void

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

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

$this->app->bind(FilterQueryExtension::class, function (Application $app) {
$tagged = iterator_to_array($app->tagged(EloquentFilterInterface::class));
Expand Down
2 changes: 1 addition & 1 deletion src/Laravel/Eloquent/Extension/FilterQueryExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function apply(Builder $builder, array $uriVariables, Operation $operatio
$context['operation'] = $operation;

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

Expand Down
49 changes: 49 additions & 0 deletions src/Laravel/Eloquent/Filter/BooleanFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Laravel\Eloquent\Filter;

use ApiPlatform\Metadata\JsonSchemaFilterInterface;
use ApiPlatform\Metadata\Parameter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

final class BooleanFilter implements FilterInterface, JsonSchemaFilterInterface
{
use QueryPropertyTrait;

private const BOOLEAN_VALUES = [
'true' => true,
'false' => false,
'1' => true,
'0' => false,
];

/**
* @param Builder<Model> $builder
* @param array<string, mixed> $context
*/
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
{
if (!\is_string($values) || !\array_key_exists($values, self::BOOLEAN_VALUES)) {
return $builder;
}

return $builder->{$context['whereClause'] ?? 'where'}($this->getQueryProperty($parameter), $values);
}

public function getSchema(Parameter $parameter): array
{
return ['type' => 'boolean'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ private function addSchemaValidation(Parameter $parameter): Parameter
$assertions[] = 'array';
}

if (isset($schema['type']) && 'boolean' === $schema['type']) {
$assertions[] = 'boolean';
}

if (!$assertions) {
return $parameter;
}
Expand Down
29 changes: 29 additions & 0 deletions src/Laravel/Tests/Eloquent/Filter/BooleanFilterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Laravel\Tests\Eloquent\Filter;

use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
use ApiPlatform\Metadata\QueryParameter;
use Illuminate\Database\Eloquent\Builder;
use PHPUnit\Framework\TestCase;

final class BooleanFilterTest extends TestCase
{
public function testOperator(): void
{
$f = new BooleanFilter();
$builder = $this->createStub(Builder::class);
$this->assertEquals($builder, $f->apply($builder, ['is_active' => 'true'], new QueryParameter(key: 'isActive', property: 'is_active')));
}
}
11 changes: 11 additions & 0 deletions src/Laravel/Tests/EloquentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,15 @@ public function testWithAccessor(): void
$res = $this->get('/api/with_accessors/1', ['Accept' => ['application/ld+json']]);
$this->assertArraySubset(['name' => 'test'], $res->json());
}

public function testBooleanFilter(): void
{
BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
$res = $this->get('/api/books?published=notabool', ['Accept' => ['application/ld+json']]);
$this->assertEquals($res->getStatusCode(), 422);

$res = $this->get('/api/books?published=0', ['Accept' => ['application/ld+json']]);
$this->assertEquals($res->getStatusCode(), 200);
$this->assertEquals($res->json()['totalItems'], 0);
}
}
4 changes: 3 additions & 1 deletion src/Laravel/workbench/app/Models/Book.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Workbench\App\Models;

use ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter;
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
Expand Down Expand Up @@ -71,12 +72,13 @@
property: 'name'
)]
#[QueryParameter(key: 'properties', filter: PropertyFilter::class)]
#[QueryParameter(key: 'published', filter: BooleanFilter::class)]
class Book extends Model
{
use HasFactory;
use HasUlids;

protected $visible = ['name', 'author', 'isbn', 'publication_date', 'is_available'];
protected $visible = ['name', 'author', 'isbn', 'publication_date', 'published', 'is_available'];
protected $fillable = ['name', 'is_available'];
protected $casts = [
'is_available' => 'boolean',
Expand Down
1 change: 1 addition & 0 deletions src/Laravel/workbench/database/factories/BookFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function definition(): array
'publication_date' => fake()->optional()->date(),
'is_available' => 1 === random_int(0, 1),
'internal_note' => fake()->text(),
'published' => fake()->boolean(100),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function up(): void
$table->date('publication_date')->nullable();
$table->boolean('is_available')->default(true);
$table->text('internal_note')->nullable();
$table->boolean('published')->nullable();
$table->integer('author_id')->unsigned();
$table->foreign('author_id')->references('id')->on('authors');
$table->timestamps();
Expand Down

0 comments on commit c78ed0b

Please sign in to comment.