PaginationPlugin::onPostPersistExistingResource()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 0
nc 1
nop 1
dl 0
loc 2
rs 10
c 1
b 0
f 1
1
<?php
2
3
namespace W2w\Lib\Apie\Plugins\Pagination;
4
5
use erasys\OpenApi\Spec\v3\Document;
6
use erasys\OpenApi\Spec\v3\Header;
7
use erasys\OpenApi\Spec\v3\Operation;
8
use erasys\OpenApi\Spec\v3\PathItem;
9
use erasys\OpenApi\Spec\v3\Reference;
10
use erasys\OpenApi\Spec\v3\Schema;
11
use Pagerfanta\Adapter\AdapterInterface;
12
use Pagerfanta\Adapter\ArrayAdapter;
13
use Pagerfanta\Pagerfanta;
14
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterHelper;
15
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterRequest;
16
use W2w\Lib\Apie\Events\DecodeEvent;
17
use W2w\Lib\Apie\Events\DeleteResourceEvent;
18
use W2w\Lib\Apie\Events\ModifySingleResourceEvent;
19
use W2w\Lib\Apie\Events\NormalizeEvent;
20
use W2w\Lib\Apie\Events\ResponseAllEvent;
21
use W2w\Lib\Apie\Events\ResponseEvent;
22
use W2w\Lib\Apie\Events\RetrievePaginatedResourcesEvent;
23
use W2w\Lib\Apie\Events\RetrieveSingleResourceEvent;
24
use W2w\Lib\Apie\Events\StoreExistingResourceEvent;
25
use W2w\Lib\Apie\Events\StoreNewResourceEvent;
26
use W2w\Lib\Apie\PluginInterfaces\ApieAwareInterface;
27
use W2w\Lib\Apie\PluginInterfaces\ApieAwareTrait;
28
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface;
29
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface;
30
use W2w\Lib\Apie\PluginInterfaces\ResourceLifeCycleInterface;
31
use W2w\Lib\Apie\Plugins\Pagination\Normalizers\PaginatorNormalizer;
32
33
class PaginationPlugin implements ResourceLifeCycleInterface, NormalizerProviderInterface, OpenApiEventProviderInterface, ApieAwareInterface
34
{
35
    use ApieAwareTrait;
36
37
    const PREV_HEADER = 'x-pagination-previous';
38
39
    const NEXT_HEADER = 'x-pagination-next';
40
41
    const FIRST_HEADER = 'x-pagination-first';
42
43
    const LAST_HEADER = 'x-pagination-last';
44
45
    const COUNT_HEADER = 'x-pagination-count';
46
47
    public function getNormalizers(): array
48
    {
49
        return [new PaginatorNormalizer()];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array(new W2w\Lib...\PaginatorNormalizer()) returns the type array<integer,W2w\Lib\Ap...rs\PaginatorNormalizer> which is incompatible with the return type mandated by W2w\Lib\Apie\PluginInter...rface::getNormalizers() of Symfony\Component\Serial...DenormalizerInterface[].

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
50
    }
51
52
    public function onOpenApiDocGenerated(Document $document): Document
53
    {
54
        /** @var PathItem[] $paths */
55
        $paths = $document->paths ?? [];
56
        $added = false;
57
        foreach ($paths as $url => $path) {
58
            if (strpos($url, '{id}', 0) === false && $path->get && $this->patch($path->get)) {
59
                $added = true;
60
            }
61
        }
62
        if ($added) {
63
            $document->components->headers['Count'] = new Header('number of results', ['schema' => new Schema(['type' => 'number', 'format' => 'int'])]);
64
            $document->components->headers['Url'] = new Header('pagination url', ['schema' => new Schema(['type' => 'string', 'format' => 'url']) ]);
65
        }
66
        return $document;
67
    }
68
69
    private function patch(Operation $operation): bool
70
    {
71
        $added = false;
72
        foreach ($operation->responses as &$response) {
73
            if ($response instanceof Reference) {
74
                continue;
75
            }
76
            $added = true;
77
            $countSchema = new Reference('#/components/headers/Count');
78
            $urlSchema = new Reference('#/components/headers/Url');
79
            $response->headers[self::COUNT_HEADER] = $countSchema;
80
            $response->headers[self::PREV_HEADER] = $urlSchema;
81
            $response->headers[self::NEXT_HEADER] = $urlSchema;
82
            $response->headers[self::FIRST_HEADER] = $urlSchema;
83
            $response->headers[self::LAST_HEADER] = $urlSchema;
84
        }
85
        return $added;
86
    }
87
88
    public function onPreDeleteResource(DeleteResourceEvent $event)
89
    {
90
    }
91
92
    public function onPostDeleteResource(DeleteResourceEvent $event)
93
    {
94
    }
95
96
    public function onPreRetrieveResource(RetrieveSingleResourceEvent $event)
97
    {
98
    }
99
100
    public function onPostRetrieveResource(RetrieveSingleResourceEvent $event)
101
    {
102
    }
103
104
    public function onPreRetrieveAllResources(RetrievePaginatedResourcesEvent $event)
105
    {
106
    }
107
108
    public function onPostRetrieveAllResources(RetrievePaginatedResourcesEvent $event)
109
    {
110
    }
111
112
    public function onPrePersistExistingResource(StoreExistingResourceEvent $event)
113
    {
114
    }
115
116
    public function onPostPersistExistingResource(StoreExistingResourceEvent $event)
117
    {
118
    }
119
120
    public function onPreDecodeRequestBody(DecodeEvent $event)
121
    {
122
    }
123
124
    public function onPostDecodeRequestBody(DecodeEvent $event)
125
    {
126
    }
127
128
    public function onPreModifyResource(ModifySingleResourceEvent $event)
129
    {
130
    }
131
132
    public function onPostModifyResource(ModifySingleResourceEvent $event)
133
    {
134
    }
135
136
    public function onPreCreateResource(StoreNewResourceEvent $event)
137
    {
138
    }
139
140
    public function onPostCreateResource(StoreNewResourceEvent $event)
141
    {
142
    }
143
144
    public function onPrePersistNewResource(StoreExistingResourceEvent $event)
145
    {
146
    }
147
148
    public function onPostPersistNewResource(StoreExistingResourceEvent $event)
149
    {
150
    }
151
152
    public function onPreCreateResponse(ResponseEvent $event)
153
    {
154
    }
155
156
    public function onPostCreateResponse(ResponseEvent $event)
157
    {
158
        if (!($event instanceof ResponseAllEvent)) {
159
            return;
160
        }
161
        $resource = $event->getResource();
162
        if (!($resource instanceof Pagerfanta)) {
163
            if (is_array($resource)) {
164
                $resource = new Pagerfanta(new ArrayAdapter($resource));
165
            } else if (is_iterable($resource)) {
166
                $resource = new Pagerfanta(new ArrayAdapter(iterator_to_array($resource)));
167
            } else {
168
                return;
169
            }
170
            $event->getSearchFilterRequest()->updatePaginator($resource);
171
        }
172
        $response = $event->getResponse()
173
            ->withHeader(self::FIRST_HEADER, $this->generateUrl($event, 0))
174
            ->withHeader(self::LAST_HEADER, $this->generateUrl($event, $resource->getNbPages() - 1))
175
            ->withHeader(self::COUNT_HEADER, $resource->getNbResults());
176
        if ($resource->hasPreviousPage()) {
177
            $response = $response->withHeader(self::PREV_HEADER, $this->generateUrl($event, $resource->getPreviousPage() - 1));
178
        }
179
        if ($resource->hasNextPage()) {
180
            $response = $response->withHeader(self::NEXT_HEADER, $this->generateUrl($event, $resource->getNextPage() - 1));
181
        }
182
        $event->setResponse($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type null; however, parameter $response of W2w\Lib\Apie\Events\ResponseEvent::setResponse() does only seem to accept Psr\Http\Message\ResponseInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
        $event->setResponse(/** @scrutinizer ignore-type */ $response);
Loading history...
183
    }
184
185
    private function generateUrl(ResponseAllEvent  $event, int $page)
186
    {
187
        $filterRequest = new SearchFilterRequest($page, $event->getSearchFilterRequest()->getNumberOfItems());
188
        return $this->getApie()->getOverviewUrlForResourceClass($event->getResourceClass(), $filterRequest);
189
    }
190
191
    public function onPreCreateNormalizedData(NormalizeEvent $event)
192
    {
193
    }
194
195
    public function onPostCreateNormalizedData(NormalizeEvent $event)
196
    {
197
    }
198
}
199