Skip to content

Commit c2347ec

Browse files
committed
Oh yeah
1 parent 972e12d commit c2347ec

File tree

7 files changed

+112
-153
lines changed

7 files changed

+112
-153
lines changed

features/main/subresource.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ Feature: Subresource support
301301
}
302302
"""
303303

304+
@createSchema
304305
Scenario: Get offers subresource from aggregate offers subresource
305306
Given I have a product with offers
306307
When I send a "GET" request to "/dummy_products/2/offers/1/offers"

src/Bridge/Doctrine/Orm/State/ItemProvider.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public function provide(string $resourceClass, array $identifiers = [], ?string
6868

6969
$this->handleLinks($queryBuilder, $identifiers, $queryNameGenerator, $context, $resourceClass, $operationName);
7070

71-
dd($queryBuilder->getDQL());
7271
foreach ($this->itemExtensions as $extension) {
7372
$extension->applyToItem($queryBuilder, $queryNameGenerator, $resourceClass, $identifiers, $operationName, $context);
7473

src/Bridge/Doctrine/Orm/State/LinksHandlerTrait.php

Lines changed: 91 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGenerator;
1717
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
1818
use ApiPlatform\Metadata\Link;
19+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\DummyProduct;
20+
use Doctrine\ORM\Mapping\ClassMetadataInfo;
1921
use Doctrine\ORM\QueryBuilder;
2022
use Doctrine\Persistence\Mapping\ClassMetadata;
2123

@@ -30,113 +32,110 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
3032

3133
$links = $operation instanceof GraphQlOperation ? $operation->getLinks() : $operation->getUriVariables();
3234

33-
if ($linkClass = $context['linkClass'] ?? false) {
34-
foreach ($links as $link) {
35-
if ($linkClass === $link->getTargetClass()) {
36-
foreach ($identifiers as $identifier => $value) {
37-
$this->applyLink($queryBuilder, $queryNameGenerator, $doctrineClassMetadata, $alias, $link, $identifier, $value);
38-
}
39-
40-
return;
41-
}
42-
}
43-
}
44-
45-
if (null === $links) {
35+
// if ($linkClass = $context['linkClass'] ?? false) {
36+
// foreach ($links as $link) {
37+
// if ($linkClass === $link->getTargetClass()) {
38+
// foreach ($identifiers as $identifier => $value) {
39+
// $this->applyLink($queryBuilder, $queryNameGenerator, $doctrineClassMetadata, $alias, $link, $identifier, $value);
40+
// }
41+
//
42+
// return;
43+
// }
44+
// }
45+
// }
46+
47+
if (!$links) {
4648
return;
4749
}
4850

49-
dd($links)
50-
dump($operation->getUriTemplate());
51-
$aliases = [];
52-
$previousContext = [];
53-
$conditions = [];
54-
foreach ($links as $parameterName => $link) {
55-
$identifier = $link->getIdentifiers()[0];
56-
if (!isset($aliases[$link->getTargetClass()])) {
57-
$aliases[$link->getTargetClass()] = $link->getTargetClass() === $operation->getClass() ? $alias : $queryNameGenerator->generateJoinAlias($alias);
58-
}
51+
$previousAlias = $alias;
52+
$previousIdentifier = end($links)->getIdentifiers()[0] ?? 'id';
53+
$expressions = [];
54+
$i = 0;
5955

60-
$currentAlias = $aliases[$link->getTargetClass()];
61-
$placeholder = $queryNameGenerator->generateParameterName($parameterName);
62-
// inverse == prop de la targetClass
63-
if (!$link->getInverseProperty() && !$link->getProperty() && !$link->getExpandedValue()) {
64-
$conditions[] = $aliases[$link->getTargetClass()] . ".$identifier = :$placeholder";
65-
$previousContext = ['alias' => $currentAlias, 'identifier' => $identifier];
56+
foreach (array_reverse($links) as $parameterName => $link) {
57+
if ($link->getExpandedValue() || !$link->getFromClass()) {
58+
++$i;
6659
continue;
6760
}
6861

62+
$identifierProperty = $link->getIdentifiers()[0] ?? 'id';
63+
$currentAlias = $i === 0 ? $alias : $queryNameGenerator->generateJoinAlias($alias);
64+
$placeholder = $queryNameGenerator->generateParameterName($parameterName);
65+
66+
if (!$link->getFromProperty() && !$link->getToProperty()) {
67+
$doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass());
6968

70-
if ($inverseProperty = $link->getInverseProperty()) {
71-
$joinAlias = $queryNameGenerator->generateJoinAlias($alias);
72-
$inverseAlias = $queryNameGenerator->generateJoinAlias($alias);
73-
$conditions[] = "{$previousContext['alias']}.{$previousContext['identifier']} IN (SELECT $inverseAlias.$identifier FROM {$link->getTargetClass()} $joinAlias JOIN $joinAlias.$inverseProperty $inverseAlias WHERE $joinAlias.$identifier = $placeholder)";
74-
$previousContext = ['alias' => $currentAlias, 'identifier' => $identifier];
69+
$queryBuilder->andWhere("{$currentAlias}.$identifierProperty = :$placeholder");
70+
$queryBuilder->setParameter($placeholder, $identifiers[$parameterName], $doctrineClassMetadata->getTypeOfField($identifierProperty));
71+
$previousAlias = $currentAlias;
72+
$previousIdentifier = $identifierProperty;
73+
++$i;
7574
continue;
7675
}
7776

78-
dump($conditions);
79-
dd($link);
80-
// dd($link);
81-
// if ($link->getExpandedValue()) {
82-
// dd($queryBuilder->getDQL());
83-
// dd('test');
84-
// continue;
85-
// }
77+
if ($link->getFromProperty()) {
78+
$doctrineClassMetadata = $manager->getClassMetadata($link->getFromClass());
79+
$joinAlias = $queryNameGenerator->generateJoinAlias('m');
80+
$assocationMapping = $doctrineClassMetadata->getAssociationMappings()[$link->getFromProperty()];
81+
$relationType = $assocationMapping['type'];
8682

87-
// $this->applyLink($queryBuilder, $queryNameGenerator, $doctrineClassMetadata, $alias, $link, $identifiers[$parameterName] ?? null);
88-
}
89-
}
83+
if ($relationType & ClassMetadataInfo::TO_MANY) {
84+
$nextAlias = $queryNameGenerator->generateJoinAlias($alias);
85+
86+
$expressions["$previousAlias.$previousIdentifier"] = "SELECT $joinAlias.{$previousIdentifier} FROM {$link->getFromClass()} $nextAlias INNER JOIN $nextAlias.{$link->getFromProperty()} $joinAlias WHERE $nextAlias.{$identifierProperty} = :$placeholder";
87+
88+
$queryBuilder->setParameter($placeholder, $identifiers[$parameterName], $doctrineClassMetadata->getTypeOfField($identifierProperty));
89+
$previousAlias = $nextAlias;
90+
++$i;
91+
continue;
92+
}
93+
94+
95+
// A single-valued association path expression to an inverse side is not supported in DQL queries.
96+
if ($relationType & ClassMetadataInfo::TO_ONE && !$assocationMapping['isOwningSide']) {
97+
$queryBuilder->innerJoin("$previousAlias.".$assocationMapping['mappedBy'], $joinAlias);
98+
} else {
99+
$queryBuilder->join(
100+
$link->getFromClass(),
101+
$joinAlias,
102+
'with',
103+
"{$previousAlias}.{$previousIdentifier} = $joinAlias.{$link->getFromProperty()}"
104+
);
105+
}
106+
107+
$queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
108+
$queryBuilder->setParameter($placeholder, $identifiers[$parameterName], $doctrineClassMetadata->getTypeOfField($identifierProperty));
109+
$previousAlias = $joinAlias;
110+
$previousIdentifier = $identifierProperty;
111+
++$i;
112+
continue;
113+
}
90114

91-
private function applyLink(QueryBuilder $queryBuilder, QueryNameGenerator $queryNameGenerator, ClassMetadata $doctrineClassMetadata, string $alias, Link $link, $value = null)
92-
{
93-
$propertyIdentifier = $link->getIdentifiers()[0];
94-
$placeholder = ':' . $queryNameGenerator->generateParameterName($propertyIdentifier);
95-
if ($inverseProperty = $link->getInverseProperty()) {
96115
$joinAlias = $queryNameGenerator->generateJoinAlias($alias);
97-
$inverseAlias = $queryNameGenerator->generateJoinAlias($alias);
98-
99-
// SELECT o FROM ApiPlatform\Tests\Fixtures\TestBundle\Entity\ThirdLevel o
100-
// WHERE o.id IN (
101-
// SELECT o_a3.id FROM
102-
// ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy o_a1
103-
// JOIN o_a1.relatedDummies o_a2
104-
// JOIN o_a2.thirdLevel o_a3
105-
// WHERE o_a1.id = :id_id
106-
// ) AND o.id = :id_id
107-
108-
// $queryBuilder->join(
109-
// $link->getTargetClass(),
110-
// $joinAlias,
111-
// 'with',
112-
// "$alias.$propertyIdentifier = $joinAlias.$inverseProperty"
113-
// );
114-
115-
$expression = "$alias.$propertyIdentifier IN (SELECT $inverseAlias.$propertyIdentifier FROM {$link->getTargetClass()} $joinAlias JOIN $joinAlias.$inverseProperty $inverseAlias WHERE $joinAlias.$propertyIdentifier = $placeholder)";
116-
117-
// $expression = $queryBuilder->expr()->eq(
118-
// "{$joinAlias}.{$propertyIdentifier}",
119-
// $placeholder
120-
// );
121-
} elseif ($property = $link->getProperty()) {
122-
$joinAlias = $queryNameGenerator->generateJoinAlias($property);
123-
124-
$queryBuilder->join(
125-
"$alias.$property",
126-
$joinAlias,
127-
);
128-
129-
$expression = $queryBuilder->expr()->eq(
130-
"{$joinAlias}.{$propertyIdentifier}",
131-
$placeholder
132-
);
133-
} else {
134-
$expression = $queryBuilder->expr()->eq(
135-
"{$alias}.{$propertyIdentifier}", $placeholder
136-
);
116+
$queryBuilder->join("{$previousAlias}.{$link->getToProperty()}", $joinAlias);
117+
$queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
118+
$queryBuilder->setParameter($placeholder, $identifiers[$parameterName], $doctrineClassMetadata->getTypeOfField($identifierProperty));
119+
$previousAlias = $joinAlias;
120+
$previousIdentifier = $identifierProperty;
121+
++$i;
137122
}
138123

139-
$queryBuilder->andWhere($expression);
140-
$queryBuilder->setParameter($placeholder, $value, $doctrineClassMetadata->getTypeOfField($propertyIdentifier));
124+
if ($expressions) {
125+
$i = 0;
126+
$clause = '';
127+
foreach ($expressions as $alias => $expression) {
128+
if ($i === 0) {
129+
$clause .= "$alias IN (" . $expression;
130+
$i++;
131+
continue;
132+
}
133+
134+
$clause .= " AND $alias IN (" . $expression;
135+
$i++;
136+
}
137+
138+
$queryBuilder->andWhere($clause . str_repeat(')', $i));
139+
}
141140
}
142141
}

src/Core/Bridge/Rector/Parser/TransformApiSubresourceVisitor.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ public function enterNode(Node $node)
7070
new Node\Expr\ArrayItem(
7171
new Node\Expr\ClassConstFetch(
7272
new Node\Name(
73-
($resource['target_class'] === $this->subresourceMetadata['resource_class']) ? 'self' : '\\'.$resource['target_class']
73+
($resource['from_class'] === $this->subresourceMetadata['resource_class']) ? 'self' : '\\'.$resource['from_class']
7474
),
7575
'class'
7676
),
77-
new Node\Scalar\String_('target_class')
77+
new Node\Scalar\String_('from_class')
7878
),
7979
new Node\Expr\ArrayItem(
8080
new Node\Expr\Array_(
@@ -93,10 +93,10 @@ public function enterNode(Node $node)
9393
);
9494
}
9595

96-
if (isset($resource['property']) || isset($resource['inverse_property'])) {
96+
if (isset($resource['from_property']) || isset($resource['to_property'])) {
9797
$identifierNodes[] = new Node\Expr\ArrayItem(
98-
new Node\Scalar\String_($resource['property'] ?? $resource['inverse_property']),
99-
new Node\Scalar\String_(isset($resource['property']) ? 'property' : 'inverse_property')
98+
new Node\Scalar\String_($resource['to_property'] ?? $resource['from_property']),
99+
new Node\Scalar\String_(isset($resource['to_property']) ? 'to_property' : 'from_property')
100100
);
101101
}
102102

src/Core/Bridge/Rector/Service/SubresourceTransformer.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public function toUriVariables(array $subresourceMetadata): array
4545
$fromClassMetadataAssociationMappings = $fromClassMetadata->getAssociationMappings();
4646

4747
$uriVariables[$identifier] = [
48-
'target_class' => $fromClass,
4948
'from_class' => $fromClass,
5049
'inverse_property' => null,
5150
'from_property' => null,

src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,13 @@ private function normalizeUriVariables($operation)
175175
$normalizedUriVariable = (new Link())->withIdentifiers([$normalizedUriVariable])->withFromClass($resourceClass);
176176
}
177177
if (\is_array($normalizedUriVariable)) {
178-
if (!isset($normalizedUriVariable['target_class'])) {
178+
if (!isset($normalizedUriVariable['from_class']) && !isset($normalizedUriVariable['expanded_value'])) {
179179
if (2 !== \count($normalizedUriVariable)) {
180180
throw new \LogicException("The uriVariables shortcut syntax needs to be the tuple: 'uriVariable' => [fromClass, fromProperty]");
181181
}
182182
$normalizedUriVariable = (new Link())->withIdentifiers([$normalizedUriVariable[1]])->withFromClass($normalizedUriVariable[0]);
183183
} else {
184-
$normalizedUriVariable = new Link(null, $normalizedUriVariable['from_property'] ?? null, $normalizedUriVariable['to_property'] ?? null, $normalizedUriVariable['class'], $normalizedUriVariable['to_class'] ?? null, $normalizedUriVariable['identifiers'] ?? null, $normalizedUriVariable['composite_identifier'] ?? null, $normalizedUriVariable['expanded_value'] ?? null);
184+
$normalizedUriVariable = new Link($normalizedParameterName, $normalizedUriVariable['from_property'] ?? null, $normalizedUriVariable['to_property'] ?? null, $normalizedUriVariable['from_class'] ?? null, $normalizedUriVariable['to_class'] ?? null, $normalizedUriVariable['identifiers'] ?? null, $normalizedUriVariable['composite_identifier'] ?? null, $normalizedUriVariable['expanded_value'] ?? null);
185185
}
186186
}
187187
if (null !== ($hasCompositeIdentifier = $operation->getCompositeIdentifier())) {

0 commit comments

Comments
 (0)