Skip to content

Commit 32e852d

Browse files
committed
Basis tests for filter, beginning
1 parent 7b24903 commit 32e852d

8 files changed

Lines changed: 284 additions & 168 deletions

app/Http/Controllers/ComputerScienceResourceController.php

Lines changed: 6 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\Models\ResourceEdits;
99
use App\Models\ResourceReview;
1010
use App\Services\CommentService;
11+
use App\Services\ComputerScienceResourceFilter;
1112
use App\Services\ResourceReviewService;
1213
use App\Services\SortingManagers\GeneralVotesSortingManager;
1314
use App\Services\SortingManagers\ResourceSortingManager;
@@ -40,151 +41,15 @@ function __construct(
4041
/**
4142
* Display a listing of the resource.
4243
*/
43-
public function index(Request $request)
44+
public function index(Request $request, ComputerScienceResourceFilter $filterService)
4445
{
4546
$query = ComputerScienceResource::query();
4647

47-
// Eager load relations
48-
$query->with(['tags', 'votes', 'upvoteSummary', 'reviewSummary', 'commentsCountRelationship']);
48+
// Apply all filters and sorting
49+
$filters = $request->query();
50+
$query = $filterService->applyFilters($query, $filters);
4951

50-
$validator = Validator::make(
51-
[
52-
'name' => $request->query('name'),
53-
'description' => $request->query('description'),
54-
'platforms' => $request->query('platforms'),
55-
'difficulty' => $request->query('difficulty'),
56-
'pricing' => $request->query('pricing'),
57-
'topics' => $request->query('topics'),
58-
'programming_languages' => $request->query('programming_languages'),
59-
'general_tags' => $request->query('general_tags'),
60-
61-
'community_rating' => $request->query('community_rating'),
62-
'teaching_clarity' => $request->query('teaching_clarity'),
63-
'engagement' => $request->query('engagement'),
64-
'practicality' => $request->query('practicality'),
65-
'user_friendliness' => $request->query('user_friendliness'),
66-
'updates' => $request->query('updates'),
67-
],
68-
[
69-
'name' => ['nullable', 'string', 'max:100'],
70-
'name' => ['nullable', 'string', 'max:1000'],
71-
'platforms' => ['nullable', 'array', 'min:1'],
72-
'platforms.*' => ['required', 'distinct', 'string', Rule::in(config('computerScienceResource.platforms'))],
73-
'difficulty' => ['nullable', 'string', Rule::in(config('computerScienceResource.difficulties'))],
74-
'pricing' => ['nullable', 'string', Rule::in(config('computerScienceResource.pricings'))],
75-
76-
'topic_tags' => ['nullable', 'array', 'min:3'],
77-
'topic_tags.*' => ['required', 'distinct', 'string', 'max:50'],
78-
79-
'general_tags' => ['nullable', 'array'],
80-
'general_tags.*' => ['required', 'distinct', 'string', 'max:50'],
81-
'programming_language_tags' => ['nullable', 'array'],
82-
'programming_language_tags.*' => ['required', 'distinct', 'string', 'max:50'],
83-
84-
'community_rating' => ['nullable', 'integer', 'between:1,4'],
85-
'teaching_clarity' => ['nullable', 'integer', 'between:1,4'],
86-
'engagement' => ['nullable', 'integer', 'between:1,4'],
87-
'practicality' => ['nullable', 'integer', 'between:1,4'],
88-
'user_friendliness' => ['nullable', 'integer', 'between:1,4'],
89-
'updates' => ['nullable', 'integer', 'between:1,4'],
90-
91-
// TODO: Add more validation for the dates
92-
]
93-
);
94-
95-
if (!$validator->validate()) {
96-
// TODO: actually show the error, need to flash instead
97-
return back()->with('error', 'Invalid query parameters data');
98-
}
99-
100-
// Fulltext search on name
101-
if ($name = $request->query('name')) {
102-
$query->whereFullText('name', $name);
103-
}
104-
105-
// Fulltext search on description
106-
if ($description = $request->query('description')) {
107-
$query->whereFullText('description', $description);
108-
}
109-
110-
// Filter by platforms (array)
111-
if ($platforms = $request->query('platforms')) {
112-
$query->where(function ($q) use ($platforms) {
113-
foreach ((array) $platforms as $platform) {
114-
$q->orWhereRaw('FIND_IN_SET(?, platforms)', [$platform]);
115-
}
116-
});
117-
}
118-
119-
// Filter by difficulty (array)
120-
if ($difficulty = $request->query('difficulty')) {
121-
$query->whereIn('difficulty', (array) $difficulty);
122-
}
123-
124-
// Filter by pricing (array)
125-
if ($pricing = $request->query('pricing')) {
126-
$query->whereIn('pricing', (array) $pricing);
127-
}
128-
129-
// Filter by topic tags
130-
if ($topics = $request->query('topics')) {
131-
$query->withAnyTags((array) $topics, 'topics');
132-
}
133-
134-
// Filter by programming languages
135-
if ($programmingLanguages = $request->query('programming_languages')) {
136-
$query->withAnyTags((array) $programmingLanguages, 'programming_languages');
137-
}
138-
139-
// Filter by general tags
140-
if ($generalTags = $request->query('general_tags')) {
141-
$query->withAnyTags((array) $generalTags, 'general_tags');
142-
}
143-
144-
145-
// Filter by reviews
146-
$ratingFilters = [
147-
'community',
148-
'teaching_clarity',
149-
'engagement',
150-
'practicality',
151-
'user_friendliness',
152-
'updates',
153-
'overall',
154-
];
155-
156-
foreach ($ratingFilters as $field) {
157-
if ($rating = $request->query($field)) {
158-
$query = $this->reviewService->applyRatingFilter($query, $field, $rating);
159-
}
160-
}
161-
162-
// Filter by Date posted
163-
if ($createdFrom = $request->query('created_from')) {
164-
$query->whereDate('computer_science_resources.created_at', '>=', $createdFrom);
165-
}
166-
167-
if ($createdTo = $request->query('created_to')) {
168-
$query->whereDate('computer_science_resources.created_at', '<=', $createdTo);
169-
}
170-
171-
// Filter by Date updated
172-
if ($updatedFrom = $request->query('updated_from')) {
173-
$query->whereDate('computer_science_resources.updated_at', '>=', $updatedFrom);
174-
}
175-
176-
if ($updatedTo = $request->query('updated_to')) {
177-
$query->whereDate('computer_science_resources.updated_at', '<=', $updatedTo);
178-
}
179-
180-
/// Handle Sorting
181-
$sortBy = $request->query('sort_by', 'top');
182-
$query = $this->resourceSortingManager->applySort($query, $sortBy);
183-
if ($request->query('reverse', 'false') == 'true') {
184-
$query = $this->resourceSortingManager->reverse($query);
185-
}
186-
187-
// Paginate and return
52+
// Paginate with appended query params
18853
$resources = $query->paginate(10)->appends($request->query());
18954

19055
return Inertia::render('Resources/Index', [

app/Http/Controllers/ResourceReviewController.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use App\Models\ResourceReview;
99
use Illuminate\Support\Facades\Auth;
1010
use Illuminate\Support\Facades\Log;
11-
use Redirect;
1211

1312
class ResourceReviewController extends Controller
1413
{
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
namespace App\Services;
3+
4+
use Illuminate\Support\Facades\Validator;
5+
use Illuminate\Validation\Rule;
6+
use App\Services\SortingManagers\ResourceSortingManager;
7+
8+
class ComputerScienceResourceFilter
9+
{
10+
protected ResourceReviewService $reviewService;
11+
protected ResourceSortingManager $resourceSortingManager;
12+
13+
public function __construct(ResourceReviewService $reviewService, ResourceSortingManager $resourceSortingManager)
14+
{
15+
$this->reviewService = $reviewService;
16+
$this->resourceSortingManager = $resourceSortingManager;
17+
}
18+
19+
public function validate(array $request)
20+
{
21+
$rules = [
22+
'name' => ['nullable', 'string', 'max:1000'],
23+
'description' => ['nullable', 'string', 'max:1000'],
24+
'platforms' => ['nullable', 'array', 'min:1'],
25+
'platforms.*' => ['required', 'distinct', 'string', Rule::in(config('computerScienceResource.platforms'))],
26+
'difficulty' => ['nullable', 'string', Rule::in(config('computerScienceResource.difficulties'))],
27+
'pricing' => ['nullable', 'string', Rule::in(config('computerScienceResource.pricings'))],
28+
'topics' => ['nullable', 'array', 'min:3'],
29+
'topics.*' => ['required', 'distinct', 'string', 'max:50'],
30+
'general_tags' => ['nullable', 'array'],
31+
'general_tags.*' => ['required', 'distinct', 'string', 'max:50'],
32+
'programming_languages' => ['nullable', 'array'],
33+
'programming_languages.*' => ['required', 'distinct', 'string', 'max:50'],
34+
35+
'community_rating' => ['nullable', 'integer', 'between:1,4'],
36+
'teaching_clarity' => ['nullable', 'integer', 'between:1,4'],
37+
'engagement' => ['nullable', 'integer', 'between:1,4'],
38+
'practicality' => ['nullable', 'integer', 'between:1,4'],
39+
'user_friendliness' => ['nullable', 'integer', 'between:1,4'],
40+
'updates' => ['nullable', 'integer', 'between:1,4'],
41+
42+
'created_from' => ['nullable', 'date'],
43+
'created_to' => ['nullable', 'date'],
44+
'updated_from' => ['nullable', 'date'],
45+
'updated_to' => ['nullable', 'date'],
46+
47+
'sort_by' => ['nullable', 'string'],
48+
'reverse' => ['nullable', 'string'],
49+
];
50+
51+
$validator = Validator::make($request, $rules);
52+
$validator->validate();
53+
}
54+
55+
public function applyFilters($query, array $filters)
56+
{
57+
$this->validate($filters);
58+
59+
// Eager load relations
60+
$query->with(['tags', 'votes', 'upvoteSummary', 'reviewSummary', 'commentsCountRelationship']);
61+
62+
// Fulltext search on name
63+
if (!empty($filters['name'])) {
64+
$query->whereFullText('name', $filters['name']);
65+
}
66+
67+
// Fulltext search on description
68+
if (!empty($filters['description'])) {
69+
$query->whereFullText('description', $filters['description']);
70+
}
71+
72+
// Filter by platforms (array)
73+
if (!empty($filters['platforms'])) {
74+
$query->where(function ($q) use ($filters) {
75+
foreach ($filters['platforms'] as $platform) {
76+
$q->orWhereRaw('FIND_IN_SET(?, platforms)', [$platform]);
77+
}
78+
});
79+
}
80+
81+
// Filter by difficulty
82+
if (!empty($filters['difficulty'])) {
83+
$query->whereIn('difficulty', (array) $filters['difficulty']);
84+
}
85+
86+
// Filter by pricing
87+
if (!empty($filters['pricing'])) {
88+
$query->whereIn('pricing', (array) $filters['pricing']);
89+
}
90+
91+
// Filter by topic tags
92+
if (!empty($filters['topics'])) {
93+
$query->withAnyTags((array) $filters['topics'], 'topics');
94+
}
95+
96+
// Filter by programming languages
97+
if (!empty($filters['programming_languages'])) {
98+
$query->withAnyTags((array) $filters['programming_languages'], 'programming_languages');
99+
}
100+
101+
// Filter by general tags
102+
if (!empty($filters['general_tags'])) {
103+
$query->withAnyTags((array) $filters['general_tags'], 'general_tags');
104+
}
105+
106+
// Filter by reviews
107+
$ratingFilters = [
108+
'community',
109+
'teaching_clarity',
110+
'engagement',
111+
'practicality',
112+
'user_friendliness',
113+
'updates',
114+
'overall',
115+
];
116+
117+
foreach ($ratingFilters as $field) {
118+
if (!empty($filters[$field])) {
119+
$query = $this->reviewService->applyRatingFilter($query, $field, $filters[$field]);
120+
}
121+
}
122+
123+
// Filter by Date posted
124+
if (!empty($filters['created_from'])) {
125+
$query->whereDate('computer_science_resources.created_at', '>=', $filters['created_from']);
126+
}
127+
if (!empty($filters['created_to'])) {
128+
$query->whereDate('computer_science_resources.created_at', '<=', $filters['created_to']);
129+
}
130+
131+
// Filter by Date updated
132+
if (!empty($filters['updated_from'])) {
133+
$query->whereDate('computer_science_resources.updated_at', '>=', $filters['updated_from']);
134+
}
135+
if (!empty($filters['updated_to'])) {
136+
$query->whereDate('computer_science_resources.updated_at', '<=', $filters['updated_to']);
137+
}
138+
139+
// Sorting
140+
$sortBy = $filters['sort_by'] ?? 'top';
141+
$query = $this->resourceSortingManager->applySort($query, $sortBy);
142+
143+
if (($filters['reverse'] ?? 'false') === 'true') {
144+
$query = $this->resourceSortingManager->reverse($query);
145+
}
146+
147+
return $query;
148+
}
149+
}

tests/Feature/ComputerScienceResourceControllerTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use PHPUnit\Framework\Attributes\DataProvider;
1111
use Tests\TestCase;
1212
use Tests\TestResources\ComputerScienceResourceTestResource;
13+
use PHPUnit\Framework\Attributes\Group;
14+
use PHPUnit\Framework\Attributes\Test;
1315

1416
class ComputerScienceResourceControllerTest extends TestCase
1517
{
@@ -83,6 +85,8 @@ public static function invalidFieldProvider(): array
8385
}
8486

8587
#[DataProvider('invalidFieldProvider')]
88+
#[Test]
89+
#[Group('slow')]
8690
public function test_cannot_post_resource_with_invalid_fields(string $field, mixed $invalidValue)
8791
{
8892
$this->actingAs($this->user);

0 commit comments

Comments
 (0)