6
6
7
7
class SearchOptions
8
8
{
9
- public array $ searches = [];
10
- public array $ exacts = [];
11
- public array $ tags = [];
12
- public array $ filters = [];
9
+ public SearchOptionSet $ searches ;
10
+ public SearchOptionSet $ exacts ;
11
+ public SearchOptionSet $ tags ;
12
+ public SearchOptionSet $ filters ;
13
+
14
+ public function __construct ()
15
+ {
16
+ $ this ->searches = new SearchOptionSet ();
17
+ $ this ->exacts = new SearchOptionSet ();
18
+ $ this ->tags = new SearchOptionSet ();
19
+ $ this ->filters = new SearchOptionSet ();
20
+ }
13
21
14
22
/**
15
23
* Create a new instance from a search string.
16
24
*/
17
25
public static function fromString (string $ search ): self
18
26
{
19
- $ decoded = static ::decode ($ search );
20
- $ instance = new SearchOptions ();
21
- foreach ($ decoded as $ type => $ value ) {
22
- $ instance ->$ type = $ value ;
23
- }
24
-
27
+ $ instance = new self ();
28
+ $ instance ->addOptionsFromString ($ search );
25
29
return $ instance ;
26
30
}
27
31
@@ -44,34 +48,37 @@ public static function fromRequest(Request $request): self
44
48
$ inputs = $ request ->only (['search ' , 'types ' , 'filters ' , 'exact ' , 'tags ' ]);
45
49
46
50
$ parsedStandardTerms = static ::parseStandardTermString ($ inputs ['search ' ] ?? '' );
47
- $ instance ->searches = array_filter ($ parsedStandardTerms ['terms ' ]);
48
- $ instance ->exacts = array_filter ($ parsedStandardTerms ['exacts ' ]);
49
-
50
- array_push ($ instance ->exacts , ...array_filter ($ inputs ['exact ' ] ?? []));
51
-
52
- $ instance ->tags = array_filter ($ inputs ['tags ' ] ?? []);
51
+ $ inputExacts = array_filter ($ inputs ['exact ' ] ?? []);
52
+ $ instance ->searches = SearchOptionSet::fromValueArray (array_filter ($ parsedStandardTerms ['terms ' ]));
53
+ $ instance ->exacts = SearchOptionSet::fromValueArray (array_filter ($ parsedStandardTerms ['exacts ' ]));
54
+ $ instance ->exacts = $ instance ->exacts ->merge (SearchOptionSet::fromValueArray ($ inputExacts ));
55
+ $ instance ->tags = SearchOptionSet::fromValueArray (array_filter ($ inputs ['tags ' ] ?? []));
53
56
57
+ $ keyedFilters = [];
54
58
foreach (($ inputs ['filters ' ] ?? []) as $ filterKey => $ filterVal ) {
55
59
if (empty ($ filterVal )) {
56
60
continue ;
57
61
}
58
- $ instance ->filters [$ filterKey ] = $ filterVal === 'true ' ? '' : $ filterVal ;
62
+ $ cleanedFilterVal = $ filterVal === 'true ' ? '' : $ filterVal ;
63
+ $ keyedFilters [$ filterKey ] = new SearchOption ($ cleanedFilterVal );
59
64
}
60
65
61
66
if (isset ($ inputs ['types ' ]) && count ($ inputs ['types ' ]) < 4 ) {
62
- $ instance -> filters ['type ' ] = implode ('| ' , $ inputs ['types ' ]);
67
+ $ keyedFilters ['type ' ] = new SearchOption ( implode ('| ' , $ inputs ['types ' ]) );
63
68
}
64
69
70
+ $ instance ->filters = new SearchOptionSet ($ keyedFilters );
71
+
65
72
return $ instance ;
66
73
}
67
74
68
75
/**
69
- * Decode a search string into an array of terms .
76
+ * Decode a search string and add its contents to this instance .
70
77
*/
71
- protected static function decode (string $ searchString ): array
78
+ protected function addOptionsFromString (string $ searchString ): void
72
79
{
80
+ /** @var array<string, string[]> $terms */
73
81
$ terms = [
74
- 'searches ' => [],
75
82
'exacts ' => [],
76
83
'tags ' => [],
77
84
'filters ' => [],
@@ -94,28 +101,30 @@ protected static function decode(string $searchString): array
94
101
}
95
102
96
103
// Unescape exacts and backslash escapes
97
- foreach ($ terms ['exacts ' ] as $ index => $ exact ) {
98
- $ terms ['exacts ' ][$ index ] = static ::decodeEscapes ($ exact );
99
- }
104
+ $ escapedExacts = array_map (fn (string $ term ) => static ::decodeEscapes ($ term ), $ terms ['exacts ' ]);
100
105
101
106
// Parse standard terms
102
107
$ parsedStandardTerms = static ::parseStandardTermString ($ searchString );
103
- array_push ($ terms ['searches ' ], ...$ parsedStandardTerms ['terms ' ]);
104
- array_push ($ terms ['exacts ' ], ...$ parsedStandardTerms ['exacts ' ]);
108
+ $ this ->searches = $ this ->searches
109
+ ->merge (SearchOptionSet::fromValueArray ($ parsedStandardTerms ['terms ' ]))
110
+ ->filterEmpty ();
111
+ $ this ->exacts = $ this ->exacts
112
+ ->merge (SearchOptionSet::fromValueArray ($ escapedExacts ))
113
+ ->merge (SearchOptionSet::fromValueArray ($ parsedStandardTerms ['exacts ' ]))
114
+ ->filterEmpty ();
115
+
116
+ // Add tags
117
+ $ this ->tags = $ this ->tags ->merge (SearchOptionSet::fromValueArray ($ terms ['tags ' ]));
105
118
106
119
// Split filter values out
120
+ /** @var array<string, SearchOption> $splitFilters */
107
121
$ splitFilters = [];
108
122
foreach ($ terms ['filters ' ] as $ filter ) {
109
123
$ explodedFilter = explode (': ' , $ filter , 2 );
110
- $ splitFilters [$ explodedFilter [0 ]] = (count ($ explodedFilter ) > 1 ) ? $ explodedFilter [1 ] : '' ;
124
+ $ filterValue = (count ($ explodedFilter ) > 1 ) ? $ explodedFilter [1 ] : '' ;
125
+ $ splitFilters [$ explodedFilter [0 ]] = new SearchOption ($ filterValue );
111
126
}
112
- $ terms ['filters ' ] = $ splitFilters ;
113
-
114
- // Filter down terms where required
115
- $ terms ['exacts ' ] = array_filter ($ terms ['exacts ' ]);
116
- $ terms ['searches ' ] = array_filter ($ terms ['searches ' ]);
117
-
118
- return $ terms ;
127
+ $ this ->filters = $ this ->filters ->merge (new SearchOptionSet ($ splitFilters ));
119
128
}
120
129
121
130
/**
@@ -175,30 +184,57 @@ protected static function parseStandardTermString(string $termString): array
175
184
*/
176
185
public function setFilter (string $ filterName , string $ filterValue = '' ): void
177
186
{
178
- $ this ->filters [$ filterName ] = $ filterValue ;
187
+ $ this ->filters = $ this ->filters ->merge (
188
+ new SearchOptionSet ([$ filterName => new SearchOption ($ filterValue )])
189
+ );
179
190
}
180
191
181
192
/**
182
193
* Encode this instance to a search string.
183
194
*/
184
195
public function toString (): string
185
196
{
186
- $ parts = $ this ->searches ;
197
+ $ parts = $ this ->searches -> toValueArray () ;
187
198
188
- foreach ($ this ->exacts as $ term ) {
199
+ foreach ($ this ->exacts -> toValueArray () as $ term ) {
189
200
$ escaped = str_replace ('\\' , '\\\\' , $ term );
190
201
$ escaped = str_replace ('" ' , '\" ' , $ escaped );
191
202
$ parts [] = '" ' . $ escaped . '" ' ;
192
203
}
193
204
194
- foreach ($ this ->tags as $ term ) {
205
+ foreach ($ this ->tags -> toValueArray () as $ term ) {
195
206
$ parts [] = "[ {$ term }] " ;
196
207
}
197
208
198
- foreach ($ this ->filters as $ filterName => $ filterVal ) {
209
+ foreach ($ this ->filters -> toValueMap () as $ filterName => $ filterVal ) {
199
210
$ parts [] = '{ ' . $ filterName . ($ filterVal ? ': ' . $ filterVal : '' ) . '} ' ;
200
211
}
201
212
202
213
return implode (' ' , $ parts );
203
214
}
215
+
216
+ /**
217
+ * Get the search options that don't have UI controls provided for.
218
+ * Provided back as a key => value array with the keys being expected
219
+ * input names for a search form, and values being the option value.
220
+ *
221
+ * @return array<string, string>
222
+ */
223
+ public function getHiddenInputValuesByFieldName (): array
224
+ {
225
+ $ options = [];
226
+
227
+ // Non-[created/updated]-by-me options
228
+ $ filterMap = $ this ->filters ->toValueMap ();
229
+ foreach (['updated_by ' , 'created_by ' , 'owned_by ' ] as $ filter ) {
230
+ $ value = $ filterMap [$ filter ] ?? null ;
231
+ if ($ value !== null && $ value !== 'me ' ) {
232
+ $ options ["filters[ $ filter] " ] = $ value ;
233
+ }
234
+ }
235
+
236
+ // TODO - Negated
237
+
238
+ return $ options ;
239
+ }
204
240
}
0 commit comments