From 9cb0decf35dcb3fee8821077dfc14febd1832a90 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 18 May 2023 12:54:00 -0700 Subject: [PATCH 01/16] Adapt multiple column search based on database driver --- app/Models/User.php | 18 +++++++++++++++++- .../Api/Users/UsersForSelectListTest.php | 13 +++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/Models/User.php b/app/Models/User.php index 1e63ebad71..dd63a3c250 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -646,11 +646,27 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo { $query = $query->where('first_name', 'LIKE', '%'.$search.'%') ->orWhere('last_name', 'LIKE', '%'.$search.'%') - ->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%{$search}%"]); + ->orWhereRaw( + $this->buildMultipleColumnSearch([ + DB::getTablePrefix() . 'users.first_name', + DB::getTablePrefix() . 'users.last_name', + ]), + ["%{$search}%"] + ); return $query; } + public function buildMultipleColumnSearch(array $columns): string + { + $driver = config('database.connections.' . config('database.default') . '.driver'); + + if ($driver === 'sqlite') { + return implode(" || ' ' || ", $columns) . ' LIKE ?'; + } + + return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; + } /** * Run additional, advanced searches. diff --git a/tests/Feature/Api/Users/UsersForSelectListTest.php b/tests/Feature/Api/Users/UsersForSelectListTest.php index 6ab5bf9a85..8cdf700f04 100644 --- a/tests/Feature/Api/Users/UsersForSelectListTest.php +++ b/tests/Feature/Api/Users/UsersForSelectListTest.php @@ -30,6 +30,19 @@ class UsersForSelectListTest extends TestCase ->assertJson(fn(AssertableJson $json) => $json->has('results', 3)->etc()); } + public function testUsersCanBeSearchedByFirstAndLastName() + { + User::factory()->create(['first_name' => 'Luke', 'last_name' => 'Skywalker']); + + Passport::actingAs(User::factory()->create()); + $response = $this->getJson(route('api.users.selectlist', ['search' => 'luke sky']))->assertOk(); + + $results = collect($response->json('results')); + + $this->assertEquals(1, $results->count()); + $this->assertTrue($results->pluck('text')->contains(fn($text) => str_contains($text, 'Luke'))); + } + public function testUsersScopedToCompanyWhenMultipleFullCompanySupportEnabled() { $this->settings->enableMultipleFullCompanySupport(); From d8d672c4c15e40f3859509a1f6278f22c17e90fd Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 18 May 2023 13:05:40 -0700 Subject: [PATCH 02/16] Move multiple column search function to Searchable trait --- app/Models/Traits/Searchable.php | 11 +++++++++++ app/Models/User.php | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index a7feb62957..687210788b 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -257,4 +257,15 @@ trait Searchable return $related->getTable(); } + + private function buildMultipleColumnSearch(array $columns): string + { + $driver = config('database.connections.' . config('database.default') . '.driver'); + + if ($driver === 'sqlite') { + return implode(" || ' ' || ", $columns) . ' LIKE ?'; + } + + return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; + } } diff --git a/app/Models/User.php b/app/Models/User.php index dd63a3c250..8f5946a65c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -657,17 +657,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo return $query; } - public function buildMultipleColumnSearch(array $columns): string - { - $driver = config('database.connections.' . config('database.default') . '.driver'); - - if ($driver === 'sqlite') { - return implode(" || ' ' || ", $columns) . ' LIKE ?'; - } - - return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; - } - /** * Run additional, advanced searches. * From ffbde4618030f711448712c986aafcea9bad0e50 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 18 May 2023 13:06:50 -0700 Subject: [PATCH 03/16] Simplify sqlite query --- app/Models/Traits/Searchable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 687210788b..97ad19c276 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -263,7 +263,7 @@ trait Searchable $driver = config('database.connections.' . config('database.default') . '.driver'); if ($driver === 'sqlite') { - return implode(" || ' ' || ", $columns) . ' LIKE ?'; + return implode("||' '||", $columns) . ' LIKE ?'; } return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; From 85974c7f6586d30617fff4886a952f4ed8135001 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 18 May 2023 13:16:52 -0700 Subject: [PATCH 04/16] Apply formatting --- app/Models/User.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/Models/User.php b/app/Models/User.php index 8f5946a65c..852851a771 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -644,17 +644,15 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo */ public function scopeSimpleNameSearch($query, $search) { - $query = $query->where('first_name', 'LIKE', '%'.$search.'%') - ->orWhere('last_name', 'LIKE', '%'.$search.'%') - ->orWhereRaw( - $this->buildMultipleColumnSearch([ - DB::getTablePrefix() . 'users.first_name', - DB::getTablePrefix() . 'users.last_name', - ]), - ["%{$search}%"] - ); - - return $query; + return $query->where('first_name', 'LIKE', '%' . $search . '%') + ->orWhere('last_name', 'LIKE', '%' . $search . '%') + ->orWhereRaw( + $this->buildMultipleColumnSearch([ + DB::getTablePrefix() . 'users.first_name', + DB::getTablePrefix() . 'users.last_name', + ]), + ["%{$search}%"] + ); } /** From 3a9670930d6f436262ef40836517a12608a684ad Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 18 May 2023 13:17:35 -0700 Subject: [PATCH 05/16] Define required fields in Custom Field seeder so sqlite can be seeded --- database/seeders/CustomFieldSeeder.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/database/seeders/CustomFieldSeeder.php b/database/seeders/CustomFieldSeeder.php index 8776872644..7891fbcacc 100644 --- a/database/seeders/CustomFieldSeeder.php +++ b/database/seeders/CustomFieldSeeder.php @@ -38,22 +38,32 @@ class CustomFieldSeeder extends Seeder [ 'custom_field_id' => '1', 'custom_fieldset_id' => '1', + 'order' => 0, + 'required' => 0, ], [ 'custom_field_id' => '2', 'custom_fieldset_id' => '1', + 'order' => 0, + 'required' => 0, ], [ 'custom_field_id' => '3', 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, ], [ 'custom_field_id' => '4', 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, ], [ 'custom_field_id' => '5', 'custom_fieldset_id' => '2', + 'order' => 0, + 'required' => 0, ], ]); From 2d86c8f03065d7579ae31c096b1c0e0d07cd10a1 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Thu, 18 May 2023 17:06:41 -0700 Subject: [PATCH 06/16] Update advancedTextSearch methods in Asset and User models --- app/Models/Asset.php | 9 +++++++-- app/Models/User.php | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/Models/Asset.php b/app/Models/Asset.php index ac431253bc..62b568dbf3 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -947,8 +947,13 @@ class Asset extends Depreciable ->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.username', 'LIKE', '%'.$term.'%') - ->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$term%"]); - + ->orWhereRaw( + $this->buildMultipleColumnSearch([ + DB::getTablePrefix().'assets_users.first_name', + DB::getTablePrefix().'assets_users.last_name', + ]), + ["%{$term}%"] + ); } /** diff --git a/app/Models/User.php b/app/Models/User.php index 852851a771..464e20ea4c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -665,7 +665,13 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo public function advancedTextSearch(Builder $query, array $terms) { foreach($terms as $term) { - $query = $query->orWhereRaw('CONCAT('.DB::getTablePrefix().'users.first_name," ",'.DB::getTablePrefix().'users.last_name) LIKE ?', ["%{$term}%"]); + $query = $query->orWhereRaw( + $this->buildMultipleColumnSearch([ + DB::getTablePrefix() . 'users.first_name', + DB::getTablePrefix() . 'users.last_name', + ]), + ["%{$term}%"] + ); } return $query; From 64e83ed9f552f6b32834ced54fe2d486d594de8a Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 22 May 2023 16:54:54 -0700 Subject: [PATCH 07/16] Update scopeAssignedSearch in Asset --- app/Models/Asset.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 62b568dbf3..333ebe51ab 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -1348,7 +1348,13 @@ class Asset extends Depreciable })->orWhere(function ($query) use ($search) { $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%') - ->orWhereRaw('CONCAT('.DB::getTablePrefix().'assets_users.first_name," ",'.DB::getTablePrefix().'assets_users.last_name) LIKE ?', ["%$search%"]) + ->orWhereRaw( + $this->buildMultipleColumnSearch([ + DB::getTablePrefix().'assets_users.first_name', + DB::getTablePrefix().'assets_users.last_name', + ]), + ["%{$search}%"] + ) ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%') ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%') ->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%'); From 9b512648a3a9bf6e19edcb9801392bc5a939ad67 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 22 May 2023 17:33:19 -0700 Subject: [PATCH 08/16] Update searchRelations in Searchable trait --- app/Models/Traits/Searchable.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 97ad19c276..69b0f800f6 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -5,6 +5,7 @@ namespace App\Models\Traits; use App\Models\Asset; use App\Models\CustomField; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Facades\DB; /** * This trait allows for cleaner searching of models, @@ -164,7 +165,13 @@ trait Searchable } // I put this here because I only want to add the concat one time in the end of the user relation search if($relation == 'user') { - $query->orWhereRaw('CONCAT (users.first_name, " ", users.last_name) LIKE ?', ["%{$term}%"]); + $query->orWhereRaw( + $this->buildMultipleColumnSearch([ + DB::getTablePrefix() . 'users.first_name', + DB::getTablePrefix() . 'users.last_name', + ]), + ["%{$term}%"] + ); } }); } From 50234bc9a5479c0ae2c1dfc4cdcc141729468994 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 22 May 2023 17:40:06 -0700 Subject: [PATCH 09/16] Formatting --- database/seeders/CustomFieldSeeder.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/database/seeders/CustomFieldSeeder.php b/database/seeders/CustomFieldSeeder.php index 7891fbcacc..551e05f40f 100644 --- a/database/seeders/CustomFieldSeeder.php +++ b/database/seeders/CustomFieldSeeder.php @@ -48,24 +48,24 @@ class CustomFieldSeeder extends Seeder 'required' => 0, ], [ - 'custom_field_id' => '3', - 'custom_fieldset_id' => '2', + 'custom_field_id' => '3', + 'custom_fieldset_id' => '2', 'order' => 0, 'required' => 0, ], [ - 'custom_field_id' => '4', - 'custom_fieldset_id' => '2', + 'custom_field_id' => '4', + 'custom_fieldset_id' => '2', 'order' => 0, 'required' => 0, ], [ - 'custom_field_id' => '5', - 'custom_fieldset_id' => '2', + 'custom_field_id' => '5', + 'custom_fieldset_id' => '2', 'order' => 0, 'required' => 0, ], - ]); + ]); } } From f40e7223975c220430283f49c3b79c454b94e134 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Mon, 22 May 2023 17:44:17 -0700 Subject: [PATCH 10/16] Add docblock --- app/Models/Traits/Searchable.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 69b0f800f6..6ac6be7357 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -265,6 +265,12 @@ trait Searchable return $related->getTable(); } + /** + * Builds a search string for either MySQL or sqlite by separating the provided columns with a space. + * + * @param array $columns Columns to include in search string. + * @return string + */ private function buildMultipleColumnSearch(array $columns): string { $driver = config('database.connections.' . config('database.default') . '.driver'); From 05a2e568d7d802a65f64ec03a74c4db8e551523a Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 23 May 2023 11:37:09 -0700 Subject: [PATCH 11/16] Add comment --- app/Models/Traits/Searchable.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 6ac6be7357..4165dc98b9 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -279,6 +279,7 @@ trait Searchable return implode("||' '||", $columns) . ' LIKE ?'; } + // Default to MySQL's concatenation method return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; } } From 9078d4c71cdf9942ed5eeb853af6dff5c57aed4c Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 23 May 2023 13:35:19 -0700 Subject: [PATCH 12/16] Add dynamic scope for prettier multiple column search --- app/Models/Traits/Searchable.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 4165dc98b9..63ee0f5b23 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -282,4 +282,9 @@ trait Searchable // Default to MySQL's concatenation method return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; } + + public function scopeOrWhereMultipleColumns($query, array $columns, $term) + { + return $query->orWhereRaw($this->buildMultipleColumnSearch($columns), ["%{$term}%"]); + } } From 054d71aedc1fe7a02bd3a6e6312fcdc4291d2b05 Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 23 May 2023 13:37:04 -0700 Subject: [PATCH 13/16] Add docblock --- app/Models/Traits/Searchable.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 63ee0f5b23..8fe3b0d499 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -283,6 +283,14 @@ trait Searchable return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; } + /** + * Search a string across multiple columns separated with a space. + * + * @param Builder $query + * @param array $columns - Columns to include in search string. + * @param $term + * @return Builder + */ public function scopeOrWhereMultipleColumns($query, array $columns, $term) { return $query->orWhereRaw($this->buildMultipleColumnSearch($columns), ["%{$term}%"]); From b2b6f0cf9637869a50dc41fb9d4081f102f506cb Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 23 May 2023 13:38:50 -0700 Subject: [PATCH 14/16] Use new dynamic scope where possible --- app/Models/Asset.php | 22 ++++++++-------------- app/Models/User.php | 22 ++++++++-------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 333ebe51ab..32ffcfc3bf 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -947,13 +947,10 @@ class Asset extends Depreciable ->orWhere('assets_users.first_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.username', 'LIKE', '%'.$term.'%') - ->orWhereRaw( - $this->buildMultipleColumnSearch([ - DB::getTablePrefix().'assets_users.first_name', - DB::getTablePrefix().'assets_users.last_name', - ]), - ["%{$term}%"] - ); + ->orWhereMultipleColumns([ + DB::getTablePrefix() . 'assets_users.first_name', + DB::getTablePrefix() . 'assets_users.last_name', + ], $term); } /** @@ -1348,13 +1345,10 @@ class Asset extends Depreciable })->orWhere(function ($query) use ($search) { $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%') - ->orWhereRaw( - $this->buildMultipleColumnSearch([ - DB::getTablePrefix().'assets_users.first_name', - DB::getTablePrefix().'assets_users.last_name', - ]), - ["%{$search}%"] - ) + ->orWhereMultipleColumns([ + DB::getTablePrefix() . 'assets_users.first_name', + DB::getTablePrefix() . 'assets_users.last_name', + ], $search) ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%') ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%') ->orWhere('assigned_assets.name', 'LIKE', '%'.$search.'%'); diff --git a/app/Models/User.php b/app/Models/User.php index 464e20ea4c..a902e47379 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -646,13 +646,10 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo { return $query->where('first_name', 'LIKE', '%' . $search . '%') ->orWhere('last_name', 'LIKE', '%' . $search . '%') - ->orWhereRaw( - $this->buildMultipleColumnSearch([ - DB::getTablePrefix() . 'users.first_name', - DB::getTablePrefix() . 'users.last_name', - ]), - ["%{$search}%"] - ); + ->orWhereMultipleColumns([ + DB::getTablePrefix() . 'users.first_name', + DB::getTablePrefix() . 'users.last_name', + ], $search); } /** @@ -665,13 +662,10 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo public function advancedTextSearch(Builder $query, array $terms) { foreach($terms as $term) { - $query = $query->orWhereRaw( - $this->buildMultipleColumnSearch([ - DB::getTablePrefix() . 'users.first_name', - DB::getTablePrefix() . 'users.last_name', - ]), - ["%{$term}%"] - ); + $query->orWhereMultipleColumns([ + DB::getTablePrefix() . 'users.first_name', + DB::getTablePrefix() . 'users.last_name', + ], $term); } return $query; From 6300909feebe229c91c28dc45868f9c0e51af7aa Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 23 May 2023 13:39:01 -0700 Subject: [PATCH 15/16] Add test for searching for user's first and last name --- tests/Feature/Api/Users/UsersSearchTest.php | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/Feature/Api/Users/UsersSearchTest.php diff --git a/tests/Feature/Api/Users/UsersSearchTest.php b/tests/Feature/Api/Users/UsersSearchTest.php new file mode 100644 index 0000000000..33f77196f3 --- /dev/null +++ b/tests/Feature/Api/Users/UsersSearchTest.php @@ -0,0 +1,28 @@ +create(['first_name' => 'Luke', 'last_name' => 'Skywalker']); + User::factory()->create(['first_name' => 'Darth', 'last_name' => 'Vader']); + + Passport::actingAs(User::factory()->viewUsers()->create()); + $response = $this->getJson(route('api.users.index', ['search' => 'luke sky']))->assertOk(); + + $results = collect($response->json('rows')); + + $this->assertEquals(1, $results->count()); + $this->assertTrue($results->pluck('name')->contains(fn($text) => str_contains($text, 'Luke'))); + $this->assertFalse($results->pluck('name')->contains(fn($text) => str_contains($text, 'Darth'))); + } +} From 0a3d46824e54d079031ae810cd03924ae08fbc4f Mon Sep 17 00:00:00 2001 From: Marcus Moore Date: Tue, 23 May 2023 15:58:58 -0700 Subject: [PATCH 16/16] Automatically add table prefixes when building multi-column search string --- app/Models/Asset.php | 8 ++++---- app/Models/Traits/Searchable.php | 10 ++++++---- app/Models/User.php | 9 ++++----- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 32ffcfc3bf..42c97a5cd7 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -948,8 +948,8 @@ class Asset extends Depreciable ->orWhere('assets_users.last_name', 'LIKE', '%'.$term.'%') ->orWhere('assets_users.username', 'LIKE', '%'.$term.'%') ->orWhereMultipleColumns([ - DB::getTablePrefix() . 'assets_users.first_name', - DB::getTablePrefix() . 'assets_users.last_name', + 'assets_users.first_name', + 'assets_users.last_name', ], $term); } @@ -1346,8 +1346,8 @@ class Asset extends Depreciable $query->where('assets_users.first_name', 'LIKE', '%'.$search.'%') ->orWhere('assets_users.last_name', 'LIKE', '%'.$search.'%') ->orWhereMultipleColumns([ - DB::getTablePrefix() . 'assets_users.first_name', - DB::getTablePrefix() . 'assets_users.last_name', + 'assets_users.first_name', + 'assets_users.last_name', ], $search) ->orWhere('assets_users.username', 'LIKE', '%'.$search.'%') ->orWhere('assets_locations.name', 'LIKE', '%'.$search.'%') diff --git a/app/Models/Traits/Searchable.php b/app/Models/Traits/Searchable.php index 8fe3b0d499..06e05348fb 100644 --- a/app/Models/Traits/Searchable.php +++ b/app/Models/Traits/Searchable.php @@ -167,8 +167,8 @@ trait Searchable if($relation == 'user') { $query->orWhereRaw( $this->buildMultipleColumnSearch([ - DB::getTablePrefix() . 'users.first_name', - DB::getTablePrefix() . 'users.last_name', + 'users.first_name', + 'users.last_name', ]), ["%{$term}%"] ); @@ -273,14 +273,16 @@ trait Searchable */ private function buildMultipleColumnSearch(array $columns): string { + $mappedColumns = collect($columns)->map(fn($column) => DB::getTablePrefix() . $column)->toArray(); + $driver = config('database.connections.' . config('database.default') . '.driver'); if ($driver === 'sqlite') { - return implode("||' '||", $columns) . ' LIKE ?'; + return implode("||' '||", $mappedColumns) . ' LIKE ?'; } // Default to MySQL's concatenation method - return 'CONCAT(' . implode('," ",', $columns) . ') LIKE ?'; + return 'CONCAT(' . implode('," ",', $mappedColumns) . ') LIKE ?'; } /** diff --git a/app/Models/User.php b/app/Models/User.php index a902e47379..98a3ec346b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -647,8 +647,8 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo return $query->where('first_name', 'LIKE', '%' . $search . '%') ->orWhere('last_name', 'LIKE', '%' . $search . '%') ->orWhereMultipleColumns([ - DB::getTablePrefix() . 'users.first_name', - DB::getTablePrefix() . 'users.last_name', + 'users.first_name', + 'users.last_name', ], $search); } @@ -660,11 +660,10 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo * @return \Illuminate\Database\Eloquent\Builder */ public function advancedTextSearch(Builder $query, array $terms) { - foreach($terms as $term) { $query->orWhereMultipleColumns([ - DB::getTablePrefix() . 'users.first_name', - DB::getTablePrefix() . 'users.last_name', + 'users.first_name', + 'users.last_name', ], $term); }