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\Jobs\Notifications\UserAchievementUnlock;
9use Exception;
10use Illuminate\Database\Eloquent\Relations\BelongsTo;
11use Illuminate\Support\Facades\DB;
12
13/**
14 * @property-read Achievement $achievement
15 * @property int $achievement_id
16 * @property-read Beatmap|null $beatmap
17 * @property int|null $beatmap_id
18 * @property \Carbon\Carbon $date
19 * @property-read string $date_json
20 * @property-read User $user
21 * @property int $user_id
22 */
23class UserAchievement extends Model
24{
25 public $incrementing = false;
26 public $timestamps = false;
27
28 protected $casts = ['date' => 'datetime'];
29 protected $primaryKey = ':composite';
30 protected $primaryKeys = ['user_id', 'achievement_id'];
31 protected $table = 'osu_user_achievements';
32
33 /**
34 * Unlock the medal for the given user.
35 */
36 public static function unlock(User $user, Achievement $achievement, ?Beatmap $beatmap = null): bool
37 {
38 return DB::transaction(function () use ($achievement, $beatmap, $user) {
39 try {
40 $userAchievement = $user->userAchievements()->create([
41 'achievement_id' => $achievement->getKey(),
42 'beatmap_id' => $beatmap?->getKey(),
43 ]);
44 } catch (Exception $e) {
45 if (is_sql_unique_exception($e)) {
46 return false;
47 }
48
49 throw $e;
50 }
51
52 Event::generate('achievement', compact('achievement', 'user'));
53
54 (new UserAchievementUnlock($achievement, $user))->dispatch();
55
56 return true;
57 });
58 }
59
60 public function achievement(): BelongsTo
61 {
62 return $this->belongsTo(Achievement::class, 'achievement_id');
63 }
64
65 public function beatmap(): BelongsTo
66 {
67 return $this->belongsTo(Beatmap::class, 'beatmap_id');
68 }
69
70 public function user(): BelongsTo
71 {
72 return $this->belongsTo(User::class, 'user_id');
73 }
74
75 public function getAttribute($key)
76 {
77 return match ($key) {
78 'achievement_id',
79 'beatmap_id',
80 'user_id' => $this->getRawAttribute($key),
81
82 'date' => $this->getTimeFast($key),
83
84 'date_json' => $this->getJsonTimeFast($key),
85
86 'achievement',
87 'beatmap',
88 'user' => $this->getRelationValue($key),
89 };
90 }
91}