Skip to content

Commit fd76410

Browse files
authored
Merge pull request #105 from imbus/SIT-173-OldAdvancedSearchDoesntWorkAnymore
Sit 173 old advanced search doesnt work anymore
2 parents a0af53c + b9933cd commit fd76410

File tree

5 files changed

+328
-3
lines changed

5 files changed

+328
-3
lines changed

app/Http/Controllers/Api/AssetsController.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ public function index(Request $request, $action = null, $upcoming_status = null)
147147
$filter = json_decode($request->input('filter'), true);
148148
}
149149

150+
if (!isset($filter[0]['field'])){
151+
$filter = array_filter($filter, function ($key) use ($allowed_columns){
152+
return in_array($key, $allowed_columns);
153+
}, ARRAY_FILTER_USE_KEY);
154+
}
155+
150156
$all_custom_fields = CustomField::all(); //used as a 'cache' of custom fields throughout this page load
151157
foreach ($all_custom_fields as $field) {
152158
$allowed_columns[] = $field->db_column_name();

app/Models/Asset.php

Lines changed: 232 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1908,9 +1908,19 @@ function ($query) use ($search) {
19081908

19091909
public function scopeByFilter($query, array $filters)
19101910
{
1911+
// Check if the filters are in Snipe-IT's original format (key => value)
1912+
if ($this->isLegacyFilterFormat($filters)) {
1913+
return $this->applyLegacyFilters($query, $filters);
1914+
}
19111915
return $this->filterService()->searchByFilter($query, $filters);
19121916
}
19131917

1918+
private function isLegacyFilterFormat($filters)
1919+
{
1920+
// Snipe-IT filters are simple key/value (not arrays with 'field')
1921+
return !isset($filters[0]['field']);
1922+
}
1923+
19141924
/**
19151925
* Query builder scope to order on model
19161926
*
@@ -2159,6 +2169,227 @@ public function scopeByDepreciationId($query, $search)
21592169
* @return \Illuminate\Database\Eloquent\Builder Modified query builder
21602170
*/
21612171

2172+
public function applyLegacyFilters($query, $filter)
2173+
{
2174+
return $query->where(
2175+
function ($query) use ($filter) {
2176+
foreach ($filter as $key => $search_val) {
2177+
2178+
$fieldname = str_replace('custom_fields.', '', $key);
2179+
2180+
if ($fieldname == 'asset_tag') {
2181+
$query->where('assets.asset_tag', 'LIKE', '%'.$search_val.'%');
2182+
}
2183+
2184+
if ($fieldname == 'name') {
2185+
$query->where('assets.name', 'LIKE', '%'.$search_val.'%');
2186+
}
2187+
2188+
2189+
if ($fieldname =='serial') {
2190+
$query->where('assets.serial', 'LIKE', '%'.$search_val.'%');
2191+
}
2192+
2193+
if ($fieldname == 'purchase_date') {
2194+
$query->where('assets.purchase_date', 'LIKE', '%'.$search_val.'%');
2195+
}
2196+
2197+
if ($fieldname == 'purchase_cost') {
2198+
$query->where('assets.purchase_cost', 'LIKE', '%'.$search_val.'%');
2199+
}
2200+
2201+
if ($fieldname == 'notes') {
2202+
$query->where('assets.notes', 'LIKE', '%'.$search_val.'%');
2203+
}
2204+
2205+
if ($fieldname == 'order_number') {
2206+
$query->where('assets.order_number', 'LIKE', '%'.$search_val.'%');
2207+
}
2208+
2209+
if ($fieldname == 'status_label') {
2210+
$query->whereHas(
2211+
'assetstatus', function ($query) use ($search_val) {
2212+
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
2213+
}
2214+
);
2215+
}
2216+
2217+
if ($fieldname == 'location') {
2218+
$query->whereHas(
2219+
'location', function ($query) use ($search_val) {
2220+
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
2221+
}
2222+
);
2223+
}
21622224

2225+
if ($fieldname == 'rtd_location') {
2226+
$query->whereHas(
2227+
'defaultLoc', function ($query) use ($search_val) {
2228+
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
2229+
}
2230+
);
2231+
}
21632232

2164-
}
2233+
if ($fieldname == 'assigned_to') {
2234+
$query->whereHasMorph(
2235+
'assignedTo', [User::class], function ($query) use ($search_val) {
2236+
$query->where(
2237+
function ($query) use ($search_val) {
2238+
$query->where('users.first_name', 'LIKE', '%'.$search_val.'%')
2239+
->orWhere('users.last_name', 'LIKE', '%'.$search_val.'%')
2240+
->orWhere('users.username', 'LIKE', '%'.$search_val.'%');
2241+
}
2242+
);
2243+
}
2244+
)->orWhereHasMorph(
2245+
'assignedTo', [Location::class], function ($query) use ($search_val) {
2246+
$query->where('locations.name', 'LIKE', '%'.$search_val.'%');
2247+
}
2248+
)->orWhereHasMorph(
2249+
'assignedTo', [Asset::class], function ($query) use ($search_val) {
2250+
$query->where(
2251+
function ($query) use ($search_val) {
2252+
// Don't use the asset table prefix here because it will pull from the original asset,
2253+
// not the subselect we're doing here to get the assigned asset
2254+
$query->where('name', 'LIKE', '%'.$search_val.'%')
2255+
->orWhere('asset_tag', 'LIKE', '%'.$search_val.'%');
2256+
}
2257+
);
2258+
}
2259+
);
2260+
}
2261+
2262+
2263+
if ($fieldname == 'manufacturer') {
2264+
$query->whereHas(
2265+
'model', function ($query) use ($search_val) {
2266+
$query->whereHas(
2267+
'manufacturer', function ($query) use ($search_val) {
2268+
$query->where(
2269+
function ($query) use ($search_val) {
2270+
$query->where('manufacturers.name', 'LIKE', '%'.$search_val.'%');
2271+
}
2272+
);
2273+
}
2274+
);
2275+
}
2276+
);
2277+
}
2278+
2279+
if ($fieldname == 'category') {
2280+
$query->whereHas(
2281+
'model', function ($query) use ($search_val) {
2282+
$query->whereHas(
2283+
'category', function ($query) use ($search_val) {
2284+
$query->where(
2285+
function ($query) use ($search_val) {
2286+
$query->where('categories.name', 'LIKE', '%'.$search_val.'%')
2287+
->orWhere('models.name', 'LIKE', '%'.$search_val.'%')
2288+
->orWhere('models.model_number', 'LIKE', '%'.$search_val.'%');
2289+
}
2290+
);
2291+
}
2292+
);
2293+
}
2294+
);
2295+
}
2296+
2297+
if ($fieldname == 'model') {
2298+
$query->whereHas(
2299+
'model', function ($query) use ($search_val) {
2300+
$query->where('models.name', 'LIKE', '%'.$search_val.'%');
2301+
}
2302+
);
2303+
}
2304+
2305+
2306+
if ($fieldname == 'model_number') {
2307+
$query->whereHas(
2308+
'model', function ($query) use ($search_val) {
2309+
$query->where('models.model_number', 'LIKE', '%'.$search_val.'%');
2310+
}
2311+
);
2312+
}
2313+
2314+
2315+
if ($fieldname == 'company') {
2316+
$query->whereHas(
2317+
'company', function ($query) use ($search_val) {
2318+
$query->where('companies.name', 'LIKE', '%'.$search_val.'%');
2319+
}
2320+
);
2321+
}
2322+
2323+
if ($fieldname == 'supplier') {
2324+
$query->whereHas(
2325+
'supplier', function ($query) use ($search_val) {
2326+
$query->where('suppliers.name', 'LIKE', '%'.$search_val.'%');
2327+
}
2328+
);
2329+
}
2330+
2331+
if ($fieldname == 'status_label') {
2332+
$query->whereHas(
2333+
'assetstatus', function ($query) use ($search_val) {
2334+
$query->where('status_labels.name', 'LIKE', '%'.$search_val.'%');
2335+
}
2336+
);
2337+
}
2338+
2339+
if ($fieldname == 'jobtitle') {
2340+
$query->where(function ($query) use ($search_val) {
2341+
if (is_array($search_val)) {
2342+
$query->whereHasMorph(
2343+
'assignedTo',
2344+
[User::class],
2345+
function ($query) use ($search_val) {
2346+
$query->whereIn('users.jobtitle', $search_val);
2347+
}
2348+
);
2349+
} else {
2350+
$query->whereHasMorph(
2351+
'assignedTo',
2352+
[User::class],
2353+
function ($query) use ($search_val) {
2354+
$query->where(function ($query) use ($search_val) {
2355+
$query->where('users.jobtitle', 'LIKE', '%' . $search_val . '%');
2356+
});
2357+
}
2358+
);
2359+
}
2360+
});
2361+
}
2362+
2363+
2364+
/**
2365+
* THIS CLUNKY BIT IS VERY IMPORTANT
2366+
*
2367+
* Although inelegant, this section matters a lot when querying against fields that do not
2368+
* exist on the asset table. There's probably a better way to do this moving forward, for
2369+
* example using the Schema:: methods to determine whether or not a column actually exists,
2370+
* or even just using the $searchableRelations variable earlier in this file.
2371+
*
2372+
* In short, this set of statements tells the query builder to ONLY query against an
2373+
* actual field that's being passed if it doesn't meet known relational fields. This
2374+
* allows us to query custom fields directly in the assets table
2375+
* (regardless of their name) and *skip* any fields that we already know can only be
2376+
* searched through relational searches that we do earlier in this method.
2377+
*
2378+
* For example, we do not store "location" as a field on the assets table, we store
2379+
* that relationship through location_id on the assets table, therefore querying
2380+
* assets.location would fail, as that field doesn't exist -- plus we're already searching
2381+
* against those relationships earlier in this method.
2382+
*
2383+
* - snipe
2384+
*/
2385+
2386+
if (($fieldname!='category') && ($fieldname!='model_number') && ($fieldname!='rtd_location') && ($fieldname!='location') && ($fieldname!='supplier')
2387+
&& ($fieldname!='status_label') && ($fieldname!='assigned_to') && ($fieldname!='model') && ($fieldname!='jobtitle') && ($fieldname!='company') && ($fieldname!='manufacturer')
2388+
) {
2389+
$query->where('assets.'.$fieldname, 'LIKE', '%' . $search_val . '%');
2390+
}
2391+
}
2392+
}
2393+
);
2394+
}
2395+
}

tests/Feature/AssetQuery/CombinedQueryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ public function testFilterAssetNoFiltersReturnsAll()
731731
$assetC = Asset::factory()->create();
732732

733733
// No filters applied
734-
$filter = [[]];
734+
$filter = [];
735735
$results = Asset::query()->byFilter($filter)->get();
736736

737737
$this->assertCount(3, $results);

tests/Feature/AssetQuery/CustomFieldQueryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public function testFilterWithEmptyArrayLeavesResultsUnchanged()
118118
$a = Asset::factory()->create(['custom_text' => 'A']);
119119
$b = Asset::factory()->create(['custom_text' => 'B']);
120120

121-
$filter = [[]];
121+
$filter = [];
122122

123123
$results = Asset::query()->byFilter($filter)->get();
124124

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
namespace Tests\Feature\AssetQuery;
3+
4+
use App\Models\Asset;
5+
use App\Models\Company;
6+
use Tests\TestCase;
7+
8+
9+
class LegacyFilterTest extends TestCase
10+
{
11+
public function testFilterAssetsByCompanyId()
12+
{
13+
$companyA = Company::factory()->create();
14+
$companyB = Company::factory()->create();
15+
16+
$assetA = Asset::factory()->create(['company_id' => $companyA->id]);
17+
$assetB = Asset::factory()->create(['company_id' => $companyB->id]);
18+
19+
$filter = [
20+
'company_id' => $companyA->id
21+
];
22+
23+
$results = Asset::query()->byFilter($filter)->get();
24+
25+
$this->assertCount(1, $results);
26+
$this->assertTrue($results->contains($assetA));
27+
}
28+
29+
public function testFilterAssetsByAssetTag()
30+
{
31+
$assetA = Asset::factory()->create(['asset_tag' => 'A1']);
32+
$assetB = Asset::factory()->create(['asset_tag' => 'B1']);
33+
34+
$filter = [
35+
'asset_tag' => 'A1'
36+
];
37+
38+
$results = Asset::query()->byFilter($filter)->get();
39+
40+
$this->assertCount(1, $results);
41+
$this->assertTrue($results->contains($assetA));
42+
}
43+
44+
public function testFilterAssetsByCompanyAndAssetTag()
45+
{
46+
$companyA = Company::factory()->create();
47+
$companyB = Company::factory()->create();
48+
49+
$assetA = Asset::factory()->create([
50+
'company_id' => $companyA->id,
51+
'asset_tag' => 'X1'
52+
]);
53+
54+
$assetB = Asset::factory()->create([
55+
'company_id' => $companyA->id,
56+
'asset_tag' => 'Y1'
57+
]);
58+
59+
$assetC = Asset::factory()->create([
60+
'company_id' => $companyB->id,
61+
'asset_tag' => 'X1'
62+
]);
63+
64+
$filter = [
65+
'company_id' => $companyA->id,
66+
'asset_tag' => 'X1'
67+
];
68+
69+
$results = Asset::query()->byFilter($filter)->get();
70+
71+
$this->assertCount(1, $results);
72+
$this->assertTrue($results->contains($assetA));
73+
}
74+
75+
public function testReturnsAllAssetsWhenFilterIsEmpty()
76+
{
77+
$assetA = Asset::factory()->create();
78+
$assetB = Asset::factory()->create();
79+
80+
$filter = [];
81+
82+
$results = Asset::query()->byFilter($filter)->get();
83+
84+
$this->assertCount(2, $results);
85+
$this->assertTrue($results->contains($assetA));
86+
$this->assertTrue($results->contains($assetB));
87+
}
88+
}

0 commit comments

Comments
 (0)