Closed lichnow closed 3 years ago
Reorder your yaml array?
In fact I'm not sure that it'd work, adding iri: '/api/users/{id}
should work but I'm not sure {id}
will be interpolated. @teohhanhui may you look into this case?
@soyuka I think I have solved this problem,follow this code
<?php
namespace App\Component\ApiPlatform;
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\OperationType;
use ApiPlatform\Core\Api\UrlGeneratorInterface;
use ApiPlatform\Core\Bridge\Symfony\Routing\RouteNameResolverInterface;
use ApiPlatform\Core\DataProvider\OperationDataProviderTrait;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Util\ClassInfoTrait;
use App\Entity\User;
use Symfony\Component\Routing\Exception\ExceptionInterface as RoutingExceptionInterface;
use Symfony\Component\Routing\RouterInterface;
class IriConverterDecorator implements IriConverterInterface
{
use ClassInfoTrait;
use OperationDataProviderTrait;
private $decorated;
private $routeNameResolver;
private $router;
private $identifiersExtractor;
public function __construct(
IriConverterInterface $decorated,
RouteNameResolverInterface $routeNameResolver,
RouterInterface $router,
IdentifiersExtractorInterface $identifiersExtractor = null
)
{
$this->decorated = $decorated;
$this->routeNameResolver = $routeNameResolver;
$this->router = $router;
$this->identifiersExtractor = $identifiersExtractor;
}
public function getItemFromIri(string $iri, array $context = [])
{
return $this->decorated->getItemFromIri($iri, $context);
}
public function getIriFromItem($item, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
{
$resourceClass = $this->getObjectClass($item);
$routeName = $this->routeNameResolver->getRouteName($resourceClass, OperationType::ITEM);
if($resourceClass === User::class){
$routeName = 'api_users_get_item';
}
try {
$identifiers = $this->generateIdentifiersUrl($this->identifiersExtractor->getIdentifiersFromItem($item), $resourceClass);
return $this->router->generate($routeName, ['id' => implode(';', $identifiers)], $referenceType);
} catch (RuntimeException $e) {
throw new InvalidArgumentException(sprintf(
'Unable to generate an IRI for the item of type "%s"',
$resourceClass
), $e->getCode(), $e);
} catch (RoutingExceptionInterface $e) {
throw new InvalidArgumentException(sprintf(
'Unable to generate an IRI for the item of type "%s"',
$resourceClass
), $e->getCode(), $e);
}
}
private function generateIdentifiersUrl(array $identifiers, string $resourceClass): array
{
if (0 === \count($identifiers)) {
throw new InvalidArgumentException(sprintf(
'No identifiers defined for resource of type "%s"',
$resourceClass
));
}
if (1 === \count($identifiers)) {
return [rawurlencode((string) reset($identifiers))];
}
foreach ($identifiers as $name => $value) {
$identifiers[$name] = sprintf('%s=%s', $name, $value);
}
return array_values($identifiers);
}
public function getIriFromResourceClass(string $resourceClass, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
{
return $this->decorated->getItemIriFromResourceClass($resourceClass, $referenceType);
}
public function getItemIriFromResourceClass(string $resourceClass, array $identifiers, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
{
return $this->decorated->getItemIriFromResourceClass($resourceClass, $identifiers, $referenceType);
}
public function getSubresourceIriFromResourceClass(string $resourceClass, array $identifiers, int $referenceType = UrlGeneratorInterface::ABS_PATH): string
{
return $this->decorated->getSubresourceIriFromResourceClass($resourceClass, $identifiers, $referenceType);
}
}
and
api_platform.iri_converter_decorator:
class: App\Component\ApiPlatform\IriConverterDecorator
decorates: api_platform.iri_converter
arguments:
- '@api_platform.iri_converter_decorator.inner'
- '@api_platform.route_name_resolver'
- '@api_platform.router'
- '@api_platform.identifiers_extractor.cached'
Yes this is how I'd have fixed that as a last solution. I think that this is quite demanded, maybe we can improve the DX on the subject.
Greetings! We appreciate your concern but weren't able to reproduce this issue or it is more of a question. As described in the API Platform contributing guide, we use GitHub issues for bugs and feature requests only.
For support question ("How To", usage advice, or troubleshooting your own code), you have several options:
Feel free reach one of the support channels above. In the meantime we're closing this issue.
The easiest way how to achieve this is to left get as first item operation and add requirements for id:
itemOperations:
get:
access_control: 'is_granted("user.view",object)'
access_control_message: 'Sorry, you must login first.'
requirements:
id: '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$' # uuid
#id: '\d+' # integer
me:
method: 'GET'
path: '/users/me'
# controller: App\Entity\User\GetMe
access_control: 'is_granted("user.view-own")'
access_control_message: 'Sorry, only the user himself or super admin can view his own detail info.'
From document I see "By default, API Platform uses the first GET operation defined in itemOperations to generate the IRI of an item and the first GET operation defined in collectionOperations to generate the IRI of a collection.". But now I want to uses which operation that not is the first operation.like
and change
to
how to specify the IRI by any operation which I want?