1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Models;
7
8use Carbon\Carbon;
9
10/**
11 * @property BeatmapDiscussion $beatmapDiscussion
12 * @property int $beatmap_discussion_id
13 * @property \Carbon\Carbon|null $created_at
14 * @property int $id
15 * @property int $score
16 * @property \Carbon\Carbon|null $updated_at
17 * @property User $user
18 * @property int|null $user_id
19 */
20class BeatmapDiscussionVote extends Model
21{
22 protected $touches = ['beatmapDiscussion'];
23
24 public static function recentlyReceivedByUser($userId, $timeframeMonths = 3)
25 {
26 return static::where('beatmap_discussion_votes.created_at', '>', Carbon::now()->subMonths($timeframeMonths))
27 ->join('beatmap_discussions', 'beatmap_discussion_votes.beatmap_discussion_id', 'beatmap_discussions.id')
28 ->select('beatmap_discussion_votes.user_id')
29 ->selectRaw('sum(beatmap_discussion_votes.score) as score')
30 ->selectRaw('count(beatmap_discussion_votes.score) as count')
31 ->where('beatmap_discussions.user_id', $userId)
32 ->where('beatmap_discussions.updated_at', '>', Carbon::now()->subMonths($timeframeMonths))
33 ->whereHas('user', function ($userQuery) {
34 $userQuery->default();
35 })
36 ->groupBy('beatmap_discussion_votes.user_id')
37 ->orderByDesc('count')
38 ->get();
39 }
40
41 public static function recentlyGivenByUser($userId, $timeframeMonths = 3)
42 {
43 return static::where('beatmap_discussion_votes.created_at', '>', Carbon::now()->subMonths($timeframeMonths))
44 ->join('beatmap_discussions', 'beatmap_discussion_votes.beatmap_discussion_id', 'beatmap_discussions.id')
45 ->select('beatmap_discussions.user_id')
46 ->selectRaw('sum(beatmap_discussion_votes.score) as score')
47 ->selectRaw('count(beatmap_discussion_votes.score) as count')
48 ->where('beatmap_discussion_votes.user_id', $userId)
49 ->where('beatmap_discussions.updated_at', '>', Carbon::now()->subMonths($timeframeMonths))
50 ->whereHas('beatmapDiscussion.user', function ($userQuery) {
51 $userQuery->default();
52 })
53 ->groupBy('beatmap_discussions.user_id')
54 ->orderByDesc('count')
55 ->get();
56 }
57
58 public static function search($rawParams = [])
59 {
60 [$query, $params] = static::searchQueryAndParams(cursor_from_params($rawParams) ?? $rawParams);
61
62 $isModerator = $rawParams['is_moderator'] ?? false;
63
64 if (isset($rawParams['user'])) {
65 $params['user'] = $rawParams['user'];
66 $findAll = $isModerator || (($rawParams['current_user_id'] ?? null) === $rawParams['user']);
67 $user = User::lookup($params['user'], null, $findAll);
68
69 if ($user === null) {
70 $query->none();
71 } else {
72 $query->where('user_id', $user->getKey());
73 }
74 }
75
76 if (isset($rawParams['receiver'])) {
77 $params['receiver'] = $rawParams['receiver'];
78 $user = User::lookup($params['receiver']);
79
80 if ($user === null) {
81 $query->none();
82 } else {
83 $query->whereIn('beatmap_discussion_id', BeatmapDiscussion::where('user_id', '=', $user->getKey())->select('id'));
84 }
85 }
86
87 if (isset($rawParams['sort'])) {
88 $sort = explode('_', strtolower($rawParams['sort']));
89
90 if (in_array($sort[0] ?? null, ['id'], true)) {
91 $sortField = $sort[0];
92 }
93
94 if (in_array($sort[1] ?? null, ['asc', 'desc'], true)) {
95 $sortOrder = $sort[1];
96 }
97 }
98
99 $sortField ?? ($sortField = 'id');
100 $sortOrder ?? ($sortOrder = 'desc');
101
102 $params['sort'] = "{$sortField}_{$sortOrder}";
103 $query->orderBy($sortField, $sortOrder);
104
105 if (isset($rawParams['score'])) {
106 $params['score'] = get_int($rawParams['score']);
107 if ($params['score'] !== null) {
108 $query->where('score', '=', $params['score']);
109 }
110 }
111
112 $params['beatmapset_discussion_id'] = get_int($rawParams['beatmapset_discussion_id'] ?? null);
113 if ($params['beatmapset_discussion_id'] !== null) {
114 // column name is beatmap_ =)
115 $query->where('beatmap_discussion_id', $params['beatmapset_discussion_id']);
116 }
117
118 // TODO: normalize with main beatmapset discussion behaviour (needs React-side fixing)
119 if (!isset($user) && !$isModerator) {
120 $query->whereHas('user', function ($userQuery) {
121 $userQuery->default();
122 });
123 }
124
125 return ['query' => $query, 'params' => $params];
126 }
127
128 public function beatmapDiscussion()
129 {
130 return $this->belongsTo(BeatmapDiscussion::class);
131 }
132
133 public function user()
134 {
135 return $this->belongsTo(User::class, 'user_id');
136 }
137
138 public function setScoreAttribute($value)
139 {
140 if ($value > 0) {
141 $value = 1;
142 } elseif ($value < 0) {
143 $value = -1;
144 } else {
145 $value = 0;
146 }
147
148 $this->attributes['score'] = $value;
149 }
150
151 public function forEvent()
152 {
153 return [
154 'user_id' => $this->user_id,
155 'score' => $this->score,
156 ];
157 }
158}