Skip to content

Commit

Permalink
feat: parameter implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Mar 4, 2024
1 parent 46de660 commit c11e889
Show file tree
Hide file tree
Showing 23 changed files with 698 additions and 30 deletions.
Expand Up @@ -71,7 +71,7 @@ For this to work, we need to consider a 4 year old bug on searching with UIDs. O
/books?author.id=/author/1
```

Many attempts to fix these behavior on API Platform have lead to bugs and to be reverted. My proposal is to change how filters are applied to provide filters with less logic, that are easier to maintain and that do one thing good.
Many attempts to fix these behavior on API Platform have lead to bugs and to be reverted. The proposal is to change how filters are applied to provide filters with less logic, that are easier to maintain and that do one thing good.

For the following example we will use an UUID to represent the stored identifier of an Author resource.

Expand Down
61 changes: 61 additions & 0 deletions docs/adr/filter-notes
@@ -0,0 +1,61 @@
```php
// how to give uidfilter the parameters it should declare?
// is it automatic if we find a property having the uid type?
#[Get(filters: [new SearchFilter(), new UidFilter()])
#[Parameter('key', schema: ['type' => 'string'])] // add transform + validate extension points
class Book {

}

final class Parameter {
mixed $value;
?string $property;
?string $class;
array $attributes;
}

class FilterInterface {}

class UidFilter {
public function __construct(private readonly string $class) {}

public function parseQueryParameter(array $queryParameters = []): Parameter[] {
return [
new Parameter(value: '', attributes: ['operation' => 'and'])
];
}

// Query parameter type
public function getSchema(): array {
return ['type' => 'string'];
}

public function getOpenApiParameter(): OpenApi\Parameter {
return ...;
}
}

public function process(Operation $operation) {
$request = $context['request'];

foreach($operation->getFilters() as $filter) {
foreach ($filter->parseQueryParameter($request->query, $context) as $parameter) {
$this->queryParameterValidator->validate($filter, $parameter, $context);
$filter->execute($filter, $parameter, $context);
}
}
}
```


TODO:
see SerializerFilterContextBuilder: public function apply(Request $request, bool $normalization, array $attributes, array &$context): void;
maybe something like:

```
class SerializerFilterInterface {
public function getNormalizationContext(...);
public function getDenormalizationContext(...);
}
```

2 changes: 1 addition & 1 deletion src/Metadata/GraphQl/Subscription.php
Expand Up @@ -17,7 +17,7 @@

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
final class Subscription extends Operation

{
public function __construct(
?string $resolver = null,
?array $args = null,
Expand Down
18 changes: 18 additions & 0 deletions src/Metadata/HeaderParameter.php
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Metadata;

class HeaderParameter extends Parameter implements HeaderParameterInterface
{
}
18 changes: 18 additions & 0 deletions src/Metadata/HeaderParameterInterface.php
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Metadata;

interface HeaderParameterInterface
{
}
18 changes: 9 additions & 9 deletions src/Metadata/Metadata.php
Expand Up @@ -21,15 +21,15 @@
abstract class Metadata
{
/**
* @param string|null $deprecationReason https://api-platform.com/docs/core/deprecations/#deprecating-resource-classes-operations-and-properties
* @param string|null $security https://api-platform.com/docs/core/security
* @param string|null $securityPostDenormalize https://api-platform.com/docs/core/security/#executing-access-control-rules-after-denormalization
* @param mixed|null $mercure
* @param mixed|null $messenger
* @param mixed|null $input
* @param mixed|null $output
* @param mixed|null $provider
* @param mixed|null $processor
* @param string|null $deprecationReason https://api-platform.com/docs/core/deprecations/#deprecating-resource-classes-operations-and-properties
* @param string|null $security https://api-platform.com/docs/core/security
* @param string|null $securityPostDenormalize https://api-platform.com/docs/core/security/#executing-access-control-rules-after-denormalization
* @param mixed|null $mercure
* @param mixed|null $messenger
* @param mixed|null $input
* @param mixed|null $output
* @param mixed|null $provider
* @param mixed|null $processor
* @param array<string, Parameter> $parameters
*/
public function __construct(
Expand Down
24 changes: 12 additions & 12 deletions src/Metadata/Operation.php
Expand Up @@ -47,18 +47,18 @@ abstract class Operation extends Metadata
* class?: string|null,
* name?: string,
* }|string|false|null $output {@see https://api-platform.com/docs/core/dto/#specifying-an-input-or-an-output-data-representation}
* @param string|array|bool|null $mercure {@see https://api-platform.com/docs/core/mercure}
* @param string|bool|null $messenger {@see https://api-platform.com/docs/core/messenger/#dispatching-a-resource-through-the-message-bus}
* @param bool|null $elasticsearch {@see https://api-platform.com/docs/core/elasticsearch/}
* @param bool|null $read {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $deserialize {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $validate {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $write {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $serialize {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $fetchPartial {@see https://api-platform.com/docs/core/performance/#fetch-partial}
* @param bool|null $forceEager {@see https://api-platform.com/docs/core/performance/#force-eager}
* @param string|callable|null $provider {@see https://api-platform.com/docs/core/state-providers/#state-providers}
* @param string|callable|null $processor {@see https://api-platform.com/docs/core/state-processors/#state-processors}
* @param string|array|bool|null $mercure {@see https://api-platform.com/docs/core/mercure}
* @param string|bool|null $messenger {@see https://api-platform.com/docs/core/messenger/#dispatching-a-resource-through-the-message-bus}
* @param bool|null $elasticsearch {@see https://api-platform.com/docs/core/elasticsearch/}
* @param bool|null $read {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $deserialize {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $validate {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $write {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $serialize {@see https://api-platform.com/docs/core/events/#the-event-system}
* @param bool|null $fetchPartial {@see https://api-platform.com/docs/core/performance/#fetch-partial}
* @param bool|null $forceEager {@see https://api-platform.com/docs/core/performance/#force-eager}
* @param string|callable|null $provider {@see https://api-platform.com/docs/core/state-providers/#state-providers}
* @param string|callable|null $processor {@see https://api-platform.com/docs/core/state-processors/#state-processors}
* @param array<string, Parameter> $parameters
*/
public function __construct(
Expand Down
117 changes: 110 additions & 7 deletions src/Metadata/Parameter.php
@@ -1,16 +1,119 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Metadata;

use ApiPlatform\OpenApi;
use ApiPlatform\State\ProviderInterface;

abstract class Parameter
{
/**
* @param array<string, mixed> $extraProperties
* @param ProviderInterface|string|null $provider
* @param FilterInterface|string|null $filter
*/
public function __construct(
protected ?string $key = null,
protected ?\ArrayObject $schema = null,
protected ?OpenApi\Model\Parameter $openApi = null,
protected mixed $provider = null,
protected mixed $filter = null,
protected array $extraProperties = [],
) {
}

public function getKey(): ?string
{
return $this->key;
}

public function getSchema(): ?\ArrayObject
{
return $this->schema;
}

public function getOpenApi(): ?OpenApi\Model\Parameter
{
return $this->openApi;
}

public function getProvider(): mixed
{
return $this->provider;
}

public function getFilter(): mixed
{
return $this->filter;
}

public function getExtraProperties(): ?array
{
return $this->extraProperties;
}

public function withKey(?string $key): static
{
$self = clone $this;
$self->key = $key;

return $self;
}

/**
* @param \ArrayObject<string,mixed> $schema
*/
public function withSchema(\ArrayObject $schema): static
{
$self = clone $this;
$self->schema = $schema;

return $self;
}

public function withOpenApi(OpenApi\Model\Parameter $openApi): static
{
$self = clone $this;
$self->openApi = $openApi;

return $self;
}

public function withProvider(mixed $provider): static
{
$self = clone $this;
$self->provider = $provider;

return $self;
}

public function withFilter(mixed $filter): static
{
$self = clone $this;
$self->filter = $filter;

return $self;
}

final class Parameter {
public string $key;
public \ArrayObject $schema;
public array $context;
public ?OpenApi\Model\Parameter $openApi;
/**
* @param fn(mixed $value, Parameter $parameter, array $context)|string|null
* @param array<string, mixed> $extraProperties
*/
public mixed $provider;
public function withExtraProperties(array $extraProperties = []): static
{
$self = clone $this;
$self->extraProperties = $extraProperties;

return $self;
}
}
1 change: 1 addition & 0 deletions src/Metadata/Post.php
Expand Up @@ -171,6 +171,7 @@ class: $class,
provider: $provider,
processor: $processor,
stateOptions: $stateOptions,
parameters: $parameters,
extraProperties: $extraProperties
);
}
Expand Down
18 changes: 18 additions & 0 deletions src/Metadata/QueryParameter.php
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Metadata;

class QueryParameter extends Parameter implements QueryParameterInterface
{
}
18 changes: 18 additions & 0 deletions src/Metadata/QueryParameterInterface.php
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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\Metadata;

interface QueryParameterInterface
{
}

0 comments on commit c11e889

Please sign in to comment.