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 App\Exceptions\InvariantException;
9use App\Models\Store\Product;
10use Carbon\Carbon;
11
12/**
13 * @property \Carbon\Carbon|null $created_at
14 * @property string $description
15 * @property \Carbon\Carbon $end_date
16 * @property string|null $header_banner
17 * @property string|null $info_url
18 * @property string $name
19 * @property int $play_mode
20 * @property \Illuminate\Database\Eloquent\Collection $profileBanners ProfileBanner
21 * @property int|null $rank_max
22 * @property int|null $rank_min
23 * @property \Illuminate\Database\Eloquent\Collection $registrations TournamentRegistration
24 * @property \Carbon\Carbon $signup_close
25 * @property \Carbon\Carbon $signup_open
26 * @property \Carbon\Carbon $start_date
27 * @property int|null $tournament_banner_product_id
28 * @property int $tournament_id
29 * @property \Carbon\Carbon|null $updated_at
30 */
31class Tournament extends Model
32{
33 protected $primaryKey = 'tournament_id';
34
35 protected $casts = [
36 'end_date' => 'datetime',
37 'signup_close' => 'datetime',
38 'signup_open' => 'datetime',
39 'start_date' => 'datetime',
40 ];
41
42 public static function getGroupedListing()
43 {
44 $tournaments = static::query()
45 ->with('registrations')
46 ->orderBy('tournament_id', 'desc')
47 ->get();
48
49 $now = Carbon::now();
50
51 return [
52 'current' => $tournaments->where('end_date', '>', $now),
53 'previous' => $tournaments->where('end_date', '<=', $now),
54 ];
55 }
56
57 public function profileBanners()
58 {
59 return $this->hasMany(ProfileBanner::class);
60 }
61
62 public function registrations()
63 {
64 return $this->hasMany(TournamentRegistration::class);
65 }
66
67 public function product()
68 {
69 return $this->belongsTo(Product::class, 'tournament_banner_product_id');
70 }
71
72 public function isRegistrationOpen()
73 {
74 $now = Carbon::now();
75
76 return $this->signup_open < $now && $this->signup_close > $now;
77 }
78
79 public function isTournamentRunning()
80 {
81 $now = Carbon::now();
82
83 return $this->start_date < $now && $this->end_date > $now;
84 }
85
86 public function isStoreBannerAvailable()
87 {
88 return $this->tournament_banner_product_id !== null && $this->product->isAvailable();
89 }
90
91 public function isSignedUp($user)
92 {
93 if (!$user) {
94 return false;
95 }
96
97 return $this->registrations()->where('user_id', '=', $user->user_id)->exists();
98 }
99
100 public function isValidRank($user)
101 {
102 if (!$user) {
103 return false;
104 }
105
106 $userRank = UserStatistics\Model
107 ::getClass($this->playModeStr(), $this->play_mode_variant)
108 ::firstOrNew(['user_id' => $user->getKey()])
109 ->globalRank();
110
111 if ($this->rank_min !== null && ($userRank === null || $this->rank_min > $userRank)) {
112 return false;
113 }
114
115 if ($this->rank_max !== null && ($userRank === null || $this->rank_max < $userRank)) {
116 return false;
117 }
118
119 return true;
120 }
121
122 public function unregister($user)
123 {
124 //sanity check: we shouldn't be touching users once the tournament is already in action.
125 if ($this->isTournamentRunning()) {
126 throw new InvariantException('tournament is already running');
127 }
128
129 $this->registrations()->where('user_id', '=', $user->user_id)->delete();
130 }
131
132 public function register($user)
133 {
134 if ($this->isSignedUp($user)) {
135 return;
136 }
137
138 //sanity check: we shouldn't be touching users once the tournament is already in action.
139 if ($this->isTournamentRunning()) {
140 throw new InvariantException('tournament is already running');
141 }
142
143 $reg = new TournamentRegistration();
144 $reg->user()->associate($user);
145
146 $this->registrations()->save($reg);
147 }
148
149 public function playModeStr()
150 {
151 return Beatmap::modeStr($this->play_mode);
152 }
153
154 public function pageLinks()
155 {
156 $links = [];
157
158 if ($this->info_url !== null) {
159 $links[] = [
160 'url' => $this->info_url,
161 'title' => osu_trans('tournament.show.info_page'),
162 ];
163 }
164
165 if ($this->isStoreBannerAvailable()) {
166 $links[] = [
167 'url' => route('store.products.show', $this->tournament_banner_product_id),
168 'title' => osu_trans('tournament.show.banner'),
169 ];
170 }
171
172 return $links;
173 }
174}