SortParameter   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 47
c 1
b 0
f 0
dl 0
loc 151
rs 10
wmc 15

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A transformSortableColumns() 0 34 3
A fail() 0 4 1
A message() 0 3 1
A takeNextSortRule() 0 14 2
B passes() 0 37 7
1
<?php
2
3
namespace musa11971\SortRequest\Rules;
4
5
use Illuminate\Contracts\Validation\Rule;
6
use musa11971\SortRequest\Sorters\DefaultSorter;
7
use musa11971\SortRequest\SortableColumn;
8
use musa11971\SortRequest\SortableColumnCollection;
9
use musa11971\SortRequest\Support\Foundation\Contracts\Sorter;
10
11
class SortParameter implements Rule
12
{
13
    const SORT_PATTERN = "~(?'column'[a-zA-Z0-9-_]*)\((?'direction'[a-zA-Z0-9-_]*)\)~";
14
15
    /** @var string $failure */
16
    private $failure = 'The :attribute is invalid.';
17
18
    /** @var SortableColumnCollection $sortableColumns */
19
    public $sortableColumns;
20
21
    /** @var array $sortingRules */
22
    public $sortingRules = [];
23
24
    /**
25
     * @param array $sortableColumns
26
     */
27
    public function __construct($sortableColumns)
28
    {
29
        $this->sortableColumns = $this->transformSortableColumns($sortableColumns);
30
    }
31
32
    /**
33
     * Determine if the validation rule passes.
34
     *
35
     * @param  string  $attribute
36
     * @param  mixed  $value
37
     * @return bool
38
     */
39
    public function passes($attribute, $value)
40
    {
41
        // Go through the value and take out all the rules
42
        $rules = [];
43
44
        while($rule = $this->takeNextSortRule($value)) {
45
            $rules[] = $rule;
46
        }
47
48
        // Fail the validation if no rules were passed
49
        if(!count($rules))
50
            return $this->fail('The :attribute contains no valid sorting rules.');
51
52
        foreach($rules as $rule) {
53
            $column = $rule['column'];
54
            $direction = $rule['direction'];
55
56
            // Validate that the column can be sorted on
57
            if(!$this->sortableColumns->isValidColumn($column))
58
                return $this->fail("'{$column}' cannot be sorted on.");
59
60
            // Validate that the direction is valid
61
            if(!$this->sortableColumns->isValidDirectionForColumn($column, $direction))
62
                return $this->fail("'{$direction}' is an invalid sorting direction for '{$column}'");
63
64
            // Validate whether the column is not present more than once
65
            $doubleRule = array_filter($rules, function($rule) use($column) {
66
                return ($rule['column'] == $column);
67
            });
68
69
            if(count($doubleRule) > 1)
70
                return $this->fail("'{$direction}' should only have one sort rule.");
71
        }
72
73
        $this->sortingRules = $rules;
74
75
        return true;
76
    }
77
78
    /**
79
     * Makes the validation rule fail and sets the failure message.
80
     *
81
     * @param $failure
82
     * @return bool
83
     */
84
    private function fail($failure) {
85
        $this->failure = $failure;
86
87
        return false;
88
    }
89
90
    /**
91
     * Get the validation error message.
92
     *
93
     * @return string
94
     */
95
    public function message()
96
    {
97
        return $this->failure;
98
    }
99
100
    /**
101
     * Attempts to find and return the next sort rule in a subject string.
102
     *
103
     * @param $subject
104
     * @return array|null
105
     */
106
    private function takeNextSortRule(&$subject)
107
    {
108
        if(preg_match(self::SORT_PATTERN, $subject, $match))
109
        {
110
            // Remove the match from the string
111
            $subject = preg_replace(self::SORT_PATTERN, '', $subject, 1);
112
113
            return [
114
                'column'    => $match['column'],
115
                'direction' => $match['direction']
116
            ];
117
        }
118
119
        return null;
120
    }
121
122
    /**
123
     * Transforms the format used in form requests to a SortableColumnCollection.
124
     *
125
     * @param array $columns
126
     * @return SortableColumnCollection
127
     */
128
    private function transformSortableColumns($columns)
129
    {
130
        $collection = new SortableColumnCollection();
131
132
        foreach($columns as $left => $right) {
133
            // ['columnName' => Sorter::class]; notation
134
            if (is_string($left)) {
135
                // Redefine variables for clarity
136
                $columnName = $left;
137
                $sortClass = $right;
138
139
                // Create the sorter
140
                /** @var Sorter $sorter */
141
                $sorter = new $sortClass;
142
                $sorter->column = $columnName;
143
144
                // Add the SortableColumn
145
                $collection->add(new SortableColumn($columnName, $sorter));
146
            }
147
            // ['columnName']; notation
148
            else {
149
                // Redefine variables for clarity
150
                $columnName = $right;
151
152
                // Create the default sorter
153
                $sorter = new DefaultSorter();
154
                $sorter->column = $columnName;
155
156
                // Add the SortableColumn
157
                $collection->add(new SortableColumn($columnName, $sorter));
158
            }
159
        }
160
161
        return $collection;
162
    }
163
}