88use Illuminate \Database \Eloquent \Model ;
99use Illuminate \Database \Query \Expression ;
1010use Illuminate \Http \JsonResponse ;
11+ use Illuminate \Support \Carbon ;
1112use Illuminate \Support \Collection ;
1213use Illuminate \Support \Facades \DB ;
1314use Illuminate \Support \Str ;
@@ -258,6 +259,7 @@ protected function filterRecords(): void
258259 }
259260
260261 $ this ->columnSearch ();
262+ $ this ->columnControlSearch ();
261263 $ this ->searchPanesSearch ();
262264
263265 // If no modification between the original query and the filtered one has been made
@@ -281,23 +283,122 @@ public function columnSearch(): void
281283 $ columns = $ this ->request ->columns ();
282284
283285 foreach ($ columns as $ index => $ column ) {
284- $ column = $ this ->getColumnName ($ index );
286+ $ columnName = $ this ->getColumnName ($ index );
285287
286- if (is_null ($ column )) {
288+ if (is_null ($ columnName )) {
287289 continue ;
288290 }
289291
290- if (! $ this ->request ->isColumnSearchable ($ index ) || $ this ->isBlacklisted ($ column ) && ! $ this ->hasFilterColumn ($ column )) {
292+ if (! $ this ->request ->isColumnSearchable ($ index ) || $ this ->isBlacklisted ($ columnName ) && ! $ this ->hasFilterColumn ($ columnName )) {
291293 continue ;
292294 }
293295
294- if ($ this ->hasFilterColumn ($ column )) {
296+ if ($ this ->hasFilterColumn ($ columnName )) {
295297 $ keyword = $ this ->getColumnSearchKeyword ($ index , true );
296- $ this ->applyFilterColumn ($ this ->getBaseQueryBuilder (), $ column , $ keyword );
298+ $ this ->applyFilterColumn ($ this ->getBaseQueryBuilder (), $ columnName , $ keyword );
297299 } else {
298- $ column = $ this ->resolveRelationColumn ($ column );
300+ $ columnName = $ this ->resolveRelationColumn ($ columnName );
299301 $ keyword = $ this ->getColumnSearchKeyword ($ index );
300- $ this ->compileColumnSearch ($ index , $ column , $ keyword );
302+ $ this ->compileColumnSearch ($ index , $ columnName , $ keyword );
303+ }
304+ }
305+ }
306+
307+ public function columnControlSearch (): void
308+ {
309+ $ columns = $ this ->request ->columns ();
310+
311+ foreach ($ columns as $ index => $ column ) {
312+ $ columnName = $ this ->getColumnName ($ index );
313+
314+ if (is_null ($ columnName ) || ! ($ column ['searchable ' ] ?? false )) {
315+ continue ;
316+ }
317+
318+ if ($ this ->isBlacklisted ($ columnName ) && ! $ this ->hasFilterColumn ($ columnName )) {
319+ continue ;
320+ }
321+
322+ $ columnControl = $ this ->request ->columnControl ($ index );
323+ $ list = $ columnControl ['list ' ] ?? [];
324+ $ search = $ columnControl ['search ' ] ?? [];
325+ $ value = $ search ['value ' ] ?? '' ;
326+ $ logic = $ search ['logic ' ] ?? 'equal ' ;
327+ $ mask = $ search ['mask ' ] ?? '' ; // for date type
328+ $ type = $ search ['type ' ] ?? 'text ' ; // text, num, date
329+
330+ if ($ value != '' || str_contains (strtolower ($ logic ), 'empty ' ) || $ list ) {
331+ $ operator = match ($ logic ) {
332+ 'contains ' , 'notContains ' , 'starts ' , 'ends ' => 'LIKE ' ,
333+ 'greater ' => '> ' ,
334+ 'less ' => '< ' ,
335+ 'greaterOrEqual ' => '>= ' ,
336+ 'lessOrEqual ' => '<= ' ,
337+ 'empty ' , 'notEmpty ' => null ,
338+ default => '= ' ,
339+ };
340+
341+ switch ($ logic ) {
342+ case 'contains ' :
343+ case 'notContains ' :
344+ $ value = '% ' .$ value .'% ' ;
345+ break ;
346+ case 'starts ' :
347+ $ value = $ value .'% ' ;
348+ break ;
349+ case 'ends ' :
350+ $ value = '% ' .$ value ;
351+ break ;
352+ }
353+
354+ if ($ this ->hasFilterColumn ($ columnName )) {
355+ $ value = $ list ? implode (', ' , $ list ) : $ value ;
356+ $ this ->applyFilterColumn ($ this ->getBaseQueryBuilder (), $ columnName , $ value );
357+
358+ continue ;
359+ }
360+
361+ if ($ list ) {
362+ if (str_contains ($ logic , 'not ' )) {
363+ $ this ->query ->whereNotIn ($ columnName , $ list );
364+ } else {
365+ $ this ->query ->whereIn ($ columnName , $ list );
366+ }
367+
368+ continue ;
369+ }
370+
371+ if (str_contains (strtolower ($ logic ), 'empty ' )) {
372+ $ this ->query ->whereNull ($ columnName , not: $ logic === 'notEmpty ' );
373+
374+ continue ;
375+ }
376+
377+ if ($ type === 'date ' ) {
378+ try {
379+ $ value = $ mask ? Carbon::createFromFormat ($ mask , $ value ) : Carbon::parse ($ value );
380+ } catch (\Exception ) {
381+ // can't parse date
382+ }
383+
384+ if ($ logic === 'notEqual ' ) {
385+ $ this ->query ->where (function ($ q ) use ($ columnName , $ value ) {
386+ $ q ->whereDate ($ columnName , '!= ' , $ value )->orWhereNull ($ columnName );
387+ });
388+ } else {
389+ $ this ->query ->whereDate ($ columnName , $ operator , $ value );
390+ }
391+
392+ continue ;
393+ }
394+
395+ if (str_contains ($ logic , 'not ' )) {
396+ $ this ->query ->whereNot ($ columnName , $ operator , $ value );
397+
398+ continue ;
399+ }
400+
401+ $ this ->query ->where ($ columnName , $ operator , $ value );
301402 }
302403 }
303404 }
@@ -536,11 +637,11 @@ protected function getSelectedColumns($query): array
536637 ];
537638
538639 foreach ($ q ->columns ?? [] as $ select ) {
539- $ sql = trim ($ select instanceof Expression ? $ select ->getValue ($ this ->getConnection ()->getQueryGrammar ()) : $ select );
640+ $ sql = trim ($ select instanceof Expression ? $ select ->getValue ($ this ->getConnection ()->getQueryGrammar ()) : ( string ) $ select );
540641 // Remove expressions
541642 $ sql = preg_replace ('/\s*\w*\((?:[^()]*|(?R))*\)/ ' , '_ ' , $ sql );
542643 // Remove multiple spaces
543- $ sql = preg_replace ('/\s+/ ' , ' ' , $ sql );
644+ $ sql = preg_replace ('/\s+/ ' , ' ' , ( string ) $ sql );
544645 // Remove wrappers
545646 $ sql = str_replace (['` ' , '" ' , '[ ' , '] ' ], '' , $ sql );
546647 // Loop on select columns
@@ -694,10 +795,6 @@ public function addColumn($name, $content, $order = false): static
694795
695796 /**
696797 * Perform search using search pane values.
697- *
698- *
699- * @throws \Psr\Container\ContainerExceptionInterface
700- * @throws \Psr\Container\NotFoundExceptionInterface
701798 */
702799 protected function searchPanesSearch (): void
703800 {
0 commit comments