Skip to content

Allow subresource items in the iri converter #1877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 26, 2018

Conversation

soyuka
Copy link
Member

@soyuka soyuka commented Apr 19, 2018

Q A
Bug fix? depends
New feature? depends
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets n/a
License MIT
Doc PR

This enables subresources in the IRIConverter so that subresources can get denormalized as well.
I thought that it'd be better to use the ReadListener (avoid code duplication) instead of injecting another data provider. Let me know if you'd prefer the later (it's why I didn't added any tests yet).


Done:

  • Move the ReadListener code to a Trait (OperationDataProviderTrait)
  • Initialize a new OperationDataProvider class (uses the trait, used only in IriConverter, has a getDataFromRouteParameters method)
  • Refactor IriConverterTests (removed duplication code)
  • ReadListener uses the trait as well
  • Add behat subresource denormalizeRelation test


public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, ChainIdentifierDenormalizer $identifierDenormalizer = null)
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider = null, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, ChainIdentifierDenormalizer $identifierDenormalizer = null, ReadListener $readListener = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It introduces a dependency to a Symfony full stack related class in the low level infrastructure. I don't think it's a good idea, it will be very painful to support other frameworks (or PSR-7) in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay WDYT about this:

  1. Move private methods to a trait
  2. Introduce a new class (OperationDataProvider that uses the trait)
  3. Inject the OperationDataProvider in the IriConverter so that it can call item and subresource operations

This way:

  • no changes in read listener
  • de-duplicate code in IriConverter
  • improves future updates in the IriConverter concerning data retrieval (IriToItem)

@soyuka soyuka force-pushed the allow-subresource-iri branch from 5e70e35 to c6218e5 Compare April 20, 2018 09:35
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

final class OperationDataProvider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we introduce an interface?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may but I'm also to mark this as @internal unless we can find a global use for it? For now it'll be only used by the IriConverter and I really doubt it'll be used for anything else :p.
I just want to avoid injecting everything the OperationDataProviderTrait depends on in the IriConverter.

}

if (null === $data) {
throw new NotFoundHttpException('Not Found');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't throw a HTTP error here, but a domain exception.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally should be in ReadListener only

*
* @return array|\Traversable|null
*/
private function getCollectionData(Request $request, array $attributes, array $context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you think we can avoid to depend of the request, but extract the relevant data from the controller/event listener and pass it through the context? It will be easier to support PSR-7 this way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we can totally do this will be cleaner.

@soyuka soyuka force-pushed the allow-subresource-iri branch 2 times, most recently from 6045f20 to 6d5ff4b Compare April 20, 2018 14:56
@soyuka soyuka changed the base branch from master to 2.2 April 20, 2018 14:56
@soyuka soyuka changed the title Proposal: Allow subresource items in the iri converter WIP: Allow subresource items in the iri converter Apr 20, 2018
@@ -58,7 +58,7 @@
<argument type="service" id="api_platform.router" />
<argument type="service" id="api_platform.property_accessor" />
<argument type="service" id="api_platform.identifiers_extractor.cached" />
<argument type="service" id="api_platform.identifier.denormalizer" />
<argument type="service" id="api_platform.operation_data_provider" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not released yet == not BC break.

@soyuka soyuka force-pushed the allow-subresource-iri branch from 6d5ff4b to 74f1eb6 Compare April 20, 2018 15:23
'api_platform.identifier.date_normalizer',
'api_platform.identifier.uuid_normalizer',
'api_platform.identifiers_extractor',
'api_platform.identifiers_extractor.cached',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alphabetical order :D

@soyuka soyuka changed the title WIP: Allow subresource items in the iri converter Allow subresource items in the iri converter Apr 20, 2018
@soyuka soyuka force-pushed the allow-subresource-iri branch from 74f1eb6 to 5fb07c7 Compare April 20, 2018 15:30
@soyuka soyuka force-pushed the allow-subresource-iri branch 3 times, most recently from 636d989 to a823bcf Compare April 23, 2018 07:50
@@ -26,6 +26,13 @@
<argument type="tagged" tag="api_platform.subresource_data_provider" />
</service>
<service id="ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface" alias="api_platform.subresource_data_provider" />

<service id="api_platform.operation_data_provider" class="ApiPlatform\Core\DataProvider\OperationDataProvider">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public="false"?

}

if (null === $identifierDenormalizer) {
@trigger_error(sprintf('Not injecting "%s" is deprecated since API Platform 2.2 and will not be possible anymore in API Platform 3.', ChainIdentifierDenormalizer::class), E_USER_DEPRECATED);
if (null === $operationDataProvider) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will make very difficult to use API Platform without a framework or with another framework than Symfony. For instance, if I want to use API Platform's classes without a framework, and I don't bother to support subresources (I've a project like that), I'm stuck.

use ApiPlatform\Core\Util\RequestAttributesExtractor;

/**
* @internal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has it's a mandatory dependency of IriConverter, and there is no related interface, it cannot be @internal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please add some PHPDoc.

/**
* @internal
*/
class OperationDataProvider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just get rid of this class, it introduces unneeded complexity and indirection. Can we not include the trait directly in the data provider? It looks cleaner to me (and allows to keep subresources support optional).

$this->identifierDenormalizer = $identifierDenormalizer;
}

public function getDataFromRouteParameters(string $iri = null, array $parameters, array $context = [])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is $iri nullable?

return self::extractAttributesFromParameters($request->attributes->all());
}

public static function extractAttributesFromParameters(array $attributes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would introduce a new class, as it's not tight with the Request nor Symfony anymore.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please copy/adapt the PHPDoc.

use ApiPlatform\Core\Exception\InvalidIdentifierException;
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;

trait OperationDataProviderTrait
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@internal?

/**
* @var CollectionDataProviderInterface
*/
private $collectionDataProvider;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a blank line between props (btw, it's often better to have traits without props).

/**
* Retrieves data for a collection operation.
*
* @return array|\Traversable|null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iterable|null?


public function __construct(CollectionDataProviderInterface $collectionDataProvider, ItemDataProviderInterface $itemDataProvider, SubresourceDataProviderInterface $subresourceDataProvider = null, SerializerContextBuilderInterface $serializerContextBuilder = null, ChainIdentifierDenormalizer $identifierDenormalizer = null)
{
$this->collectionDataProvider = $collectionDataProvider;
$this->itemDataProvider = $itemDataProvider;
$this->subresourceDataProvider = $subresourceDataProvider;
$this->serializerContextBuilder = $serializerContextBuilder;
$this->identifierDenormalizer = $identifierDenormalizer;

if (null === $identifierDenormalizer) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, I would not throw a deprecation if it's not provided. If you don't inject it, you'll just loose support for complex identifiers (not a big deal in many projects).

@dunglas
Copy link
Member

dunglas commented Apr 25, 2018

It's a big change for a patch release. I would be more confortable to revert the previous fix and merge the new system in master. WDYT @soyuka?

@soyuka soyuka force-pushed the allow-subresource-iri branch 3 times, most recently from aa0b43a to 8d89a7f Compare April 25, 2018 14:52
@soyuka
Copy link
Member Author

soyuka commented Apr 25, 2018

It's a big change for a patch release. I would be more confortable to revert the previous fix and merge the new system in master. WDYT @soyuka?

I removed some deprecations and the added code will work without subresources and without identifier denormalizer. It's really your call though, I don't mind! Still, this may be a big improvement for those who want to use subresources in the denormalization process (closes a bunch of issues)!

Copy link
Member

@dunglas dunglas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, thanks for handling that!

@soyuka soyuka requested a review from meyerbaptiste April 25, 2018 16:18
@soyuka soyuka force-pushed the allow-subresource-iri branch from 8d89a7f to 985f0c1 Compare April 26, 2018 07:26
@soyuka soyuka merged commit 309e0ff into api-platform:2.2 Apr 26, 2018
@soyuka soyuka deleted the allow-subresource-iri branch April 26, 2018 15:04
@soyuka soyuka restored the allow-subresource-iri branch May 2, 2018 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants