-
Notifications
You must be signed in to change notification settings - Fork 0
#41 - report system #57
base: main
Are you sure you want to change the base?
Changes from 151 commits
68e8220
cb3a938
478614b
c4cc684
476f850
75ed108
b187077
627985f
e9075b5
6c06208
0b8d97b
4080f09
cd29821
33fea8f
ebc9be1
0c0da65
7ce97e7
f407303
099b6e7
56509a3
c272a66
c22bfde
ccc30af
6c0931e
2e98425
b08535e
e8a9a44
034dcb9
2a7fbd3
709835f
5171231
a5c5c96
c2251ba
9c3f7e2
dd81354
5f0ca90
a764c1f
5e87c1a
debf707
1cb1344
6fbac51
db99f04
41bfdfb
b89bebd
f52f1bd
9cd1a98
7368847
6f05d10
a14de99
96c6a4f
7979a6a
a666a63
6f7ea17
c419df0
4b1255c
077e28c
7f1241e
c79ec87
1b87b4b
afc8b39
3348dab
17e17a4
0bae801
f14c69f
51c0243
eec729d
c7edb1c
6f40241
e0d6a0b
b8446ba
78c0553
0e98908
79ebabf
956a82b
d8a2f85
70ed748
201f60a
0f816ac
d2175b8
a33603c
035fc7d
a8676fc
5b1724f
c09ff5d
2421d22
37998b8
6e4118b
ff56387
cf3a732
103c798
65db078
8913631
f0b0b5f
22bd981
049116f
3ef0756
e1376b0
d65c578
ec39b1d
d87c7ac
ba6cf67
4059b46
d10a8f4
0eb0c54
c67ad34
611abdf
5e6664f
4ffbcb1
3e9022d
85e36a0
e364580
598acb4
937eb51
5d2c362
97ba1b2
bb82d7f
030a3e9
bb9404b
4597d19
0267a7c
ce2a94c
931b097
7b470a3
c7f27c5
974fdd5
5b8576d
975a2fb
14aa9f6
385b926
e1b727a
bf1dfd0
f19cfd0
10b24b3
4489c45
5aa8863
ab4223e
c15e4dd
389eaa7
3397181
4f871eb
c777627
36e9923
1c8936c
435533e
b77a1de
a9239d1
7238778
9620294
85909b5
7b23849
2ab970f
c349a59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,78 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| declare(strict_types=1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Interns2025b\Http\Controllers; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Illuminate\Http\JsonResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Interns2025b\Http\Requests\StoreReportRequest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Interns2025b\Http\Resources\ReportResource; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Interns2025b\Models\Event; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Interns2025b\Models\Organization; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Interns2025b\Models\Report; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Interns2025b\Models\User; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Symfony\Component\HttpFoundation\Response as Status; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class ReportController | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public function store(StoreReportRequest $request): JsonResponse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $user = $request->user(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $type = match ($request->input("type")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "user" => User::class, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "organization" => Organization::class, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "event" => Event::class, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $alreadyReported = Report::alreadyReportedToday( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $user->id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (int)$request->input("id"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ($alreadyReported) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response()->json([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "message" => __("report.already_reported"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], Status::HTTP_TOO_MANY_REQUESTS); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Report::create([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "reporter_id" => $user->id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "reportable_type" => $type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "reportable_id" => $request->input("id"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "reason" => $request->input("reason"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response()->json([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "message" => __("report.success"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], Status::HTTP_OK); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public function userReports(): JsonResponse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response()->json([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "data" => ReportResource::collection( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Report::query()->where("reportable_type", User::class)->latest()->get(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Report::query()->where("reportable_type", User::class)->latest()->get(), | |
| Report::query()->where("reportable_type", User::class)->latest()->paginate($request->query('per_page', 15)), |
Copilot
AI
Jul 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using get() without pagination could cause performance issues when there are many reports. Consider implementing pagination for these admin endpoints.
| return response()->json([ | |
| "data" => ReportResource::collection( | |
| Report::query()->where("reportable_type", User::class)->latest()->get(), | |
| ), | |
| ], Status::HTTP_OK); | |
| } | |
| public function organizationReports(): JsonResponse | |
| { | |
| return response()->json([ | |
| "data" => ReportResource::collection( | |
| Report::query()->where("reportable_type", Organization::class)->latest()->get(), | |
| ), | |
| ], Status::HTTP_OK); | |
| } | |
| public function eventReports(): JsonResponse | |
| { | |
| return response()->json([ | |
| "data" => ReportResource::collection( | |
| Report::query()->where("reportable_type", Event::class)->latest()->get(), | |
| $perPage = request()->query('per_page', 15); | |
| return response()->json([ | |
| "data" => ReportResource::collection( | |
| Report::query()->where("reportable_type", User::class)->latest()->paginate($perPage), | |
| ), | |
| ], Status::HTTP_OK); | |
| } | |
| public function organizationReports(): JsonResponse | |
| { | |
| $perPage = request()->query('per_page', 15); | |
| return response()->json([ | |
| "data" => ReportResource::collection( | |
| Report::query()->where("reportable_type", Organization::class)->latest()->paginate($perPage), | |
| ), | |
| ], Status::HTTP_OK); | |
| } | |
| public function eventReports(): JsonResponse | |
| { | |
| $perPage = request()->query('per_page', 15); | |
| return response()->json([ | |
| "data" => ReportResource::collection( | |
| Report::query()->where("reportable_type", Event::class)->latest()->paginate($perPage), |
Copilot
AI
Jul 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using get() without pagination could cause performance issues when there are many reports. Consider implementing pagination for these admin endpoints.
| Report::query()->where("reportable_type", Event::class)->latest()->get(), | |
| Report::query()->where("reportable_type", Event::class)->latest()->paginate(15), |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Interns2025b\Http\Requests; | ||
|
|
||
| use Illuminate\Foundation\Http\FormRequest; | ||
|
|
||
| class StoreReportRequest extends FormRequest | ||
| { | ||
| public function rules(): array | ||
| { | ||
| return [ | ||
| "type" => "required|in:user,organization,event", | ||
| "id" => "required|integer", | ||
| "reason" => "nullable|string|max:1000", | ||
| ]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Interns2025b\Http\Resources; | ||
|
|
||
| use Illuminate\Http\Request; | ||
| use Illuminate\Http\Resources\Json\JsonResource; | ||
|
|
||
| class ReportResource extends JsonResource | ||
| { | ||
| public function toArray(Request $request): array | ||
| { | ||
| return [ | ||
| "id" => $this->id, | ||
| "reporter_id" => $this->reporter_id, | ||
| "reportable_type" => class_basename($this->reportable_type), | ||
| "reportable_id" => $this->reportable_id, | ||
| "reason" => $this->reason, | ||
| ]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Interns2025b\Models; | ||
|
|
||
| use Carbon\Carbon; | ||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
| use Illuminate\Database\Eloquent\Model; | ||
| use Illuminate\Database\Eloquent\Relations\MorphTo; | ||
|
|
||
| /** | ||
| * @property int $reporter_id | ||
| * @property string $reportable_type | ||
| * @property int $reportable_id | ||
| * @property string $reason | ||
| * @property Model|MorphTo $reportable | ||
| */ | ||
| class Report extends Model | ||
| { | ||
PrabuckiDominik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| use HasFactory; | ||
|
|
||
| protected $fillable = [ | ||
| "reporter_id", | ||
| "reportable_type", | ||
| "reportable_id", | ||
| "reason", | ||
| ]; | ||
|
|
||
| public function reportable(): MorphTo | ||
| { | ||
| return $this->morphTo(); | ||
| } | ||
|
|
||
| public static function alreadyReportedToday(int $reporterId, string $reportableType, int $reportableId): bool | ||
| { | ||
| return static::query() | ||
| ->where("reporter_id", $reporterId) | ||
| ->where("reportable_type", $reportableType) | ||
| ->where("reportable_id", $reportableId) | ||
| ->whereDate("created_at", Carbon::today()) | ||
| ->exists(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Database\Factories; | ||
|
|
||
| use Illuminate\Database\Eloquent\Factories\Factory; | ||
| use Interns2025b\Models\Event; | ||
| use Interns2025b\Models\Organization; | ||
| use Interns2025b\Models\Report; | ||
| use Interns2025b\Models\User; | ||
|
|
||
| class ReportFactory extends Factory | ||
| { | ||
| protected $model = Report::class; | ||
|
|
||
| public function definition(): array | ||
| { | ||
| $reportables = [ | ||
| User::class, | ||
| Organization::class, | ||
| Event::class, | ||
| ]; | ||
|
|
||
| $reportableType = $this->faker->randomElement($reportables); | ||
| $reportableId = $reportableType::factory()->create()->id; | ||
|
|
||
| return [ | ||
| "reporter_id" => User::factory()->create()->id, | ||
| "reportable_type" => $reportableType, | ||
| "reportable_id" => $reportableId, | ||
| "reason" => $this->faker->sentence(), | ||
| "created_at" => now(), | ||
| "updated_at" => now(), | ||
| ]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| declare(strict_types=1); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| use Illuminate\Database\Migrations\Migration; | ||||||||||||||||||||||||||||
| use Illuminate\Database\Schema\Blueprint; | ||||||||||||||||||||||||||||
| use Illuminate\Support\Facades\Schema; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return new class() extends Migration { | ||||||||||||||||||||||||||||
| public function up(): void | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| Schema::create("reports", function (Blueprint $table): void { | ||||||||||||||||||||||||||||
| $table->id(); | ||||||||||||||||||||||||||||
| $table->foreignId("reporter_id")->constrained("users")->cascadeOnDelete(); | ||||||||||||||||||||||||||||
| $table->morphs("reportable"); | ||||||||||||||||||||||||||||
| $table->text("reason")->nullable(); | ||||||||||||||||||||||||||||
| $table->timestamps(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| $table->unique([ | ||||||||||||||||||||||||||||
| "reporter_id", | ||||||||||||||||||||||||||||
| "reportable_type", | ||||||||||||||||||||||||||||
| "reportable_id", | ||||||||||||||||||||||||||||
| "created_at", | ||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+23
|
||||||||||||||||||||||||||||
| $table->unique([ | |
| "reporter_id", | |
| "reportable_type", | |
| "reportable_id", | |
| "created_at", | |
| $table->date("created_date")->virtualAs("DATE(created_at)"); | |
| $table->unique([ | |
| "reporter_id", | |
| "reportable_type", | |
| "reportable_id", | |
| "created_date", |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| return [ | ||
| "already_reported" => "You have already reported this today.", | ||
| "success" => "Reported successfully.", | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| return [ | ||
| "already_reported" => "Już zgłosiłeś to dzisiaj.", | ||
| "success" => "Zgłoszono pomyślnie.", | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type mapping logic is duplicated in three methods (store, userReports, organizationReports, eventReports). Consider extracting this into a private method or constant to reduce duplication.