-
-
Notifications
You must be signed in to change notification settings - Fork 920
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
Conversation
|
||
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay WDYT about this:
- Move private methods to a trait
- Introduce a new class (
OperationDataProvider
that uses the trait) - 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)
5e70e35
to
c6218e5
Compare
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
|
||
final class OperationDataProvider |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
6045f20
to
6d5ff4b
Compare
@@ -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" /> |
There was a problem hiding this comment.
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.
6d5ff4b
to
74f1eb6
Compare
'api_platform.identifier.date_normalizer', | ||
'api_platform.identifier.uuid_normalizer', | ||
'api_platform.identifiers_extractor', | ||
'api_platform.identifiers_extractor.cached', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alphabetical order :D
74f1eb6
to
5fb07c7
Compare
636d989
to
a823bcf
Compare
@@ -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"> |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
?
There was a problem hiding this comment.
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 = []) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iterable|null
?
src/EventListener/ReadListener.php
Outdated
|
||
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) { |
There was a problem hiding this comment.
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).
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 |
aa0b43a
to
8d89a7f
Compare
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)! |
There was a problem hiding this 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!
8d89a7f
to
985f0c1
Compare
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:
getDataFromRouteParameters
method)