Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.62% covered (success)
93.62%
44 / 47
90.91% covered (success)
90.91%
10 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
QuerySearch
93.62% covered (success)
93.62%
44 / 47
90.91% covered (success)
90.91%
10 / 11
24.15
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getApieContext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromCamelCaseArray
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 fromArray
85.71% covered (warning)
85.71%
18 / 21
0.00% covered (danger)
0.00%
0 / 1
7.14
 toHttpQuery
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 withPageIndex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPageIndex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getItemsPerPage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTextSearch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSearches
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOrderBy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\Core\Datalayers\Search;
3
4use Apie\Core\Attributes\Internal;
5use Apie\Core\Context\ApieContext;
6use Apie\Core\Lists\StringHashmap;
7use Apie\Core\ValueObjects\Utils;
8use Apie\DoctrineEntityDatalayer\Enums\SortingOrder;
9
10final class QuerySearch
11{
12    private const MAPPING = [
13        'pageIndex' => 'page_index',
14        'itemsPerPage' => 'items_per_page',
15        'textSearch' => 'search',
16        'searches' => 'query',
17        'orderBy' => 'order_by',
18    ];
19    private ?string $textSearch;
20
21    private StringHashmap $searches;
22
23    private StringHashmap $orderBy;
24
25    private ApieContext $apieContext;
26
27    public function __construct(
28        private int $pageIndex = 0,
29        private int $itemsPerPage = 20,
30        ?string $textSearch = null,
31        ?StringHashmap $searches = null,
32        ?StringHashmap $orderBy = null,
33        #[Internal]
34        ?ApieContext $apieContext = null,
35    ) {
36        $this->textSearch = $textSearch;
37        $this->searches = $searches ?? new StringHashmap();
38        $this->orderBy = $orderBy ?? new StringHashmap();
39        $this->apieContext = $apieContext ?? new ApieContext();
40    }
41
42    public function getApieContext(): ApieContext
43    {
44        return $this->apieContext;
45    }
46
47    /**
48     * @param array<string, string|int|array<string, mixed>> $input
49     */
50    public static function fromCamelCaseArray(array $input, ?ApieContext $apieContext = new ApieContext()): self
51    {
52        foreach (self::MAPPING as $keyName => $translatedKey) {
53            if (isset($input[$keyName])) {
54                $input[$translatedKey] = $input[$keyName];
55                unset($input[$keyName]);
56            }
57        }
58
59        return self::fromArray($input, $apieContext);
60    }
61
62    /**
63     * @param array<string, string|int|array<string, mixed>> $input
64     */
65    public static function fromArray(array $input, ?ApieContext $apieContext = new ApieContext()): self
66    {
67        $pageIndex = $input['page'] ?? 0;
68        $itemsPerPage = max($input['items_per_page'] ?? 20, 1);
69        $data = is_array($input['query'] ?? '') ? $input['query'] : [];
70        $orderBy = $input['order_by'] ?? [];
71        if (!is_array($orderBy) && !empty($orderBy)) {
72            $orderBy = explode(',', Utils::toString($orderBy));
73        }
74        $constructedOrderBy = [];
75        foreach ($orderBy as $column) {
76            if (str_starts_with($column, '+')) {
77                $constructedOrderBy[substr($column, 1)] = SortingOrder::ASCENDING->value;
78            } elseif (str_starts_with($column, '-')) {
79                $constructedOrderBy[substr($column, 1)] = SortingOrder::DESCENDING->value;
80            } else {
81                $constructedOrderBy[$column] = SortingOrder::ASCENDING->value;
82            }
83        }
84        return new QuerySearch(
85            $pageIndex,
86            $itemsPerPage,
87            $input['search'] ?? null,
88            new StringHashmap($data),
89            new StringHashmap($constructedOrderBy),
90            $apieContext
91        );
92    }
93
94    public function toHttpQuery(): string
95    {
96        $query = [];
97        if ($this->pageIndex > 0) {
98            $query['page'] = $this->pageIndex;
99        }
100        if ($this->itemsPerPage !== 20) {
101            $query['items_per_page'] = $this->itemsPerPage;
102        }
103        if ($this->textSearch !== null) {
104            $query['search'] = $this->textSearch;
105        }
106        if (0 !== $this->searches->count()) {
107            $query['query'] = $this->searches->toArray();
108        }
109        return empty($query) ? '' : '?' . http_build_query($query);
110    }
111
112    public function withPageIndex(int $pageIndex): self
113    {
114        return new self($pageIndex, $this->itemsPerPage, $this->textSearch, $this->searches);
115    }
116
117    public function getPageIndex(): int
118    {
119        return $this->pageIndex;
120    }
121
122    public function getItemsPerPage(): int
123    {
124        return $this->itemsPerPage;
125    }
126
127    public function getTextSearch(): ?string
128    {
129        return $this->textSearch;
130    }
131
132    public function getSearches(): StringHashmap
133    {
134        return $this->searches;
135    }
136
137    public function getOrderBy(): StringHashmap
138    {
139        return $this->orderBy;
140    }
141}