Test Failed
Pull Request — master (#41)
by Alex
05:40
created

LaravelReadQuery   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 261
Duplicated Lines 10.34 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 36
c 0
b 0
f 0
lcom 1
cbo 8
dl 27
loc 261
rs 8.8

8 Methods

Rating   Name   Duplication   Size   Complexity  
C getResourceSet() 0 64 16
B getRelatedResourceSet() 0 31 2
A getResourceFromResourceSet() 0 6 1
C getResource() 0 29 8
A getRelatedResourceReference() 14 14 2
A getResourceFromRelatedResourceSet() 13 13 2
A getSourceEntityInstance() 0 5 1
A checkSourceInstance() 0 6 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Query;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Database\Eloquent\Relations\Relation;
7
use Illuminate\Support\Facades\App;
8
use Symfony\Component\Process\Exception\InvalidArgumentException;
9
use POData\Providers\Metadata\ResourceProperty;
10
use POData\Providers\Metadata\ResourceSet;
11
use POData\Providers\Query\QueryResult;
12
use POData\Providers\Query\QueryType;
13
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
14
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
15
16
class LaravelReadQuery
17
{
18
19
    /**
20
     * Gets collection of entities belongs to an entity set
21
     * IE: http://host/EntitySet
22
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value
23
     *
24
     * @param QueryType $queryType indicates if this is a query for a count, entities, or entities with a count
25
     * @param ResourceSet $resourceSet The entity set containing the entities to fetch
26
     * @param FilterInfo $filterInfo represents the $filter parameter of the OData query.  NULL if no $filter specified
27
     * @param mixed $orderBy sorted order if we want to get the data in some specific order
28
     * @param int $top number of records which  need to be skip
29
     * @param String $skipToken value indicating what records to skip
30
     * @param Model|Relation|null $sourceEntityInstance Starting point of query
31
     *
32
     * @return QueryResult
33
     */
34
    public function getResourceSet(
35
        QueryType $queryType,
36
        ResourceSet $resourceSet,
37
        $filterInfo = null,
38
        $orderBy = null,
39
        $top = null,
40
        $skipToken = null,
41
        $sourceEntityInstance = null
42
    ) {
43
        if (null != $filterInfo && !($filterInfo instanceof FilterInfo)) {
44
            throw new InvalidArgumentException('Filter info must be either null or instance of FilterInfo.');
45
        }
46
47
        $this->checkSourceInstance($sourceEntityInstance);
48
49
        if (null == $sourceEntityInstance) {
50
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
51
        }
52
53
        $result          = new QueryResult();
54
        $result->results = null;
55
        $result->count   = null;
56
57
        if (null != $orderBy) {
58
            foreach ($orderBy->getOrderByInfo()->getOrderByPathSegments() as $order) {
59
                foreach ($order->getSubPathSegments() as $subOrder) {
60
                    $sourceEntityInstance = $sourceEntityInstance->orderBy(
61
                        $subOrder->getName(),
62
                        $order->isAscending() ? 'asc' : 'desc'
63
                    );
64
                }
65
            }
66
        }
67
68
        $resultSet = $sourceEntityInstance->get();
69
70
        if (isset($filterInfo)) {
71
            $method = "return ".$filterInfo->getExpressionAsString().";";
72
            $clln = "$".$resourceSet->getResourceType()->getName();
73
            $isvalid = create_function($clln, $method);
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
74
            $resultSet = $resultSet->filter($isvalid);
75
        }
76
77
        $resultCount = $resultSet->count();
78
79
        if (isset($skipToken)) {
80
            $resultSet = $resultSet->slice($skipToken);
81
        }
82
        if (isset($top)) {
83
            $resultSet = $resultSet->take($top);
84
        }
85
86
87
        if (QueryType::ENTITIES() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
88
            $result->results = array();
89
            foreach ($resultSet as $res) {
90
                $result->results[] = $res;
91
            }
92
        }
93
        if (QueryType::COUNT() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
94
            $result->count = $resultCount;
95
        }
96
        return $result;
97
    }
98
99
    /**
100
     * Get related resource set for a resource
101
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
102
     * http://host/EntitySet?$expand=NavigationPropertyToCollection
103
     *
104
     * @param QueryType $queryType indicates if this is a query for a count, entities, or entities with a count
105
     * @param ResourceSet $sourceResourceSet The entity set containing the source entity
106
     * @param object $sourceEntityInstance The source entity instance.
107
     * @param ResourceSet $targetResourceSet The resource set of containing the target of the navigation property
108
     * @param ResourceProperty $targetProperty The navigation property to retrieve
109
     * @param FilterInfo $filter represents the $filter parameter of the OData query.  NULL if no $filter specified
110
     * @param mixed $orderBy sorted order if we want to get the data in some specific order
111
     * @param int $top number of records which  need to be skip
112
     * @param String $skip value indicating what records to skip
113
     *
114
     * @return QueryResult
115
     *
116
     */
117
    public function getRelatedResourceSet(
118
        QueryType $queryType,
119
        ResourceSet $sourceResourceSet,
120
        $sourceEntityInstance,
121
        ResourceSet $targetResourceSet,
122
        ResourceProperty $targetProperty,
123
        $filter = null,
124
        $orderBy = null,
125
        $top = null,
126
        $skip = null
127
    ) {
128
        if (!($sourceEntityInstance instanceof Model)) {
129
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
130
        }
131
132
        assert(null != $sourceEntityInstance, "Source instance must not be null");
133
        $this->checkSourceInstance($sourceEntityInstance);
134
135
        $propertyName = $targetProperty->getName();
136
        $results = $sourceEntityInstance->$propertyName();
137
138
        return $this->getResourceSet(
139
            $queryType,
140
            $sourceResourceSet,
141
            $filter,
142
            $orderBy,
143
            $top,
144
            $skip,
145
            $results
146
        );
147
    }
148
149
    /**
150
     * Gets an entity instance from an entity set identified by a key
151
     * IE: http://host/EntitySet(1L)
152
     * http://host/EntitySet(KeyA=2L,KeyB='someValue')
153
     *
154
     * @param ResourceSet $resourceSet The entity set containing the entity to fetch
155
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to fetch
156
     *
157
     * @return object|null Returns entity instance if found else null
158
     */
159
    public function getResourceFromResourceSet(
160
        ResourceSet $resourceSet,
161
        KeyDescriptor $keyDescriptor = null
162
    ) {
163
        return $this->getResource($resourceSet, $keyDescriptor);
164
    }
165
166
167
    /**
168
     * Common method for getResourceFromRelatedResourceSet() and getResourceFromResourceSet()
169
     * @param ResourceSet|null $resourceSet
170
     * @param KeyDescriptor|null $keyDescriptor
171
     * @param Model|Relation|null $sourceEntityInstance Starting point of query
172
     */
173
    public function getResource(
174
        ResourceSet $resourceSet = null,
175
        KeyDescriptor $keyDescriptor = null,
176
        array $whereCondition = [],
177
        $sourceEntityInstance = null
178
    ) {
179
        if (null == $resourceSet && null == $sourceEntityInstance) {
180
            throw new \Exception('Must supply at least one of a resource set and source entity.');
181
        }
182
183
        $this->checkSourceInstance($sourceEntityInstance);
184
185
        if (null == $sourceEntityInstance) {
186
            assert(null != $resourceSet);
187
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
188
        }
189
190
        if ($keyDescriptor) {
191
            foreach ($keyDescriptor->getValidatedNamedValues() as $key => $value) {
192
                $trimValue = trim($value[0], "\"'");
193
                $sourceEntityInstance = $sourceEntityInstance->where($key, $trimValue);
194
            }
195
        }
196
        foreach ($whereCondition as $fieldName => $fieldValue) {
197
            $sourceEntityInstance = $sourceEntityInstance->where($fieldName, $fieldValue);
198
        }
199
        $sourceEntityInstance = $sourceEntityInstance->get();
200
        return (0 == $sourceEntityInstance->count()) ? null : $sourceEntityInstance->first();
201
    }
202
203
    /**
204
     * Get related resource for a resource
205
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
206
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity
207
     *
208
     * @param ResourceSet $sourceResourceSet The entity set containing the source entity
209
     * @param object $sourceEntityInstance The source entity instance.
210
     * @param ResourceSet $targetResourceSet The entity set containing the entity pointed to by the navigation property
211
     * @param ResourceProperty $targetProperty The navigation property to fetch
212
     *
213
     * @return object|null The related resource if found else null
214
     */
215 View Code Duplication
    public function getRelatedResourceReference(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
        ResourceSet $sourceResourceSet,
217
        $sourceEntityInstance,
218
        ResourceSet $targetResourceSet,
219
        ResourceProperty $targetProperty
220
    ) {
221
        if (!($sourceEntityInstance instanceof Model)) {
222
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
223
        }
224
        $this->checkSourceInstance($sourceEntityInstance);
225
226
        $propertyName = $targetProperty->getName();
227
        return $sourceEntityInstance->$propertyName;
228
    }
229
230
    /**
231
     * Gets a related entity instance from an entity set identified by a key
232
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33)
233
     *
234
     * @param ResourceSet $sourceResourceSet The entity set containing the source entity
235
     * @param object $sourceEntityInstance The source entity instance.
236
     * @param ResourceSet $targetResourceSet The entity set containing the entity to fetch
237
     * @param ResourceProperty $targetProperty The metadata of the target property.
238
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to fetch
239
     *
240
     * @return object|null Returns entity instance if found else null
241
     */
242 View Code Duplication
    public function getResourceFromRelatedResourceSet(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
243
        ResourceSet $sourceResourceSet,
244
        $sourceEntityInstance,
245
        ResourceSet $targetResourceSet,
246
        ResourceProperty $targetProperty,
247
        KeyDescriptor $keyDescriptor
248
    ) {
249
        if (!($sourceEntityInstance instanceof Model)) {
250
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
251
        }
252
        $propertyName = $targetProperty->getName();
253
        return $this->getResource(null, $keyDescriptor, [], $sourceEntityInstance->$propertyName);
254
    }
255
256
257
    /**
258
     * @param ResourceSet $resourceSet
259
     * @return mixed
260
     */
261
    protected function getSourceEntityInstance(ResourceSet $resourceSet)
262
    {
263
        $entityClassName = $resourceSet->getResourceType()->getInstanceType()->name;
264
        return App::make($entityClassName);
265
    }
266
267
    /**
268
     * @param Model|Relation|null $source
269
     */
270
    protected function checkSourceInstance($source)
271
    {
272
        if (!(null == $source || $source instanceof Model || $source instanceof Relation)) {
273
            throw new InvalidArgumentException('Source entity instance must be null, a model, or a relation.');
274
        }
275
    }
276
}
277