AddPaginationLinksListener::handle()   B
last analyzed

Complexity

Conditions 8
Paths 15

Size

Total Lines 44
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 20
nc 15
nop 1
dl 0
loc 44
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Setono\SyliusPaginationPlugin\EventListener;
6
7
use Fig\Link\GenericLinkProvider;
8
use Fig\Link\Link;
9
use Psr\Link\EvolvableLinkProviderInterface;
10
use Setono\SyliusPaginationPlugin\EventHandler\EventHandlerInterface;
11
use Symfony\Component\EventDispatcher\Event;
12
use Symfony\Component\HttpFoundation\Request;
13
use Symfony\Component\HttpFoundation\RequestStack;
14
use Webmozart\Assert\Assert;
15
16
final class AddPaginationLinksListener
17
{
18
    /**
19
     * @var RequestStack
20
     */
21
    private $requestStack;
22
23
    /**
24
     * @var EventHandlerInterface
25
     */
26
    private $eventHandler;
27
28
    /**
29
     * @var string
30
     */
31
    private $pageParameter;
32
33
    public function __construct(RequestStack $requestStack, EventHandlerInterface $eventHandler, string $pageParameter)
34
    {
35
        $this->requestStack = $requestStack;
36
        $this->eventHandler = $eventHandler;
37
        $this->pageParameter = $pageParameter;
38
    }
39
40
    public function handle(Event $event): void
41
    {
42
        if (!$this->eventHandler->supports($event)) {
43
            return;
44
        }
45
46
        $paginationParameters = $this->eventHandler->handle($event);
47
48
        $request = $paginationParameters->getRequest();
49
50
        // do not add prev/next links to ajax requests
51
        if ($request->isXmlHttpRequest()) {
52
            return;
53
        }
54
55
        // only add prev/next links if the request is the master request
56
        if ($request !== $this->requestStack->getMasterRequest()) {
57
            return;
58
        }
59
60
        $numberOfResults = $paginationParameters->getNumberOfResults();
61
        $pages = ceil($numberOfResults / $paginationParameters->getMaxPerPage());
62
63
        $links = [];
64
65
        // prev link
66
        if ($paginationParameters->getCurrentPage() > 1) {
67
            $links[] = new Link('prev', $this->getPageUrl($request, $paginationParameters->getCurrentPage() - 1));
68
        }
69
70
        // next link
71
        if ($paginationParameters->getCurrentPage() < $pages) {
72
            $links[] = new Link('next', $this->getPageUrl($request, $paginationParameters->getCurrentPage() + 1));
73
        }
74
75
        if (count($links) > 0) {
76
            /** @var EvolvableLinkProviderInterface $linkProvider */
77
            $linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
78
79
            foreach ($links as $link) {
80
                $linkProvider = $linkProvider->withLink($link);
81
            }
82
83
            $request->attributes->set('_links', $linkProvider);
84
        }
85
    }
86
87
    private function getPageUrl(Request $request, int $page): string
88
    {
89
        Assert::greaterThan($page, 0);
90
91
        $components = parse_url($request->getUri());
92
93
        $uri = $components['scheme'] . '://' . $components['host'];
94
        if (array_key_exists('port', $components)) {
95
            $uri .= $components['port'];
96
        }
97
        if (array_key_exists('path', $components)) {
98
            $uri .= $components['path'];
99
        }
100
101
        $queryComponents = [];
102
        if (array_key_exists('query', $components)) {
103
            parse_str($components['query'], $queryComponents);
104
        }
105
106
        if (1 === $page) {
107
            // strip page parameter
108
            unset($queryComponents[$this->pageParameter]);
109
        } else {
110
            $queryComponents[$this->pageParameter] = $page;
111
        }
112
113
        if (count($queryComponents) > 0) {
114
            $uri .= '?' . http_build_query($queryComponents, '', '&', PHP_QUERY_RFC3986);
115
        }
116
117
        return $uri;
118
    }
119
}
120