Completed
Push — master ( 0d5de4...97668b )
by Jacob
6s
created

AbstractAdapter::findQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 7
1
<?php
2
3
namespace As3\Modlr\Api;
4
5
use As3\Modlr\Exception\HttpExceptionInterface;
6
use As3\Modlr\Metadata\EntityMetadata;
7
use As3\Modlr\Models\Collection;
8
use As3\Modlr\Models\Model;
9
use As3\Modlr\Rest;
10
use As3\Modlr\Store\Store;
11
12
/**
13
 * Abstract Adapter implementation for handling API operations.
14
 *
15
 * @author Jacob Bare <[email protected]>
16
 */
17
abstract class AbstractAdapter implements AdapterInterface
18
{
19
    /**
20
     * The Serializer
21
     *
22
     * @var SerializerInterface
23
     */
24
    protected $serializer;
25
26
    /**
27
     * The Normalizer
28
     *
29
     * @var NormalizerInterface
30
     */
31
    protected $normalizer;
32
33
    /**
34
     * The Store to use for persistence operations.
35
     *
36
     * @var Store
37
     */
38
    protected $store;
39
40
    /**
41
     * The REST configuration.
42
     *
43
     * @var Rest\RestConfiguration
44
     */
45
    protected $config;
46
47
    /**
48
     * Constructor.
49
     *
50
     * @param   SerializerInterface     $serializer
51
     * @param   NormalizerInterface     $normalizer
52
     * @param   StoreInterface          $store
53
     * @param   Rest\RestConfiguration  $config
54
     */
55
    public function __construct(SerializerInterface $serializer, NormalizerInterface $normalizer, Store $store, Rest\RestConfiguration $config)
56
    {
57
        $this->serializer = $serializer;
58
        $this->normalizer = $normalizer;
59
        $this->store = $store;
60
        $this->config = $config;
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66
    abstract public function processRequest(Rest\RestRequest $request);
67
68
    /**
69
     * {@inheritDoc}
70
     */
71
    public function findRecord($typeKey, $identifier) //, array $fields = [], array $inclusions = [])
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
72
    {
73
        $model = $this->getStore()->find($typeKey, $identifier);
74
        $payload = $this->serialize($model);
75
        return $this->createRestResponse(200, $payload);
76
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81
    public function findAll($typeKey, array $identifiers = []) //, array $pagination = [], array $fields = [], array $inclusions = [], array $sort = [])
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
82
    {
83
        $collection = $this->getStore()->findAll($typeKey, $identifiers);
84
        $payload = $this->serializeCollection($collection);
85
        return $this->createRestResponse(200, $payload);
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91
    public function findQuery($typeKey, array $criteria, array $fields = [], array $sort = [], array $inclusions =[], $offset = 0, $limit = 0)
92
    {
93
        $collection = $this->getStore()->findQuery($typeKey, $criteria, $fields, $sort, $offset, $limit);
94
        $payload = $this->serializeCollection($collection);
95
        return $this->createRestResponse(200, $payload);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createRestResponse(200, $payload); (As3\Modlr\Rest\RestResponse) is incompatible with the return type declared by the interface As3\Modlr\Api\AdapterInterface::findQuery of type As3\Modlr\Models\Collection.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
96
    }
97
98
    /**
99
     * {@inheritDoc}
100
     */
101
    public function findRelationship($typeKey, $identifier, $fieldKey)
102
    {
103
        $model = $this->getStore()->find($typeKey, $identifier);
104
        if (false === $model->isRelationship($fieldKey)) {
105
            throw AdapterException::badRequest(sprintf('The relationship field "%s" does not exist on model "%s"', $fieldKey, $typeKey));
106
        }
107
        $rel = $model->get($fieldKey);
108
        $payload = (true === $model->isHasOne($fieldKey)) ? $this->serialize($rel) : $this->serializeArray($rel);
109
        return $this->createRestResponse(200, $payload);
110
    }
111
112
    /**
113
     * {@inheritDoc}
114
     */
115 View Code Duplication
    public function createRecord($typeKey, Rest\RestPayload $payload) //, array $fields = [], array $inclusions = [])
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...
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
116
    {
117
        // @todo Do normalized payloads need to be wrapped in an object, similar to persistence Records?
118
        $normalized = $this->normalize($payload);
119
        $this->validateCreatePayload($typeKey, $normalized);
120
121
        $model = $this->getStore()->create($normalized['type']);
122
        $model->apply($normalized['properties']);
123
        $model->save();
124
        $payload = $this->serialize($model);
125
        return $this->createRestResponse(201, $payload);
126
    }
127
128
    /**
129
     * {@inheritDoc}
130
     */
131 View Code Duplication
    public function updateRecord($typeKey, $identifier, Rest\RestPayload $payload) // , array $fields = [], array $inclusions = [])
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...
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
132
    {
133
        $normalized = $this->normalize($payload);
134
        $this->validateUpdatePayload($typeKey, $identifier, $normalized);
135
136
        $model = $this->getStore()->find($typeKey, $normalized['id']);
137
        $model->apply($normalized['properties']);
138
        $model->save();
139
        $payload = $this->serialize($model);
140
        return $this->createRestResponse(200, $payload);
141
    }
142
143
    /**
144
     * {@inheritDoc}
145
     */
146
    public function deleteRecord($typeKey, $identifier)
147
    {
148
        $this->getStore()->delete($typeKey, $identifier);
149
        return $this->createRestResponse(204);
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     */
155
    public function autocomplete($typeKey, $attributeKey, $searchValue, array $pagination = [])
156
    {
157
        $collection = $this->getStore()->searchAutocomplete($typeKey, $attributeKey, $searchValue);
158
        $payload = $this->serializeCollection($collection);
159
        return $this->createRestResponse(200, $payload);
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165
    abstract public function buildUrl(EntityMetadata $metadata, $identifier, $relFieldKey = null, $isRelatedLink = false);
166
167
    /**
168
     * Validates a normalized create record payload.
169
     *
170
     * @param   string  $typeKey
171
     * @param   array   $normalized
172
     * @throws  AdapterException    On invalid create record payload.
173
     */
174
    abstract protected function validateCreatePayload($typeKey, array $normalized);
175
176
    /**
177
     * Validates a normalized update record payload.
178
     *
179
     * @param   string  $typeKey
180
     * @param   string  $identifier
181
     * @param   array   $normalized
182
     * @throws  AdapterException    On invalid update record payload.
183
     */
184
    abstract protected function validateUpdatePayload($typeKey, $identifier, array $normalized);
185
186
    /**
187
     * Creates a RestResponse object based on common response parameters shared by this adapter.
188
     *
189
     * @param   int                 $status
190
     * @param   Rest\RestPayload    $payload
191
     * @return  Rest\RestResponse
192
     */
193
    abstract protected function createRestResponse($status, Rest\RestPayload $payload = null);
194
195
    /**
196
     * {@inheritDoc}
197
     */
198
    public function handleException(\Exception $e)
199
    {
200
        $refl = new \ReflectionClass($e);
201
        if ($e instanceof HttpExceptionInterface) {
202
            $title  = sprintf('%s::%s', $refl->getShortName(), $e->getErrorType());
203
            $detail = $e->getMessage();
204
            $status = $e->getHttpCode();
205
        } else {
206
            $title  = $refl->getShortName();
207
            $detail = 'Oh no! Something bad happened on the server! Please try again.';
208
            $status = 500;
209
        }
210
211
        $serialized = $this->getSerializer()->serializeError($title, $detail, $status);
212
        $payload = new Rest\RestPayload($serialized);
213
        return $this->createRestResponse($status, $payload);
214
    }
215
216
    /**
217
     * {@inheritDoc}
218
     */
219
    public function getStore()
220
    {
221
        return $this->store;
222
    }
223
224
    /**
225
     * {@inheritDoc}
226
     */
227
    public function getSerializer()
228
    {
229
        return $this->serializer;
230
    }
231
232
    /**
233
     * {@inheritDoc}
234
     */
235
    public function getNormalizer()
236
    {
237
        return $this->normalizer;
238
    }
239
240
    /**
241
     * {@inheritDoc}
242
     */
243
    public function normalize(Rest\RestPayload $payload)
244
    {
245
        return $this->getNormalizer()->normalize($payload, $this);
246
    }
247
248
    /**
249
     * {@inheritDoc}
250
     */
251
    public function serialize(Model $model = null)
252
    {
253
        $serialized = $this->getSerializer()->serialize($model, $this);
254
        $this->validateSerialization($serialized);
255
        return new Rest\RestPayload($serialized);
0 ignored issues
show
Bug introduced by
It seems like $serialized defined by $this->getSerializer()->serialize($model, $this) on line 253 can also be of type array; however, As3\Modlr\Rest\RestPayload::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
256
    }
257
258
    /**
259
     * {@inheritDoc}
260
     */
261
    public function serializeArray(array $models)
262
    {
263
        $serialized = $this->getSerializer()->serializeArray($models, $this);
264
        $this->validateSerialization($serialized);
265
        return new Rest\RestPayload($serialized);
0 ignored issues
show
Bug introduced by
It seems like $serialized defined by $this->getSerializer()->...zeArray($models, $this) on line 263 can also be of type array; however, As3\Modlr\Rest\RestPayload::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
266
    }
267
268
    /**
269
     * {@inheritDoc}
270
     */
271
    public function serializeCollection(Collection $collection)
272
    {
273
        $serialized = $this->getSerializer()->serializeCollection($collection, $this);
274
        $this->validateSerialization($serialized);
275
        return new Rest\RestPayload($serialized);
0 ignored issues
show
Bug introduced by
It seems like $serialized defined by $this->getSerializer()->...ion($collection, $this) on line 273 can also be of type array; however, As3\Modlr\Rest\RestPayload::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
276
    }
277
278
    /**
279
     * Validates that the serialized value is support by a RestPayload.
280
     *
281
     * @param   mixed   $serialized
282
     * @return  bool
283
     * @throws  AdapterException    On invalid serialized value.
284
     */
285
    protected function validateSerialization($serialized)
286
    {
287
        if (!is_string($serialized)) {
288
            throw AdapterException::badRequest('Unable to create an API response payload. Invalid serialization occurred.');
289
        }
290
        return true;
291
    }
292
293
    /**
294
     * {@inheritDoc}
295
     */
296
    public function getEntityMetadata($typeKey)
297
    {
298
        return $this->getStore()->getMetadataForType($typeKey);
299
    }
300
}
301