@@ -55,6 +55,7 @@ class SearchFilter extends AbstractFilter
55
55
private $ requestStack ;
56
56
private $ iriConverter ;
57
57
private $ propertyAccessor ;
58
+ private $ caseSensitive ;
58
59
59
60
/**
60
61
* @param ManagerRegistry $managerRegistry
@@ -112,57 +113,66 @@ public function apply(QueryBuilder $queryBuilder, string $resourceClass, string
112
113
$ metadata = $ this ->getClassMetadata ($ resourceClass );
113
114
}
114
115
115
- if ($ metadata ->hasField ($ field )) {
116
- $ values = $ this ->normalizeValues ((array ) $ value );
116
+ $ values = $ this ->normalizeValues ((array ) $ value );
117
117
118
- if (empty ($ values )) {
119
- continue ;
120
- }
118
+ if (empty ($ values )) {
119
+ continue ;
120
+ }
121
121
122
+ $ this ->caseSensitive = true ;
123
+
124
+ if ($ metadata ->hasField ($ field )) {
122
125
if ('id ' === $ field ) {
123
126
$ values = array_map ([$ this , 'getFilterValueFromUrl ' ], $ values );
124
127
}
125
128
126
129
$ strategy = $ this ->properties [$ property ] ?? self ::STRATEGY_EXACT ;
127
130
131
+ // prefixing the strategy with i makes it case insensitive
132
+ if (strpos ($ strategy , 'i ' ) === 0 ) {
133
+ $ strategy = substr ($ strategy , 1 );
134
+ $ this ->caseSensitive = false ;
135
+ }
136
+
128
137
if (1 === count ($ values )) {
129
138
$ this ->addWhereByStrategy ($ strategy , $ queryBuilder , $ alias , $ field , $ values [0 ]);
130
- } else {
131
- if (self ::STRATEGY_EXACT !== $ strategy ) {
132
- continue ;
133
- }
134
-
135
- $ valueParameter = QueryNameGenerator::generateParameterName ($ field );
136
-
137
- $ queryBuilder
138
- ->andWhere (sprintf ('%s.%s IN (:%s) ' , $ alias , $ field , $ valueParameter ))
139
- ->setParameter ($ valueParameter , $ values );
139
+ continue ;
140
140
}
141
- } elseif ($ metadata ->hasAssociation ($ field )) {
142
- $ values = $ this ->normalizeValues ((array ) $ value );
143
141
144
- if (empty ($ values )) {
142
+ // there are many values, as we translate those to an IN clause, strategy must be exact
143
+ if (self ::STRATEGY_EXACT !== $ strategy ) {
145
144
continue ;
146
145
}
147
146
148
- $ values = array_map ([$ this , 'getFilterValueFromUrl ' ], $ values );
149
-
150
- $ association = $ field ;
151
- $ associationAlias = QueryNameGenerator::generateJoinAlias ($ association );
152
- $ valueParameter = QueryNameGenerator::generateParameterName ($ association );
147
+ $ valueParameter = QueryNameGenerator::generateParameterName ($ field );
153
148
154
149
$ queryBuilder
155
- ->join (sprintf ('%s.%s ' , $ alias , $ association ), $ associationAlias );
150
+ ->andWhere (sprintf ('%s.%s IN (:%s) ' , $ alias , $ field , $ valueParameter ))
151
+ ->setParameter ($ valueParameter , $ values );
152
+ }
156
153
157
- if (1 === count ($ values )) {
158
- $ queryBuilder
159
- ->andWhere (sprintf ('%s.id = :%s ' , $ associationAlias , $ valueParameter ))
160
- ->setParameter ($ valueParameter , $ values [0 ]);
161
- } else {
162
- $ queryBuilder
163
- ->andWhere (sprintf ('%s.id IN (:%s) ' , $ associationAlias , $ valueParameter ))
164
- ->setParameter ($ valueParameter , $ values );
165
- }
154
+ // metadata doesn't have the field, nor an association on the field
155
+ if (!$ metadata ->hasAssociation ($ field )) {
156
+ continue ;
157
+ }
158
+
159
+ $ values = array_map ([$ this , 'getFilterValueFromUrl ' ], $ values );
160
+
161
+ $ association = $ field ;
162
+ $ associationAlias = QueryNameGenerator::generateJoinAlias ($ association );
163
+ $ valueParameter = QueryNameGenerator::generateParameterName ($ association );
164
+
165
+ $ queryBuilder
166
+ ->join (sprintf ('%s.%s ' , $ alias , $ association ), $ associationAlias );
167
+
168
+ if (1 === count ($ values )) {
169
+ $ queryBuilder
170
+ ->andWhere (sprintf ('%s.id = :%s ' , $ associationAlias , $ valueParameter ))
171
+ ->setParameter ($ valueParameter , $ values [0 ]);
172
+ } else {
173
+ $ queryBuilder
174
+ ->andWhere (sprintf ('%s.id IN (:%s) ' , $ associationAlias , $ valueParameter ))
175
+ ->setParameter ($ valueParameter , $ values );
166
176
}
167
177
}
168
178
}
@@ -186,31 +196,34 @@ private function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder
186
196
case null :
187
197
case self ::STRATEGY_EXACT :
188
198
$ queryBuilder
189
- ->andWhere (sprintf ('%s.%s = :%s ' , $ alias , $ field , $ valueParameter ))
199
+ ->andWhere (sprintf ($ this -> caseWrap ( '%s.%s ' ). ' = ' . $ this -> caseWrap ( ' :%s ') , $ alias , $ field , $ valueParameter ))
190
200
->setParameter ($ valueParameter , $ value );
191
201
break ;
192
202
193
203
case self ::STRATEGY_PARTIAL :
194
204
$ queryBuilder
195
- ->andWhere (sprintf ('%s.%s LIKE :%s ' , $ alias , $ field , $ valueParameter ))
205
+ ->andWhere (sprintf ($ this -> caseWrap ( '%s.%s ' ). ' LIKE ' . $ this -> caseWrap ( ' :%s ') , $ alias , $ field , $ valueParameter ))
196
206
->setParameter ($ valueParameter , sprintf ('%%%s%% ' , $ value ));
197
207
break ;
198
208
199
209
case self ::STRATEGY_START :
200
210
$ queryBuilder
201
- ->andWhere (sprintf ('%s.%s LIKE :%s ' , $ alias , $ field , $ valueParameter ))
211
+ ->andWhere (sprintf ($ this -> caseWrap ( '%s.%s ' ). ' LIKE ' . $ this -> caseWrap ( ' :%s ') , $ alias , $ field , $ valueParameter ))
202
212
->setParameter ($ valueParameter , sprintf ('%s%% ' , $ value ));
203
213
break ;
204
214
205
215
case self ::STRATEGY_END :
206
216
$ queryBuilder
207
- ->andWhere (sprintf ('%s.%s LIKE :%s ' , $ alias , $ field , $ valueParameter ))
217
+ ->andWhere (sprintf ($ this -> caseWrap ( '%s.%s ' ). ' LIKE ' . $ this -> caseWrap ( ' :%s ') , $ alias , $ field , $ valueParameter ))
208
218
->setParameter ($ valueParameter , sprintf ('%%%s ' , $ value ));
209
219
break ;
210
220
211
221
case self ::STRATEGY_WORD_START :
222
+ $ andWhere = $ this ->caseWrap ('%1$s.%2$s ' ).' LIKE ' .$ this ->caseWrap (':%3$s_1 ' );
223
+ $ andWhere .= ' OR ' .$ this ->caseWrap ('%1$s.%2$s ' ).' LIKE ' .$ this ->caseWrap (':%3$s_2 ' );
224
+
212
225
$ queryBuilder
213
- ->andWhere (sprintf (' %1$s.%2$s LIKE :%3$s_1 OR %1$s.%2$s LIKE :%3$s_2 ' , $ alias , $ field , $ valueParameter ))
226
+ ->andWhere (sprintf ($ andWhere , $ alias , $ field , $ valueParameter ))
214
227
->setParameter (sprintf ('%s_1 ' , $ valueParameter ), sprintf ('%s%% ' , $ value ))
215
228
->setParameter (sprintf ('%s_2 ' , $ valueParameter ), sprintf ('%% %s%% ' , $ value ));
216
229
break ;
@@ -220,6 +233,23 @@ private function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder
220
233
}
221
234
}
222
235
236
+ /**
237
+ * Wraps a string with a doctrine expression according to the current case status
238
+ * Example: $this->caseWrap('o.id') becomes LOWER(o.id) when $this->caseSensitive is true.
239
+ *
240
+ * @param string $string
241
+ *
242
+ * @return string
243
+ */
244
+ private function caseWrap (string $ string ): string
245
+ {
246
+ if (false !== $ this ->caseSensitive ) {
247
+ return $ string ;
248
+ }
249
+
250
+ return sprintf ('LOWER(%s) ' , $ string );
251
+ }
252
+
223
253
/**
224
254
* {@inheritdoc}
225
255
*/
0 commit comments