Completed
Push — master ( efb816...2d4e63 )
by Timo
02:40
created

SortableHeader::_buildSortUrl()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 8.439
c 0
b 0
f 0
cc 6
eloc 17
nc 12
nop 3
1
<?php
2
3
namespace hamburgscleanest\DataTables\Models\HeaderFormatters;
4
5
use hamburgscleanest\DataTables\Helpers\SessionHelper;
6
use hamburgscleanest\DataTables\Helpers\UrlHelper;
7
use hamburgscleanest\DataTables\Interfaces\HeaderFormatter;
8
use hamburgscleanest\DataTables\Models\Header;
9
use Illuminate\Http\Request;
10
11
/**
12
 * Class SortableHeader
13
 *
14
 * Either whitelist the headers you want to make sortable
15
 * or blacklist the headers you do not want to become sortable.
16
 *
17
 * If nothing is specified, all headers are sortable.
18
 *
19
 * @package hamburgscleanest\DataTables\Models\HeaderFormatters
20
 */
21
class SortableHeader implements HeaderFormatter {
22
23
    const SORTING_SEPARATOR = '~';
24
    const COLUMN_SEPARATOR  = '.';
25
26
    /** @var array */
27
    private $_sortableHeaders;
28
29
    /** @var array */
30
    private $_dontSort;
31
32
    /** @var array */
33
    private $_sortingSymbols = ['asc' => '&#x25B2;', 'desc' => '&#x25BC;', 'none' => '⇵'];
34
35
    /**
36
     * SortableHeader constructor.
37
     *
38
     * @param array $sortableHeaders
39
     * @param array $dontSort
40
     */
41
    public function __construct(array $sortableHeaders = [], array $dontSort = [])
42
    {
43
        $this->_sortableHeaders = $sortableHeaders;
44
        $this->_dontSort = $dontSort;
45
46
    }
47
48
    /**
49
     * @param array $sortingSymbols
50
     * @return $this
51
     */
52
    public function sortingSymbols(array $sortingSymbols)
53
    {
54
        $this->_sortingSymbols = $sortingSymbols;
55
56
        return $this;
57
    }
58
59
    /**
60
     * @param array $array
61
     * @param string $key
62
     */
63
    private function _removeIndex(array $array, string $key)
64
    {
65
        $index = \array_search($key, $array, true);
66
        if ($index !== false)
67
        {
68
            unset($array[$index]);
69
        }
70
    }
71
72
    /**
73
     * Add a field to the sortable fields.
74
     *
75
     * @param string $header
76
     * @return $this
77
     */
78
    public function makeSortable(string $header)
79
    {
80
        $this->_sortableHeaders[] = $header;
81
        $this->_removeIndex($this->_dontSort, $header);
82
83
        return $this;
84
    }
85
86
    /**
87
     * Remove the ability to sort by this column/header.
88
     *
89
     * @param string $header
90
     * @return $this
91
     */
92
    public function dontSort(string $header)
93
    {
94
        $this->_dontSort[] = $header;
95
        $this->_removeIndex($this->_sortableHeaders, $header);
96
97
        return $this;
98
    }
99
100
    /**
101
     * @param Request $request
102
     * @return array
103
     */
104
    private function _extractSortFields(Request $request)
105
    {
106
        $sorting = SessionHelper::getState($request, 'sort', []);
107
108
        $sortFields = $request->get('sort');
109
        if (empty($sortFields))
110
        {
111
            return $sorting;
112
        }
113
114
        foreach (\explode(self::COLUMN_SEPARATOR, $sortFields) as $field)
115
        {
116
            $sortParts = \explode(self::SORTING_SEPARATOR, $field);
117
            if (\count($sortParts) === 1)
118
            {
119
                $sortParts[1] = 'asc';
120
            }
121
122
            $sorting[$sortParts[0]] = \mb_strtolower($sortParts[1]);
123
        }
124
125
        return $sorting;
126
    }
127
128
    /**
129
     * Get the next sorting direction.
130
     *
131
     * @param string $oldDirection
132
     * @return string
133
     */
134
    private function _getNewDirection(string $oldDirection): string
135
    {
136
        switch ($oldDirection)
137
        {
138
            case 'asc':
139
                $newDirection = 'desc';
140
                break;
141
            case 'desc':
142
                $newDirection = 'none';
143
                break;
144
            default:
145
                $newDirection = 'asc';
146
        }
147
148
        return $newDirection;
149
    }
150
151
    /**
152
     * @param Request $request
153
     * @param string $column
154
     * @param string $oldDirection
155
     * @return string
156
     * @throws \RuntimeException
157
     */
158
    private function _buildSortUrl(Request $request, string $column, string $oldDirection = 'asc')
159
    {
160
        $newDirection = $this->_getNewDirection($oldDirection);
161
162
        $newSorting = $column . self::SORTING_SEPARATOR . $newDirection;
163
        $parameters = UrlHelper::parameterizeQuery($request->getQueryString());
164
165
        if (!isset($parameters['sort']))
166
        {
167
            $parameters['sort'] = '';
168
        }
169
170
        $columnRegex = '/(^|\\' . self::COLUMN_SEPARATOR . ')' . $column . self::SORTING_SEPARATOR . $oldDirection . '/';
171
        $replacedCount = 0;
172
        $parameters['sort'] = \preg_replace($columnRegex, self::COLUMN_SEPARATOR . $newSorting, $parameters['sort'], 1, $replacedCount);
173
174
        if (!empty($parameters['sort']) && $parameters['sort'][0] === self::COLUMN_SEPARATOR)
175
        {
176
            $parameters['sort'] = \mb_substr($parameters['sort'], 1);
177
        }
178
179
        if ($replacedCount === 0)
180
        {
181
            $sorting = $newSorting;
182
            if (!empty($parameters['sort']))
183
            {
184
                $sorting = self::COLUMN_SEPARATOR . $sorting;
185
            }
186
            $parameters['sort'] .= $sorting;
187
        }
188
189
        return $request->url() . '?' . \http_build_query($parameters);
190
    }
191
192
    /**
193
     * Adds a link to sort by this header/column.
194
     * Also indicates how the columns are sorted (when sorted).
195
     *
196
     * @param Header $header
197
     * @param Request $request
198
     * @throws \RuntimeException
199
     */
200
    public function format(Header $header, Request $request)
201
    {
202
        $headerAttributeName = $header->getOriginalName();
203
204
        $sortFields = $this->_extractSortFields($request);
205
        $direction = $sortFields[$headerAttributeName] ?? 'none';
206
        $header->name .= ' <span class="sort-symbol">' . ($this->_sortingSymbols[$direction] ?? '') . '</span>';
207
208
        if (\count($this->_sortableHeaders) === 0 || \in_array($headerAttributeName, $this->_sortableHeaders, true))
209
        {
210
            $header->name = '<a class="sortable-header" href="' . $this->_buildSortUrl($request, $headerAttributeName, $direction) . '">' . $header->name . '</a>';
211
        }
212
    }
213
}