16
16
use ApiPlatform \Core \Bridge \Doctrine \Orm \Util \QueryNameGenerator ;
17
17
use ApiPlatform \Exception \RuntimeException ;
18
18
use ApiPlatform \Metadata \GraphQl \Operation as GraphQlOperation ;
19
- use ApiPlatform \ Metadata \ Link ;
19
+ use Doctrine \ ORM \ Mapping \ ClassMetadataInfo ;
20
20
use Doctrine \ORM \QueryBuilder ;
21
- use Doctrine \Persistence \Mapping \ClassMetadata ;
22
21
23
22
trait LinksHandlerTrait
24
23
{
@@ -31,81 +30,117 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que
31
30
32
31
$ links = $ operation instanceof GraphQlOperation ? $ operation ->getLinks () : $ operation ->getUriVariables ();
33
32
34
- if ($ linkClass = $ context ['linkClass ' ] ?? false ) {
35
- foreach ($ links as $ link ) {
36
- if ($ linkClass === $ link ->getFromClass ()) {
37
- foreach ($ identifiers as $ identifier => $ value ) {
38
- $ this ->applyLink ($ queryBuilder , $ queryNameGenerator , $ doctrineClassMetadata , $ alias , $ link , $ identifier , $ value );
39
- }
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
+ // }
40
44
41
- return ;
42
- }
45
+ if (!$ links ) {
46
+ return ;
47
+ }
48
+
49
+ $ previousAlias = $ alias ;
50
+ $ previousIdentifiers = end ($ links )->getIdentifiers ();
51
+ $ expressions = [];
52
+ $ identifiers = array_reverse ($ identifiers );
53
+
54
+ foreach (array_reverse ($ links ) as $ parameterName => $ link ) {
55
+ if ($ link ->getExpandedValue () || !$ link ->getFromClass ()) {
56
+ continue ;
43
57
}
44
58
45
- $ operation = $ this ->resourceMetadataCollectionFactory ->create ($ linkClass )->getOperation ($ operationName );
46
- $ links = $ operation instanceof GraphQlOperation ? $ operation ->getLinks () : $ operation ->getUriVariables ();
47
- foreach ($ links as $ link ) {
48
- if ($ resourceClass === $ link ->getFromClass ()) {
49
- $ link = $ link ->withFromProperty ($ link ->getToProperty ())->withFromClass ($ linkClass );
50
- foreach ($ identifiers as $ identifier => $ value ) {
51
- $ this ->applyLink ($ queryBuilder , $ queryNameGenerator , $ doctrineClassMetadata , $ alias , $ link , $ identifier , $ value );
52
- }
59
+ $ identifierProperties = $ link ->getIdentifiers ();
60
+ $ currentAlias = $ queryNameGenerator ->generateJoinAlias ($ alias );
53
61
54
- return ;
62
+ if ($ link ->getFromClass () === $ resourceClass ) {
63
+ $ currentAlias = $ alias ;
64
+ }
65
+
66
+ if (!$ link ->getFromProperty () && !$ link ->getToProperty ()) {
67
+ $ doctrineClassMetadata = $ manager ->getClassMetadata ($ link ->getFromClass ());
68
+
69
+ foreach ($ identifierProperties as $ identifierProperty ) {
70
+ $ placeholder = $ queryNameGenerator ->generateParameterName ($ identifierProperty );
71
+ $ queryBuilder ->andWhere ("{$ currentAlias }. $ identifierProperty = : $ placeholder " );
72
+ $ queryBuilder ->setParameter ($ placeholder , array_shift ($ identifiers ), $ doctrineClassMetadata ->getTypeOfField ($ identifierProperty ));
55
73
}
74
+
75
+ $ previousAlias = $ currentAlias ;
76
+ $ previousIdentifiers = $ identifierProperties ;
77
+ continue ;
56
78
}
57
79
58
- throw new RuntimeException (sprintf ('The class "%s" cannot be retrieved from "%s". ' , $ resourceClass , $ linkClass ));
59
- }
80
+ if (1 < \count ($ previousIdentifiers ) || 1 < \count ($ identifierProperties )) {
81
+ throw new RuntimeException ('Composite identifiers on a relation can not be handled automatically, implement your own query. ' );
82
+ }
60
83
61
- if (!$ links ) {
62
- return ;
63
- }
84
+ $ previousIdentifier = $ previousIdentifiers [0 ];
85
+ $ identifierProperty = $ identifierProperties [0 ];
86
+ $ placeholder = $ queryNameGenerator ->generateParameterName ($ identifierProperty );
87
+
88
+ if ($ link ->getFromProperty ()) {
89
+ $ doctrineClassMetadata = $ manager ->getClassMetadata ($ link ->getFromClass ());
90
+ $ joinAlias = $ queryNameGenerator ->generateJoinAlias ('m ' );
91
+ $ assocationMapping = $ doctrineClassMetadata ->getAssociationMappings ()[$ link ->getFromProperty ()];
92
+ $ relationType = $ assocationMapping ['type ' ];
93
+
94
+ if ($ relationType & ClassMetadataInfo::TO_MANY ) {
95
+ $ nextAlias = $ queryNameGenerator ->generateJoinAlias ($ alias );
96
+ $ expressions ["$ previousAlias. $ previousIdentifier " ] = "SELECT $ joinAlias. {$ previousIdentifier } FROM {$ link ->getFromClass ()} $ nextAlias INNER JOIN $ nextAlias. {$ link ->getFromProperty ()} $ joinAlias WHERE $ nextAlias. {$ identifierProperty } = : $ placeholder " ;
97
+ $ queryBuilder ->setParameter ($ placeholder , array_shift ($ identifiers ), $ doctrineClassMetadata ->getTypeOfField ($ identifierProperty ));
98
+ $ previousAlias = $ nextAlias ;
99
+ continue ;
100
+ }
64
101
65
- foreach ($ identifiers as $ identifier => $ value ) {
66
- $ link = $ links [$ identifier ] ?? $ links ['id ' ];
102
+ // A single-valued association path expression to an inverse side is not supported in DQL queries.
103
+ if ($ relationType & ClassMetadataInfo::TO_ONE && !$ assocationMapping ['isOwningSide ' ]) {
104
+ $ queryBuilder ->innerJoin ("$ previousAlias. " .$ assocationMapping ['mappedBy ' ], $ joinAlias );
105
+ } else {
106
+ $ queryBuilder ->join (
107
+ $ link ->getFromClass (),
108
+ $ joinAlias ,
109
+ 'with ' ,
110
+ "{$ previousAlias }. {$ previousIdentifier } = $ joinAlias. {$ link ->getFromProperty ()}"
111
+ );
112
+ }
113
+
114
+ $ queryBuilder ->andWhere ("$ joinAlias. $ identifierProperty = : $ placeholder " );
115
+ $ queryBuilder ->setParameter ($ placeholder , array_shift ($ identifiers ), $ doctrineClassMetadata ->getTypeOfField ($ identifierProperty ));
116
+ $ previousAlias = $ joinAlias ;
117
+ $ previousIdentifier = $ identifierProperty ;
118
+ continue ;
119
+ }
67
120
68
- $ this ->applyLink ($ queryBuilder , $ queryNameGenerator , $ doctrineClassMetadata , $ alias , $ link , $ identifier , $ value );
121
+ $ joinAlias = $ queryNameGenerator ->generateJoinAlias ($ alias );
122
+ $ queryBuilder ->join ("{$ previousAlias }. {$ link ->getToProperty ()}" , $ joinAlias );
123
+ $ queryBuilder ->andWhere ("$ joinAlias. $ identifierProperty = : $ placeholder " );
124
+ $ queryBuilder ->setParameter ($ placeholder , array_shift ($ identifiers ), $ doctrineClassMetadata ->getTypeOfField ($ identifierProperty ));
125
+ $ previousAlias = $ joinAlias ;
126
+ $ previousIdentifier = $ identifierProperty ;
69
127
}
70
- }
71
128
72
- private function applyLink (QueryBuilder $ queryBuilder , QueryNameGenerator $ queryNameGenerator , ClassMetadata $ doctrineClassMetadata , string $ alias , Link $ link , string $ identifier , $ value )
73
- {
74
- $ placeholder = ':id_ ' .$ identifier ;
75
- if ($ fromProperty = $ link ->getFromProperty ()) {
76
- $ propertyIdentifier = $ link ->getIdentifiers ()[0 ];
77
- $ joinAlias = $ queryNameGenerator ->generateJoinAlias ($ fromProperty );
78
-
79
- $ queryBuilder ->join (
80
- $ link ->getFromClass (),
81
- $ joinAlias ,
82
- 'with ' ,
83
- "$ alias. $ propertyIdentifier = $ joinAlias. $ fromProperty "
84
- );
85
-
86
- $ expression = $ queryBuilder ->expr ()->eq (
87
- "{$ joinAlias }. {$ propertyIdentifier }" ,
88
- $ placeholder
89
- );
90
- } elseif ($ property = $ link ->getToProperty ()) {
91
- $ propertyIdentifier = $ link ->getIdentifiers ()[0 ];
92
- $ joinAlias = $ queryNameGenerator ->generateJoinAlias ($ property );
93
-
94
- $ queryBuilder ->join (
95
- "$ alias. $ property " ,
96
- $ joinAlias ,
97
- );
98
-
99
- $ expression = $ queryBuilder ->expr ()->eq (
100
- "{$ joinAlias }. {$ propertyIdentifier }" ,
101
- $ placeholder
102
- );
103
- } else {
104
- $ expression = $ queryBuilder ->expr ()->eq (
105
- "{$ alias }. {$ identifier }" , $ placeholder
106
- );
129
+ if ($ expressions ) {
130
+ $ i = 0 ;
131
+ $ clause = '' ;
132
+ foreach ($ expressions as $ alias => $ expression ) {
133
+ if (0 === $ i ) {
134
+ $ clause .= "$ alias IN ( " .$ expression ;
135
+ ++$ i ;
136
+ continue ;
137
+ }
138
+
139
+ $ clause .= " AND $ alias IN ( " .$ expression ;
140
+ ++$ i ;
141
+ }
142
+
143
+ $ queryBuilder ->andWhere ($ clause .str_repeat (') ' , $ i ));
107
144
}
108
- $ queryBuilder ->andWhere ($ expression );
109
- $ queryBuilder ->setParameter ($ placeholder , $ value , $ doctrineClassMetadata ->getTypeOfField ($ identifier ));
110
145
}
111
146
}
0 commit comments