Skip to content

Commit

Permalink
fix(laravel) graphQl Relationship loading (#6792)
Browse files Browse the repository at this point in the history
Co-authored-by: Antoine Bluchet <[email protected]>
Closes: #6791

fixes api-platform/api-platform#2795, #6791
  • Loading branch information
toitzi authored Nov 15, 2024
1 parent 8109906 commit dc8c09b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/Laravel/ApiPlatformProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,12 +432,12 @@ public function register(): void
$this->app->singleton(ItemProvider::class, function (Application $app) {
$tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));

return new ItemProvider(new LinksHandler($app), new ServiceLocator($tagged));
return new ItemProvider(new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), new ServiceLocator($tagged));
});
$this->app->singleton(CollectionProvider::class, function (Application $app) {
$tagged = iterator_to_array($app->tagged(LinksHandlerInterface::class));

return new CollectionProvider($app->make(Pagination::class), new LinksHandler($app), $app->tagged(QueryExtensionInterface::class), new ServiceLocator($tagged));
return new CollectionProvider($app->make(Pagination::class), new LinksHandler($app, $app->make(ResourceMetadataCollectionFactoryInterface::class)), $app->tagged(QueryExtensionInterface::class), new ServiceLocator($tagged));
});
$this->app->tag([ItemProvider::class, CollectionProvider::class], ProviderInterface::class);

Expand Down
73 changes: 62 additions & 11 deletions src/Laravel/Eloquent/State/LinksHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@

namespace ApiPlatform\Laravel\Eloquent\State;

use ApiPlatform\Metadata\Exception\OperationNotFoundException;
use ApiPlatform\Metadata\GraphQl\Operation;
use ApiPlatform\Metadata\GraphQl\Query;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -25,6 +30,7 @@ final class LinksHandler implements LinksHandlerInterface
{
public function __construct(
private readonly Application $application,
private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory,
) {
}

Expand All @@ -34,27 +40,72 @@ public function handleLinks(Builder $builder, array $uriVariables, array $contex

if ($operation instanceof HttpOperation) {
foreach (array_reverse($operation->getUriVariables() ?? []) as $uriVariable => $link) {
$identifier = $uriVariables[$uriVariable];
$builder = $this->buildQuery($builder, $link, $uriVariables[$uriVariable]);
}

if ($to = $link->getToProperty()) {
$builder = $builder->where($builder->getModel()->{$to}()->getQualifiedForeignKeyName(), $identifier);
return $builder;
}

continue;
}
if (!($linkClass = $context['linkClass'] ?? false)) {
return $builder;
}

if ($from = $link->getFromProperty()) {
$relation = $this->application->make($link->getFromClass());
$builder = $builder->getModel()->where($relation->{$from}()->getQualifiedForeignKeyName(), $identifier);
$newLink = null;
$linkedOperation = null;
$linkProperty = $context['linkProperty'] ?? null;

continue;
try {
$resourceMetadataCollection = $this->resourceMetadataCollectionFactory->create($linkClass);
$linkedOperation = $resourceMetadataCollection->getOperation($operation->getName());
} catch (OperationNotFoundException) {
// Instead, we'll look for the first Query available.
foreach ($resourceMetadataCollection as $resourceMetadata) {
foreach ($resourceMetadata->getGraphQlOperations() as $op) {
if ($op instanceof Query) {
$linkedOperation = $op;
}
}
}
}

if (!$linkedOperation instanceof Operation) {
return $builder;
}

$builder->where($builder->getModel()->qualifyColumn($link->getIdentifiers()[0]), $identifier);
$resourceClass = $builder->getModel()::class;
foreach ($linkedOperation->getLinks() ?? [] as $link) {
if ($resourceClass === $link->getToClass() && $linkProperty === $link->getFromProperty()) {
$newLink = $link;
break;
}
}

if (!$newLink) {
return $builder;
}

return $builder;
return $this->buildQuery($builder, $newLink, $uriVariables[$newLink->getIdentifiers()[0]]);
}

/**
* @param Builder<Model> $builder
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*
* @return Builder<Model> $builder
*/
private function buildQuery(Builder $builder, Link $link, mixed $identifier): Builder
{
if ($to = $link->getToProperty()) {
return $builder->where($builder->getModel()->{$to}()->getQualifiedForeignKeyName(), $identifier);
}

if ($from = $link->getFromProperty()) {
$relation = $this->application->make($link->getFromClass());

return $builder->getModel()->where($relation->{$from}()->getQualifiedForeignKeyName(), $identifier);
}

return $builder->where($builder->getModel()->qualifyColumn($link->getIdentifiers()[0]), $identifier);
}
}

0 comments on commit dc8c09b

Please sign in to comment.