ApiResourceFacade::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 9
dl 0
loc 20
rs 9.9666
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
namespace W2w\Lib\Apie\Core;
3
4
use Psr\Http\Message\RequestInterface;
5
use Psr\Http\Message\ServerRequestInterface;
6
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
7
use W2w\Lib\Apie\Core\Models\ApiResourceFacadeResponse;
8
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterRequest;
9
use W2w\Lib\Apie\Events\DeleteResourceEvent;
10
use W2w\Lib\Apie\Events\ModifySingleResourceEvent;
11
use W2w\Lib\Apie\Events\RetrievePaginatedResourcesEvent;
12
use W2w\Lib\Apie\Events\RetrieveSingleResourceEvent;
13
use W2w\Lib\Apie\Events\StoreExistingResourceEvent;
14
use W2w\Lib\Apie\Events\StoreNewResourceEvent;
15
use W2w\Lib\Apie\Exceptions\MethodNotAllowedException;
16
use W2w\Lib\Apie\Interfaces\FormatRetrieverInterface;
17
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface;
18
use W2w\Lib\Apie\OpenApiSchema\SubActions\SubAction;
19
use W2w\Lib\Apie\OpenApiSchema\SubActions\SubActionContainer;
20
use W2w\Lib\Apie\OpenApiSchema\SubActions\SubActionFactory;
21
use W2w\Lib\Apie\PluginInterfaces\ResourceLifeCycleInterface;
22
use W2w\Lib\Apie\Plugins\Core\Serializers\SymfonySerializerAdapter;
23
24
class ApiResourceFacade
25
{
26
    /**
27
     * @var ApiResourceRetriever
28
     */
29
    private $retriever;
30
31
    /**
32
     * @var ApiResourcePersister
33
     */
34
    private $persister;
35
36
    /**
37
     * @var ClassResourceConverter
38
     */
39
    private $converter;
40
41
    /**
42
     * @var ResourceSerializerInterface
43
     */
44
    private $serializer;
45
46
    /**
47
     * @var FormatRetrieverInterface
48
     */
49
    private $formatRetriever;
50
51
    /**
52
     * @var ResourceLifeCycleInterface[]
53
     */
54
    private $resourceLifeCycles;
55
56
    /**
57
     * @var SubActionContainer
58
     */
59
    private $subActionContainer;
60
61
    /**
62
     * @var NameConverterInterface
63
     */
64
    private $nameConverter;
65
66
    /**
67
     * @var ApiResourceFacadeResponseFactory
68
     */
69
    private $responseFactory;
70
71
    public function __construct(
72
        ApiResourceRetriever $retriever,
73
        ApiResourcePersister $persister,
74
        ClassResourceConverter $converter,
75
        ResourceSerializerInterface $serializer,
76
        FormatRetrieverInterface $formatRetriever,
77
        SubActionContainer $subActionContainer,
78
        NameConverterInterface $nameConverter,
79
        ApiResourceFacadeResponseFactory  $responseFactory,
80
        iterable $resourceLifeCycles
81
    ) {
82
        $this->retriever = $retriever;
83
        $this->persister = $persister;
84
        $this->converter = $converter;
85
        $this->serializer = $serializer;
86
        $this->formatRetriever = $formatRetriever;
87
        $this->subActionContainer = $subActionContainer;
88
        $this->nameConverter = $nameConverter;
89
        $this->responseFactory = $responseFactory;
90
        $this->resourceLifeCycles = $resourceLifeCycles;
0 ignored issues
show
Documentation Bug introduced by
It seems like $resourceLifeCycles of type iterable is incompatible with the declared type W2w\Lib\Apie\PluginInter...rceLifeCycleInterface[] of property $resourceLifeCycles.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
91
    }
92
93
    private function runLifeCycleEvent(string $event, ...$args)
94
    {
95
        foreach ($this->resourceLifeCycles as $resourceLifeCycle) {
96
            $resourceLifeCycle->$event(...$args);
97
        }
98
    }
99
100
    /**
101
     * Does a DELETE instance call.
102
     *
103
     * @param string $resourceClass
104
     * @param string $id
105
     * @return ApiResourceFacadeResponse
106
     */
107
    public function delete(string $resourceClass, string $id): ApiResourceFacadeResponse
108
    {
109
        $event = new DeleteResourceEvent($resourceClass, $id);
110
        $this->runLifeCycleEvent('onPreDeleteResource', $event);
111
        $this->persister->delete($resourceClass, $id);
112
        $this->runLifeCycleEvent('onPostDeleteResource', $event);
113
114
        return new ApiResourceFacadeResponse(
115
            $this->serializer,
116
            null,
117
            'application/json'
118
        );
119
    }
120
121
    /**
122
     * Does a GET instance call.
123
     *
124
     * @param string $resourceClass
125
     * @param string $id
126
     * @param RequestInterface|null $request
127
     * @return ApiResourceFacadeResponse
128
     */
129
    public function get(string $resourceClass, string $id, ?RequestInterface $request): ApiResourceFacadeResponse
130
    {
131
        $event = new RetrieveSingleResourceEvent($resourceClass, $id, $request);
132
        $this->runLifeCycleEvent('onPreRetrieveResource', $event);
133
        // preRetrieveResource event could override resource...
134
        if (!$event->getResource()) {
135
            $event->setResource($this->retriever->retrieve($resourceClass, $id));
136
        }
137
        $this->runLifeCycleEvent('onPostRetrieveResource', $event);
138
139
        return $this->responseFactory->createResponseForResource($event->getResource(), $request);
140
    }
141
142
    /**
143
     * Does a GET all call.
144
     *
145
     * @param string $resourceClass
146
     * @param ServerRequestInterface|null $request
147
     * @return ApiResourceFacadeResponse
148
     */
149
    public function getAll(string $resourceClass, ?ServerRequestInterface $request): ApiResourceFacadeResponse
150
    {
151
        $searchFilterRequest = new SearchFilterRequest();
152
        if ($request) {
153
            $searchFilterRequest = SearchFilterRequest::createFromPsrRequest($request);
154
        }
155
        $event = new RetrievePaginatedResourcesEvent($resourceClass, $searchFilterRequest, $request);
156
        $this->runLifeCycleEvent('onPreRetrieveAllResources', $event);
157
        if (null === $event->getResources()) {
158
            $event->setResources($this->retriever->retrieveAll($resourceClass, $searchFilterRequest));
159
        }
160
        $this->runLifeCycleEvent('onPostRetrieveAllResources', $event);
161
162
        return $this->responseFactory->createResponseListForResource($event->getResources(), $resourceClass, $searchFilterRequest, $request);
163
    }
164
165
    /**
166
     * Does a PUT instance call.
167
     *
168
     * @param string $resourceClass
169
     * @param string $id
170
     * @param RequestInterface $request
171
     * @return ApiResourceFacadeResponse
172
     */
173
    public function put(string $resourceClass, string $id, RequestInterface $request): ApiResourceFacadeResponse
174
    {
175
        $resource = $this->get($resourceClass, $id, $request)->getResource();
176
        $event = new ModifySingleResourceEvent($resource, $id, $request);
177
        $this->runLifeCycleEvent('onPreModifyResource', $event);
178
        $request = $event->getRequest();
179
180
        $event->setResource(
181
            $this->serializer->putData(
182
                $event->getResource(),
183
                (string) $request->getBody(),
184
                $request->getHeader('Content-Type')[0] ?? 'application/json'
185
            )
186
        );
187
        $this->runLifeCycleEvent('onPostModifyResource', $event);
188
189
        $event = new StoreExistingResourceEvent($event);
190
        $this->runLifeCycleEvent('onPrePersistExistingResource', $event);
191
        $event->setResource($this->persister->persistExisting($event->getResource(), $id));
192
        $this->runLifeCycleEvent('onPostPersistExistingResource', $event);
193
194
        return $this->responseFactory->createResponseForResource($event->getResource(), $request);
195
    }
196
197
    /**
198
     * Does a POST new instance call.
199
     *
200
     * @param string $resourceClass
201
     * @param RequestInterface $request
202
     * @return ApiResourceFacadeResponse
203
     */
204
    public function post(string $resourceClass, RequestInterface $request): ApiResourceFacadeResponse
205
    {
206
        $event = new StoreNewResourceEvent($resourceClass, $request);
207
        $this->runLifeCycleEvent('onPreCreateResource', $event);
208
        if (!$event->getResource()) {
209
            $event->setResource($this->serializer->postData(
210
                $resourceClass,
211
                (string)$event->getRequest()->getBody(),
212
                $event->getRequest()->getHeader('Content-Type')[0] ?? 'application/json'
213
            ));
214
        }
215
        $this->runLifeCycleEvent('onPostCreateResource', $event);
216
        $event = new StoreExistingResourceEvent($event);
217
        $this->runLifeCycleEvent('onPrePersistNewResource', $event);
218
        $event->setResource($this->persister->persistNew($event->getResource()));
219
        $this->runLifeCycleEvent('onPostPersistNewResource', $event);
220
221
        return $this->responseFactory->createResponseForResource($event->getResource(), $request);
222
    }
223
224
    /**
225
     * Runs a sub action.
226
     * @param string $resourceClass
227
     * @param string $id
228
     * @param string $actionName
229
     * @param RequestInterface $request
230
     * @return ApiResourceFacadeResponse
231
     * @todo move logic to SubActionContainer
232
     */
233
    public function postSubAction(string $resourceClass, string $id, string $actionName, RequestInterface $request): ApiResourceFacadeResponse
234
    {
235
        $subActions = $this->subActionContainer->getSubActionsForResourceClass($resourceClass);
236
        if (empty($subActions[$actionName])) {
237
            throw new MethodNotAllowedException('POST');
238
        }
239
        /** @var SubAction $subAction */
240
        $subAction = $subActions[$actionName];
241
        $resource = $this->get($resourceClass, $id, $request)->getResource();
242
        $reflectionMethod = $subAction->getReflectionMethod();
243
        $context = [
244
            'initial-arguments' => [],
245
            'object-instance' => $subAction->getObject(),
246
        ];
247
        assert($this->serializer instanceof SymfonySerializerAdapter);
248
        $parameters = $reflectionMethod->getParameters();
249
        if ($parameters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parameters of type ReflectionParameter[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
250
            $context['initial-arguments'][$this->nameConverter->normalize($parameters[0]->getName())] = $resource;
251
        }
252
        $data = $this->serializer->getSerializer()->deserialize(
253
            $request->getBody(),
254
            'ReflectionMethod::' . get_class($subAction->getObject()) . '::' . $reflectionMethod->getName(),
255
            'json',
256
            $context
257
        );
258
        return $this->responseFactory->createResponseForResource($data, $request);
259
    }
260
}
261