LinkSorter::createSorterParam()   A
last analyzed

Complexity

Conditions 6
Paths 18

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 25
ccs 14
cts 14
cp 1
rs 9.2222
c 0
b 0
f 0
cc 6
nc 18
nop 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\DataView;
6
7
use InvalidArgumentException;
8
use Yiisoft\Html\Html;
9
use Yiisoft\Html\Tag\A;
10
use Yiisoft\Html\Tag\I;
11
use Yiisoft\Router\CurrentRoute;
12
use Yiisoft\Router\UrlGeneratorInterface;
13
use Yiisoft\Widget\Widget;
14
15
use function array_merge;
16
use function http_build_query;
17
use function implode;
18
use function ucfirst;
19
20
final class LinkSorter extends Widget
21
{
22
    private string $attribute = '';
23
    private array $attributes = [];
24
    private int $currentPage = 0;
25
    private array $directions = [];
26
    private array $iconAttributes = [];
27
    private string $iconAsc = '&#x2191;';
28
    private string $iconAscClass = '';
29
    private string $iconDesc = '&#x2193;';
30
    private string $iconDescClass = '';
31
    private string $label = '';
32
    private array $linkAttributes = [];
33
    private array $pageConfig = [];
34
    private int $pageSize = 0;
35
    private string $pageName = 'page';
36
    private string $pageSizeName = 'pagesize';
37
    private array|null $urlArguments = null;
38
    private array $urlQueryParameters = [];
39
40 21
    public function __construct(
41
        private CurrentRoute $currentRoute,
42
        private UrlGeneratorInterface|null $urlGenerator = null
43
    ) {
44 21
    }
45
46
    /**
47
     * Returns a new instance with the attribute name for link sorting.
48
     *
49
     * @param string $value The value label for the link.
50
     */
51 21
    public function attribute(string $value): self
52
    {
53 21
        $new = clone $this;
54 21
        $new->attribute = $value;
55
56 21
        return $new;
57
    }
58
59
    /**
60
     * Returns a new instance with the attributes for the link sorting.
61
     *
62
     * @param array $values The attributes for the link sorting.
63
     */
64 21
    public function attributes(array $values): self
65
    {
66 21
        $new = clone $this;
67 21
        $new->attributes = $values;
68
69 21
        return $new;
70
    }
71
72
    /**
73
     * Return a new instance with current page of pagination.
74
     *
75
     * @param int $value Current page.
76
     */
77 19
    public function currentPage(int $value): self
78
    {
79 19
        $new = clone $this;
80 19
        $new->currentPage = $value;
81
82 19
        return $new;
83
    }
84
85
    /**
86
     * Returns a new instance with the currently requested sort information.
87
     *
88
     * @param array $values The currently requested sort information.
89
     */
90 18
    public function directions(array $values): self
91
    {
92 18
        $new = clone $this;
93 18
        $new->directions = $values;
94
95 18
        return $new;
96
    }
97
98
    /**
99
     * Returns a new instance with the icon text for the ascending sort direction.
100
     *
101
     * @param string $value The icon text for the ascending sort direction.
102
     */
103 1
    public function iconAsc(string $value): self
104
    {
105 1
        $new = clone $this;
106 1
        $new->iconAsc = $value;
107
108 1
        return $new;
109
    }
110
111
    /**
112
     * Returns a new instance with the icon text for the descending sort direction.
113
     *
114
     * @param string $value The icon text for the descending sort direction.
115
     */
116 1
    public function iconDesc(string $value): self
117
    {
118 1
        $new = clone $this;
119 1
        $new->iconDesc = $value;
120
121 1
        return $new;
122
    }
123
124
    /**
125
     * Returns a new instance with the CSS class for the ascending sort direction.
126
     *
127
     * @param string $value The CSS class for the ascending sort direction.
128
     */
129 10
    public function iconAscClass(string $value): self
130
    {
131 10
        $new = clone $this;
132 10
        $new->iconAsc = '';
133 10
        $new->iconAscClass = $value;
134
135 10
        return $new;
136
    }
137
138
    /**
139
     * Returns a new instance with the CSS class for the descending sort direction.
140
     *
141
     * @param string $value The CSS class for the descending sort direction.
142
     */
143 5
    public function iconDescClass(string $value): self
144
    {
145 5
        $new = clone $this;
146 5
        $new->iconDesc = '';
147 5
        $new->iconDescClass = $value;
148
149 5
        return $new;
150
    }
151
152
    /**
153
     * Returns a new instance with the label for attribute.
154
     *
155
     * @param string $value The label for attribute.
156
     */
157 4
    public function label(string $value): self
158
    {
159 4
        $new = clone $this;
160 4
        $new->label = $value;
161
162 4
        return $new;
163
    }
164
165
    /**
166
     * Returns a new instance with the HTML attributes for a tag `<a>`.
167
     *
168
     * @param array $values Attribute values indexed by attribute names.
169
     */
170 6
    public function linkAttributes(array $values): self
171
    {
172 6
        $new = clone $this;
173 6
        $new->linkAttributes = $values;
174
175 6
        return $new;
176
    }
177
178
    /**
179
     * Return a new instance with page config for arguments or query parameters in url.
180
     *
181
     * @param array $value The page config for arguments or query parameters in url.
182
     */
183 2
    public function pageConfig(array $value): self
184
    {
185 2
        $new = clone $this;
186 2
        $new->pageConfig = $value;
187
188 2
        return $new;
189
    }
190
191
    /**
192
     * Return a new instance with name of argument or query parameter for page.
193
     *
194
     * @param string $value The name of argument or query parameter for page.
195
     */
196 1
    public function pageName(string $value): self
197
    {
198 1
        $new = clone $this;
199 1
        $new->pageName = $value;
200
201 1
        return $new;
202
    }
203
204
    /**
205
     * Return a new instance with page size of pagination.
206
     *
207
     * @param int $value The page size of pagination.
208
     */
209 20
    public function pageSize(int $value): self
210
    {
211 20
        $new = clone $this;
212 20
        $new->pageSize = $value;
213
214 20
        return $new;
215
    }
216
217
    /**
218
     * Return a new instance with name of argument or query parameter for page size.
219
     *
220
     * @param string $value The name of argument or query parameter for page size.
221
     */
222 1
    public function pageSizeName(string $value): self
223
    {
224 1
        $new = clone $this;
225 1
        $new->pageSizeName = $value;
226
227 1
        return $new;
228
    }
229
230
    /**
231
     * Return a new instance with arguments of the route.
232
     *
233
     * @param array $value Arguments of the route.
234
     */
235 5
    public function urlArguments(array $value): self
236
    {
237 5
        $new = clone $this;
238 5
        $new->urlArguments = $value;
239
240 5
        return $new;
241
    }
242
243
    /**
244
     * Return a new instance with query parameters of the route.
245
     *
246
     * @param array $value The query parameters of the route.
247
     */
248 7
    public function urlQueryParameters(array $value): self
249
    {
250 7
        $new = clone $this;
251 7
        $new->urlQueryParameters = $value;
252
253 7
        return $new;
254
    }
255
256 20
    public function render(): string
257
    {
258 20
        return match (isset($this->attributes[$this->attribute])) {
259 20
            true => $this->renderSorterLink(),
260 20
            false => '',
261 20
        };
262
    }
263
264
    /**
265
     * Creates the sort variable for the specified attribute.
266
     *
267
     * The newly created sort variable can be used to create a URL that will lead to sorting by the specified attribute.
268
     *
269
     * @param string $attribute the attribute name.
270
     *
271
     * @throws InvalidArgumentException if the specified attribute is not defined.
272
     */
273 19
    private function createSorterParam(string $attribute): string
274
    {
275 19
        $attributes = $this->attributes;
276 19
        $directions = $this->directions;
277 19
        $direction = 'asc';
278
279 19
        if (isset($attributes['default'])) {
280
            /** @var string */
281 1
            $direction = $attributes['default'];
282
        }
283
284 19
        if (isset($directions[$attribute])) {
285 17
            $direction = $directions[$attribute] === 'desc' ? 'asc' : 'desc';
286 17
            unset($directions[$attribute]);
287
        }
288
289 19
        $directions = array_merge([$attribute => $direction], $directions);
290 19
        $sorts = [];
291
292
        /** @psalm-var array<string,string> $directions */
293 19
        foreach ($directions as $attribute => $direction) {
294 19
            $sorts[] = $direction === 'desc' ? '-' . $attribute : $attribute;
295
        }
296
297 19
        return implode(',', $sorts);
298
    }
299
300
    /**
301
     * Creates a URL for sorting the data by the specified attribute.
302
     *
303
     * This method will consider the current sorting status.
304
     *
305
     * For example, if the current page already sorts the data by the specified attribute in ascending order, then the
306
     * URL created will lead to a page that sorts the data by the specified attribute in descending order.
307
     *
308
     * @param string $attribute The attribute name.
309
     *
310
     * @throws InvalidArgumentException If the attribute is unknown.
311
     */
312 19
    private function createUrl(string $attribute): string
313
    {
314 19
        if ($this->urlGenerator === null) {
315 1
            throw new Exception\UrlGeneratorNotSetException();
316
        }
317
318 18
        $pageConfig = $this->pageConfig;
319 18
        $urlQueryParameters = $this->urlQueryParameters;
320
321 18
        if ($pageConfig === []) {
322 17
            $pageConfig = [$this->pageName => $this->currentPage, $this->pageSizeName => $this->pageSize];
323
        }
324
325 18
        if ($this->urlArguments !== null) {
326
            /** @psalm-var array<string,string> */
327 4
            $urlArguments = array_merge($this->urlArguments, $pageConfig);
328 4
            $urlArguments['sort'] = $this->createSorterParam($attribute);
329
        } else {
330 14
            $urlArguments = [];
331 14
            $urlQueryParameters = array_merge($urlQueryParameters, $pageConfig);
332 14
            $urlQueryParameters['sort'] = $this->createSorterParam($attribute);
333
        }
334
335 18
        $urlName = $this->currentRoute->getName();
336
337 18
        return match ($urlName) {
338 18
            null => $urlQueryParameters ? '?' . http_build_query($urlQueryParameters) : '',
339 18
            default => $this->urlGenerator->generate($urlName, $urlArguments, $urlQueryParameters),
0 ignored issues
show
Bug introduced by
It seems like $urlName can also be of type null; however, parameter $name of Yiisoft\Router\UrlGeneratorInterface::generate() does only seem to accept string, 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

339
            default => $this->urlGenerator->generate(/** @scrutinizer ignore-type */ $urlName, $urlArguments, $urlQueryParameters),
Loading history...
340 18
        };
341
    }
342
343 19
    private function renderLabel(
344
        string $label,
345
        string $icon,
346
        string $iconClass,
347
        array $iconAttributes = []
348
    ): string {
349 19
        $html = '';
350
351 19
        if ($iconClass !== '') {
352 10
            Html::addCssClass($iconAttributes, $iconClass);
353
        }
354
355 19
        if ($label !== '') {
356 19
            $html = Html::encode($label);
357
        }
358
359 19
        if ($icon !== '' || $iconClass !== '') {
360 17
            $html .= ' ' . I::tag()->attributes($iconAttributes)->content($icon)->encode(false)->render();
361
        }
362
363 19
        return $html;
364
    }
365
366
    /**
367
     * Generates a hyperlink that links to the sort urlName to sort by the specified attribute.
368
     *
369
     * Based on the sort direction, the CSS class of the generated hyperlink will be appended with "asc" or "desc".
370
     *
371
     * There is one special attribute `label` which will be used as the label of the hyperlink.
372
     *
373
     * If no label is defined, the attribute name will be used.
374
     *
375
     * @throws InvalidArgumentException If the attribute is unknown.
376
     */
377 19
    private function renderSorterLink(): string
378
    {
379 19
        $icon = '';
380 19
        $iconClass = '';
381 19
        $linkAttributes = $this->linkAttributes;
382 19
        $sorterClass = '';
383
384 19
        if (isset($this->directions[$this->attribute]) && $this->directions[$this->attribute] === 'desc') {
385 6
            $icon = $this->iconDesc;
386 6
            $iconClass = $this->iconDescClass;
387 6
            $sorterClass = 'desc';
388 13
        } elseif (isset($this->directions[$this->attribute])) {
389 11
            $icon = $this->iconAsc;
390 11
            $iconClass = $this->iconAscClass;
391 11
            $sorterClass = 'asc';
392
        }
393
394 19
        if ($sorterClass !== '') {
395 17
            Html::addCssClass($linkAttributes, $sorterClass);
396
        }
397
398 19
        $linkAttributes['data-sort'] = $this->createSorterParam($this->attribute);
399 19
        $label = $this->label !== '' ? $this->label : ucfirst($this->attribute);
400
401 19
        return A::tag()
402 19
            ->attributes($linkAttributes)
403 19
            ->content($this->renderLabel($label, $icon, $iconClass, $this->iconAttributes))
404 19
            ->encode(false)
405 19
            ->href($this->createUrl($this->attribute))
406 19
            ->render();
407
    }
408
}
409