1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace AlgoWeb\PODataLaravel\Query; |
4
|
|
|
|
5
|
|
|
use POData\Providers\Metadata\ResourceProperty; |
6
|
|
|
use POData\Providers\Metadata\ResourceSet; |
7
|
|
|
use POData\UriProcessor\QueryProcessor\Expression\Parser\IExpressionProvider; |
8
|
|
|
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo; |
9
|
|
|
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor; |
10
|
|
|
use POData\Providers\Query\IQueryProvider; |
11
|
|
|
use POData\Providers\Expression\MySQLExpressionProvider; |
12
|
|
|
use POData\Providers\Query\QueryType; |
13
|
|
|
use POData\Providers\Query\QueryResult; |
14
|
|
|
use POData\Providers\Expression\PHPExpressionProvider; |
15
|
|
|
use Illuminate\Support\Facades\DB; |
16
|
|
|
|
17
|
|
|
class LaravelQuery implements IQueryProvider |
18
|
|
|
{ |
19
|
|
|
protected $expression; |
20
|
|
|
public $queryProviderClassName; |
21
|
|
|
|
22
|
2 |
|
public function __construct() |
23
|
|
|
{ |
24
|
|
|
/* MySQLExpressionProvider();*/ |
25
|
2 |
|
$table = DB::table('dummy'); |
26
|
2 |
|
$this->expression = new LaravelExpressionProvider($table); //PHPExpressionProvider('expression'); |
|
|
|
|
27
|
2 |
|
$this->queryProviderClassName = get_class($this); |
28
|
2 |
|
} |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Indicates if the QueryProvider can handle ordered paging, this means respecting order, skip, and top parameters |
32
|
|
|
* If the query provider can not handle ordered paging, it must return the entire result set and POData will |
33
|
|
|
* perform the ordering and paging |
34
|
|
|
* |
35
|
|
|
* @return Boolean True if the query provider can handle ordered paging, false if POData should perform the paging |
36
|
|
|
*/ |
37
|
|
|
public function handlesOrderedPaging() |
38
|
|
|
{ |
39
|
|
|
return true; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Gets the expression provider used by to compile OData expressions into expression used by this query provider. |
44
|
|
|
* |
45
|
|
|
* @return \POData\Providers\Expression\IExpressionProvider |
46
|
|
|
*/ |
47
|
|
|
public function getExpressionProvider() |
48
|
|
|
{ |
49
|
|
|
return $this->expression; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Gets collection of entities belongs to an entity set |
54
|
|
|
* IE: http://host/EntitySet |
55
|
|
|
* http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value |
56
|
|
|
* |
57
|
|
|
* @param QueryType $queryType indicates if this is a query for a count, entities, or entities with a count |
58
|
|
|
* @param ResourceSet $resourceSet The entity set containing the entities to fetch |
59
|
|
|
* @param FilterInfo $filterInfo represents the $filter parameter of the OData query. NULL if no $filter specified |
60
|
|
|
* @param mixed $orderBy sorted order if we want to get the data in some specific order |
61
|
|
|
* @param int $top number of records which need to be skip |
62
|
|
|
* @param String $skipToken value indicating what records to skip |
63
|
|
|
* |
64
|
|
|
* @return QueryResult |
65
|
|
|
*/ |
66
|
3 |
|
public function getResourceSet( |
67
|
|
|
QueryType $queryType, |
68
|
|
|
ResourceSet $resourceSet, |
69
|
|
|
$filterInfo = null, |
70
|
|
|
$orderBy = null, |
71
|
|
|
$top = null, |
72
|
|
|
$skipToken = null, |
73
|
|
|
$sourceEntityInstance = null |
74
|
|
|
) { |
75
|
3 |
|
if ($resourceSet == null && $sourceEntityInstance == null) { |
76
|
|
|
throw new \Exception('Must supply at least one of a resource set and source entity'); |
77
|
|
|
} |
78
|
3 |
|
if ($sourceEntityInstance == null) { |
79
|
1 |
|
$sourceEntityInstance = $this->getSourceEntityInstance($resourceSet); |
80
|
1 |
|
} |
81
|
|
|
|
82
|
3 |
|
$result = new QueryResult(); |
83
|
3 |
|
$result->results = null; |
84
|
3 |
|
$result->count = null; |
85
|
|
|
|
86
|
3 |
|
if (isset($orderBy) && null != $orderBy) { |
87
|
|
|
foreach ($orderBy->getOrderByInfo()->getOrderByPathSegments() as $order) { |
88
|
|
|
foreach ($order->getSubPathSegments() as $subOrder) { |
89
|
|
|
$sourceEntityInstance = $sourceEntityInstance->orderBy( |
90
|
|
|
$subOrder->getName(), |
91
|
|
|
$order->isAscending() ? 'asc' : 'desc' |
92
|
|
|
); |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
} |
96
|
3 |
|
if (isset($skipToken)) { |
97
|
1 |
|
$sourceEntityInstance = $sourceEntityInstance->skip($skipToken); |
98
|
1 |
|
} |
99
|
3 |
|
if (isset($top)) { |
100
|
1 |
|
$sourceEntityInstance = $sourceEntityInstance->take($top); |
101
|
1 |
|
} |
102
|
|
|
|
103
|
3 |
|
$resultSet = $sourceEntityInstance->get(); |
104
|
|
|
|
105
|
3 |
|
if (isset($filterInfo)) { |
106
|
|
|
$method = "return ".$filterInfo->getExpressionAsString().";"; |
107
|
|
|
$clln = "$".$resourceSet->getResourceType()->getName(); |
108
|
|
|
$isvalid = create_function($clln, $method); |
|
|
|
|
109
|
|
|
$resultSet = $resultSet->filter($isvalid); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
|
113
|
3 |
|
if (QueryType::ENTITIES() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) { |
114
|
1 |
|
$result->results = array(); |
115
|
1 |
|
foreach ($resultSet as $res) { |
116
|
1 |
|
$result->results[] = $res; |
117
|
1 |
|
} |
118
|
1 |
|
} |
119
|
3 |
|
if (QueryType::COUNT() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) { |
120
|
3 |
|
if (is_array($resultSet)) { |
121
|
|
|
$resultSet = collect($resultSet); |
122
|
|
|
} |
123
|
3 |
|
$result->count = $resultSet->count(); |
124
|
3 |
|
} |
125
|
3 |
|
return $result; |
126
|
|
|
} |
127
|
|
|
/** |
128
|
|
|
* Gets an entity instance from an entity set identified by a key |
129
|
|
|
* IE: http://host/EntitySet(1L) |
130
|
|
|
* http://host/EntitySet(KeyA=2L,KeyB='someValue') |
131
|
|
|
* |
132
|
|
|
* @param ResourceSet $resourceSet The entity set containing the entity to fetch |
133
|
|
|
* @param KeyDescriptor $keyDescriptor The key identifying the entity to fetch |
134
|
|
|
* |
135
|
|
|
* @return object|null Returns entity instance if found else null |
136
|
|
|
*/ |
137
|
|
|
public function getResourceFromResourceSet( |
138
|
|
|
ResourceSet $resourceSet, |
139
|
|
|
KeyDescriptor $keyDescriptor = null |
140
|
|
|
) { |
141
|
|
|
return $this->getResource($resourceSet, $keyDescriptor); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Common method for getResourceFromRelatedResourceSet() and getResourceFromResourceSet() |
146
|
|
|
* @param ResourceSet|null $resourceSet |
147
|
|
|
* @param null|KeyDescriptor $keyDescriptor |
148
|
|
|
*/ |
149
|
|
|
protected function getResource( |
150
|
|
|
$resourceSet, |
151
|
|
|
$keyDescriptor, |
152
|
|
|
array $whereCondition = [], |
153
|
|
|
$sourceEntityInstance = null |
154
|
|
|
) { |
155
|
|
|
if ($resourceSet == null && $sourceEntityInstance == null) { |
156
|
|
|
throw new \Exception('Must supply at least one of a resource set and source entity'); |
157
|
|
|
} |
158
|
|
|
if ($sourceEntityInstance == null) { |
159
|
|
|
$entityClassName = $resourceSet->getResourceType()->getInstanceType()->name; |
|
|
|
|
160
|
|
|
$sourceEntityInstance = new $entityClassName(); |
161
|
|
|
} |
162
|
|
|
if ($keyDescriptor) { |
163
|
|
|
foreach ($keyDescriptor->getValidatedNamedValues() as $key => $value) { |
164
|
|
|
$sourceEntityInstance = $sourceEntityInstance->where($key, $value[0]); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
foreach ($whereCondition as $fieldName => $fieldValue) { |
168
|
|
|
$sourceEntityInstance = $sourceEntityInstance->where($fieldName, $fieldValue); |
169
|
|
|
} |
170
|
|
|
$sourceEntityInstance = $sourceEntityInstance->get(); |
171
|
|
|
if (0 == $sourceEntityInstance->count()) { |
172
|
|
|
return null; |
173
|
|
|
} |
174
|
|
|
return $sourceEntityInstance->first(); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Get related resource set for a resource |
179
|
|
|
* IE: http://host/EntitySet(1L)/NavigationPropertyToCollection |
180
|
|
|
* http://host/EntitySet?$expand=NavigationPropertyToCollection |
181
|
|
|
* |
182
|
|
|
* @param QueryType $queryType indicates if this is a query for a count, entities, or entities with a count |
183
|
|
|
* @param ResourceSet $sourceResourceSet The entity set containing the source entity |
184
|
|
|
* @param object $sourceEntityInstance The source entity instance. |
185
|
|
|
* @param ResourceSet $targetResourceSet The resource set of containing the target of the navigation property |
186
|
|
|
* @param ResourceProperty $targetProperty The navigation property to retrieve |
187
|
|
|
* @param FilterInfo $filter represents the $filter parameter of the OData query. NULL if no $filter specified |
188
|
|
|
* @param mixed $orderBy sorted order if we want to get the data in some specific order |
189
|
|
|
* @param int $top number of records which need to be skip |
190
|
|
|
* @param String $skip value indicating what records to skip |
191
|
|
|
* |
192
|
|
|
* @return QueryResult |
193
|
|
|
* |
194
|
|
|
*/ |
195
|
3 |
|
public function getRelatedResourceSet( |
196
|
|
|
QueryType $queryType, |
197
|
|
|
ResourceSet $sourceResourceSet, |
198
|
|
|
$sourceEntityInstance, |
199
|
|
|
ResourceSet $targetResourceSet, |
200
|
|
|
ResourceProperty $targetProperty, |
201
|
|
|
$filter = null, |
202
|
|
|
$orderBy = null, |
203
|
|
|
$top = null, |
204
|
|
|
$skip = null |
205
|
|
|
) { |
206
|
3 |
|
$propertyName = $targetProperty->getName(); |
207
|
3 |
|
$results = $sourceEntityInstance->$propertyName(); |
208
|
|
|
|
209
|
3 |
|
return $this->getResourceSet( |
210
|
3 |
|
$queryType, |
211
|
3 |
|
$sourceResourceSet, |
212
|
3 |
|
$filter, |
213
|
3 |
|
$orderBy, |
214
|
3 |
|
$top, |
215
|
3 |
|
$skip, |
216
|
|
|
$results |
217
|
3 |
|
); |
218
|
|
|
|
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Gets a related entity instance from an entity set identified by a key |
223
|
|
|
* IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33) |
224
|
|
|
* |
225
|
|
|
* @param ResourceSet $sourceResourceSet The entity set containing the source entity |
226
|
|
|
* @param object $sourceEntityInstance The source entity instance. |
227
|
|
|
* @param ResourceSet $targetResourceSet The entity set containing the entity to fetch |
228
|
|
|
* @param ResourceProperty $targetProperty The metadata of the target property. |
229
|
|
|
* @param KeyDescriptor $keyDescriptor The key identifying the entity to fetch |
230
|
|
|
* |
231
|
|
|
* @return object|null Returns entity instance if found else null |
232
|
|
|
*/ |
233
|
|
|
public function getResourceFromRelatedResourceSet( |
234
|
|
|
ResourceSet $sourceResourceSet, |
235
|
|
|
$sourceEntityInstance, |
236
|
|
|
ResourceSet $targetResourceSet, |
237
|
|
|
ResourceProperty $targetProperty, |
238
|
|
|
KeyDescriptor $keyDescriptor |
239
|
|
|
) { |
240
|
|
|
$propertyName = $targetProperty->getName(); |
241
|
|
|
return $this->getResource(null, $keyDescriptor, [], $sourceEntityInstance->$propertyName); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Get related resource for a resource |
246
|
|
|
* IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity |
247
|
|
|
* http://host/EntitySet?$expand=NavigationPropertyToSingleEntity |
248
|
|
|
* |
249
|
|
|
* @param ResourceSet $sourceResourceSet The entity set containing the source entity |
250
|
|
|
* @param object $sourceEntityInstance The source entity instance. |
251
|
|
|
* @param ResourceSet $targetResourceSet The entity set containing the entity pointed to by the navigation property |
252
|
|
|
* @param ResourceProperty $targetProperty The navigation property to fetch |
253
|
|
|
* |
254
|
|
|
* @return object|null The related resource if found else null |
255
|
|
|
*/ |
256
|
|
|
public function getRelatedResourceReference( |
257
|
|
|
ResourceSet $sourceResourceSet, |
258
|
|
|
$sourceEntityInstance, |
259
|
|
|
ResourceSet $targetResourceSet, |
260
|
|
|
ResourceProperty $targetProperty |
261
|
|
|
) { |
262
|
|
|
$propertyName = $targetProperty->getName(); |
263
|
|
|
return $sourceEntityInstance->$propertyName; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @param ResourceSet $resourceSet |
268
|
|
|
* @return mixed |
269
|
|
|
*/ |
270
|
|
|
protected function getSourceEntityInstance(ResourceSet $resourceSet) |
271
|
|
|
{ |
272
|
|
|
$entityClassName = $resourceSet->getResourceType()->getInstanceType()->name; |
273
|
|
|
$sourceEntityInstance = new $entityClassName(); |
274
|
|
|
return $sourceEntityInstance = $sourceEntityInstance->newQuery(); |
|
|
|
|
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.