Passed
Push — master ( ea67ba...517615 )
by Nico
13:18
created

FieldValidatorVisitor   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 79
dl 0
loc 217
ccs 113
cts 113
cp 1
rs 9.0399
c 0
b 0
f 0
wmc 42

27 Methods

Rating   Name   Duplication   Size   Complexity  
A check() 0 3 1
A visitSimpleValueWrapper() 0 3 1
A visitFieldBody() 0 3 1
A visitAndOperand() 0 3 1
A visitFieldAttachment() 0 3 1
A visitFieldAuthor() 0 3 1
A visitOrExpression() 0 3 1
A visitExpression() 0 6 1
A visitOrOperand() 0 3 1
A visitNotExpression() 0 5 1
A visitOperand() 0 6 1
A visitAndExpression() 0 3 1
A visitTail() 0 6 2
A visitField() 0 6 1
A mergeHeadTail() 0 5 1
B visitTimestampField() 0 37 10
A visitDateField() 0 12 3
A visitFieldCategory() 0 3 1
A visitFieldGroup() 0 13 2
A visitFieldTitle() 0 3 1
A visitFieldVisibility() 0 12 2
A visitFieldId() 0 3 1
A visitFieldLocked() 0 3 1
A visitFieldRating() 0 3 1
A visitFieldStatus() 0 3 1
A visitFieldTimestamped() 0 6 2
A visitFieldElabid() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like FieldValidatorVisitor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FieldValidatorVisitor, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
/**
3
 * @author Nicolas CARPi <[email protected]>
4
 * @author Marcel Bolten <[email protected]>
5
 * @copyright 2022 Nicolas CARPi
6
 * @see https://www.elabftw.net Official website
7
 * @license AGPL-3.0
8
 * @package elabftw
9
 */
10
11
namespace Elabftw\Services\AdvancedSearchQuery\Visitors;
12
13
use Elabftw\Services\AdvancedSearchQuery\Collectors\InvalidFieldCollector;
14
use Elabftw\Services\AdvancedSearchQuery\Enums\TimestampFields;
15
use Elabftw\Services\AdvancedSearchQuery\Grammar\AndExpression;
16
use Elabftw\Services\AdvancedSearchQuery\Grammar\AndOperand;
17
use Elabftw\Services\AdvancedSearchQuery\Grammar\DateField;
18
use Elabftw\Services\AdvancedSearchQuery\Grammar\Field;
19
use Elabftw\Services\AdvancedSearchQuery\Grammar\NotExpression;
20
use Elabftw\Services\AdvancedSearchQuery\Grammar\OrExpression;
21
use Elabftw\Services\AdvancedSearchQuery\Grammar\OrOperand;
22
use Elabftw\Services\AdvancedSearchQuery\Grammar\SimpleValueWrapper;
23
use Elabftw\Services\AdvancedSearchQuery\Grammar\TimestampField;
24
use Elabftw\Services\AdvancedSearchQuery\Interfaces\Visitable;
25
use Elabftw\Services\AdvancedSearchQuery\Interfaces\Visitor;
26
use function sprintf;
27
use function ucfirst;
28
29
/** @psalm-suppress UnusedParam */
30
class FieldValidatorVisitor implements Visitor
31
{
32 2
    public function check(Visitable $parsedQuery, VisitorParameters $parameters): array
33
    {
34 2
        return $parsedQuery->accept($this, $parameters)->getfieldErrors();
35
    }
36
37 1
    public function visitSimpleValueWrapper(SimpleValueWrapper $simpleValueWrapper, VisitorParameters $parameters): InvalidFieldCollector
38
    {
39 1
        return new InvalidFieldCollector();
40
    }
41
42 2
    public function visitDateField(DateField $dateField, VisitorParameters $parameters): InvalidFieldCollector
43
    {
44 2
        if ($dateField->getDateType() === 'range' && $dateField->getValue() > $dateField->getDateTo()) {
45 1
            $message = sprintf(
46 1
                'date:%s..%s. Second date needs to be equal or greater than first date.',
47 1
                $dateField->getValue(),
48 1
                $dateField->getDateTo(),
49 1
            );
50 1
            return new InvalidFieldCollector(array($message));
51
        }
52
53 1
        return new InvalidFieldCollector();
54
    }
55
56 2
    public function visitTimestampField(TimestampField $timestampField, VisitorParameters $parameters): InvalidFieldCollector
57
    {
58 2
        $messages = array();
59
60 2
        if ($parameters->getEntityType() !== 'experiments'
61 2
            && $timestampField->getFieldType() === TimestampFields::TimestampedAt
62
        ) {
63 1
            $messages[] = sprintf(
64 1
                '%s: is only allowed when searching in experiments.',
65 1
                TimestampFields::TimestampedAt->value,
66 1
            );
67
        }
68
69
        // MySQL range for TIMESTAMP values is '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.999999'
70
        // We use 1970-01-02 and 2038-01-18 because time 00:00:00 and/or 23:59:59 will be added
71 2
        $DateMin = 19700102;
72 2
        $DateMax = 20380118;
73
74 2
        if ((intval($timestampField->getValue(), 10) < $DateMin) || (intval($timestampField->getValue(), 10) > $DateMax)
75 2
            || ($timestampField->getDateType() === 'range' && ((intval($timestampField->getDateTo(), 10) < $DateMin) || (intval($timestampField->getDateTo(), 10) > $DateMax)))
76
        ) {
77 1
            $messages[] = sprintf(
78 1
                '%s: Date needs to be between 1970-01-02 and 2038-01-18.',
79 1
                $timestampField->getFieldType()->value,
80 1
            );
81
        }
82
83 2
        if ($timestampField->getDateType() === 'range' && $timestampField->getValue() > $timestampField->getDateTo()) {
84 1
            $messages[] = sprintf(
85 1
                '%s:%s..%s. Second date needs to be equal or greater than first date.',
86 1
                $timestampField->getFieldType()->value,
87 1
                $timestampField->getValue(),
88 1
                $timestampField->getDateTo(),
89 1
            );
90
        }
91
92 2
        return new InvalidFieldCollector($messages);
93
    }
94
95 2
    public function visitField(Field $field, VisitorParameters $parameters): InvalidFieldCollector
96
    {
97
        // Call class methods dynamically to avoid many if statements.
98
        // This works because the parser and the Fields enum define the list of fields.
99 2
        $method = 'visitField' . ucfirst($field->getFieldType()->value);
100 2
        return $this->$method($field->getValue(), $field->getAffix(), $parameters);
101
    }
102
103 1
    public function visitNotExpression(NotExpression $notExpression, VisitorParameters $parameters): InvalidFieldCollector
104
    {
105 1
        $invalidFieldCollectorExpression = $notExpression->getExpression()->accept($this, $parameters);
106
107 1
        return new InvalidFieldCollector($invalidFieldCollectorExpression->getfieldErrors());
108
    }
109
110 2
    public function visitAndExpression(AndExpression $andExpression, VisitorParameters $parameters): InvalidFieldCollector
111
    {
112 2
        return $this->visitExpression($andExpression, $parameters);
113
    }
114
115 2
    public function visitOrExpression(OrExpression $orExpression, VisitorParameters $parameters): InvalidFieldCollector
116
    {
117 2
        return $this->visitExpression($orExpression, $parameters);
118
    }
119
120 1
    public function visitOrOperand(OrOperand $orOperand, VisitorParameters $parameters): InvalidFieldCollector
121
    {
122 1
        return $this->visitOperand($orOperand, $parameters);
123
    }
124
125 2
    public function visitAndOperand(AndOperand $andOperand, VisitorParameters $parameters): InvalidFieldCollector
126
    {
127 2
        return $this->visitOperand($andOperand, $parameters);
128
    }
129
130 2
    private function visitTail(null | OrExpression | AndExpression | OrOperand | AndOperand $tail, VisitorParameters $parameters): InvalidFieldCollector
131
    {
132 2
        if ($tail) {
133 2
            return $tail->accept($this, $parameters);
134
        }
135 2
        return new InvalidFieldCollector();
136
    }
137
138 2
    private function visitOperand(OrOperand | AndOperand $operand, VisitorParameters $parameters): InvalidFieldCollector
139
    {
140 2
        $head = $operand->getOperand()->accept($this, $parameters);
141 2
        $tail = $this->visitTail($operand->getTail(), $parameters);
142
143 2
        return $this->mergeHeadTail($head, $tail);
144
    }
145
146 2
    private function visitExpression(OrExpression | AndExpression $expression, VisitorParameters $parameters): InvalidFieldCollector
147
    {
148 2
        $head = $expression->getExpression()->accept($this, $parameters);
149 2
        $tail = $this->visitTail($expression->getTail(), $parameters);
150
151 2
        return $this->mergeHeadTail($head, $tail);
152
    }
153
154 2
    private function mergeHeadTail(InvalidFieldCollector $head, InvalidFieldCollector $tail): InvalidFieldCollector
155
    {
156 2
        return new InvalidFieldCollector(array_merge(
157 2
            $head->getfieldErrors(),
158 2
            $tail->getfieldErrors(),
159 2
        ));
160
    }
161
162 1
    private function visitFieldAttachment(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
163
    {
164 1
        return new InvalidFieldCollector();
165
    }
166
167 1
    private function visitFieldAuthor(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
168
    {
169 1
        return new InvalidFieldCollector();
170
    }
171
172 1
    private function visitFieldBody(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
173
    {
174 1
        return new InvalidFieldCollector();
175
    }
176
177 2
    private function visitFieldCategory(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
178
    {
179 2
        return new InvalidFieldCollector();
180
    }
181
182 1
    private function visitFieldElabid(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
183
    {
184 1
        return new InvalidFieldCollector();
185
    }
186
187 2
    private function visitFieldGroup(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
188
    {
189 2
        $teamGroups = $parameters->getTeamGroups();
190 2
        $groupNames = array_column($teamGroups, 'name');
191 2
        if (!in_array($searchTerm, $groupNames, true)) {
192 1
            $message = sprintf(
193 1
                'group:%s. Valid values are %s.',
194 1
                $searchTerm,
195 1
                implode(', ', $groupNames),
196 1
            );
197 1
            return new InvalidFieldCollector(array($message));
198
        }
199 1
        return new InvalidFieldCollector();
200
    }
201
202 1
    private function visitFieldId(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
203
    {
204 1
        return new InvalidFieldCollector();
205
    }
206
207 1
    private function visitFieldLocked(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
208
    {
209 1
        return new InvalidFieldCollector();
210
    }
211
212 1
    private function visitFieldRating(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
213
    {
214 1
        return new InvalidFieldCollector();
215
    }
216
217 2
    private function visitFieldStatus(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
218
    {
219 2
        return new InvalidFieldCollector();
220
    }
221
222 2
    private function visitFieldTimestamped(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
223
    {
224 2
        if ($parameters->getEntityType() !== 'experiments') {
225 1
            return new InvalidFieldCollector(array('timestamped: is only allowed when searching in experiments.'));
226
        }
227 1
        return new InvalidFieldCollector();
228
    }
229
230 1
    private function visitFieldTitle(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
231
    {
232 1
        return new InvalidFieldCollector();
233
    }
234
235 2
    private function visitFieldVisibility(string $searchTerm, string $affix, VisitorParameters $parameters): InvalidFieldCollector
236
    {
237 2
        $visibilityFieldHelper = new VisibilityFieldHelper($searchTerm);
238 2
        if (!$visibilityFieldHelper->getArr()) {
239 1
            $message = sprintf(
240 1
                'visibility:%s. Valid values are %s.',
241 1
                $searchTerm,
242 1
                $visibilityFieldHelper->possibleInput,
243 1
            );
244 1
            return new InvalidFieldCollector(array($message));
245
        }
246 1
        return new InvalidFieldCollector();
247
    }
248
}
249