Skip to content

Commit 55cb344

Browse files
authored
Merge pull request ppy#12059 from notbakaneko/feature/beatmap-tags-ruleset
Add per ruleset tag support
2 parents 53920ab + 0b38e11 commit 55cb344

File tree

6 files changed

+87
-34
lines changed

6 files changed

+87
-34
lines changed

app/Http/Controllers/BeatmapTagsController.php

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace App\Http\Controllers;
99

10+
use App\Exceptions\InvariantException;
1011
use App\Models\Beatmap;
1112
use App\Models\BeatmapTag;
1213
use App\Models\Tag;
@@ -42,6 +43,10 @@ public function update($beatmapId, $tagId)
4243

4344
$tag = Tag::findOrFail($tagId);
4445

46+
if ($tag->ruleset_id !== null && $tag->ruleset_id !== $beatmap->playmode) {
47+
throw new InvariantException(osu_trans('beatmap_tags.update.invalid_ruleset'));
48+
}
49+
4550
$tag
4651
->beatmapTags()
4752
->firstOrCreate(['beatmap_id' => $beatmapId, 'user_id' => \Auth::user()->getKey()]);

app/Models/Tag.php

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
/**
1414
* @property int $id
1515
* @property string $name
16+
* @property ?int $ruleset_id
1617
* @property string $description
1718
* @property-read Collection<BeatmapTag> $beatmapTags
1819
*/

database/factories/TagFactory.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class TagFactory extends Factory
1616
public function definition(): array
1717
{
1818
return [
19-
'name' => fn () => "Tag {$this->faker->unique()->word}",
2019
'description' => fn () => $this->faker->sentence,
20+
'name' => fn () => "Tag {$this->faker->unique()->word}",
2121
];
2222
}
2323
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
6+
declare(strict_types=1);
7+
8+
use Illuminate\Database\Migrations\Migration;
9+
use Illuminate\Database\Schema\Blueprint;
10+
use Illuminate\Support\Facades\Schema;
11+
12+
return new class extends Migration
13+
{
14+
public function up(): void
15+
{
16+
Schema::table('tags', function (Blueprint $table) {
17+
$table->smallInteger('ruleset_id')->nullable()->unsigned()->after('name');
18+
$table->dropIndex('tags_name_unique');
19+
});
20+
}
21+
22+
public function down(): void
23+
{
24+
Schema::table('tags', function (Blueprint $table) {
25+
$table->unique('name', 'tags_name_unique');
26+
$table->dropColumn('ruleset_id');
27+
});
28+
}
29+
};

resources/lang/en/beatmap_tags.php

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
6+
return [
7+
'update' => [
8+
'invalid_ruleset' => 'This tag is not valid with this Beatmap.',
9+
],
10+
];

tests/Controllers/BeatmapTagsControllerTest.php

+41-33
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,61 @@
1616

1717
class BeatmapTagsControllerTest extends TestCase
1818
{
19-
private Tag $tag;
20-
private Beatmap $beatmap;
21-
private BeatmapTag $beatmapTag;
19+
public static function dataProviderForUpdate(): array
20+
{
21+
return [
22+
[0, null, true],
23+
[0, 0, true],
24+
[0, 1, false],
25+
];
26+
}
2227

23-
public function testStore(): void
28+
public function testDestroy(): void
2429
{
25-
$user = User::factory()
26-
->has(Score::factory()->state(['beatmap_id' => $this->beatmap]), 'soloScores')
27-
->create();
30+
$beatmapTag = BeatmapTag::factory()->create();
2831

29-
$this->expectCountChange(fn () => BeatmapTag::count(), 1);
32+
$this->expectCountChange(fn () => BeatmapTag::count(), -1);
3033

31-
$this->actAsScopedUser($user);
34+
$this->actAsScopedUser($beatmapTag->user);
3235
$this
33-
->put(route('api.beatmaps.tags.update', ['beatmap' => $this->beatmap->getKey(), 'tag' => $this->tag->getKey()]))
36+
->delete(route('api.beatmaps.tags.destroy', ['beatmap' => $beatmapTag->beatmap_id, 'tag' => $beatmapTag->tag_id]))
3437
->assertSuccessful();
3538
}
3639

37-
public function testStoreNoScore(): void
40+
/**
41+
* @dataProvider dataProviderForUpdate
42+
*/
43+
public function testUpdate(int $beatmapRulesetId, ?int $tagRulesetId, bool $successful): void
3844
{
39-
$this->expectCountChange(fn () => BeatmapTag::count(), 0);
45+
$tag = Tag::factory()->state(['ruleset_id' => $tagRulesetId])->create();
46+
$beatmap = Beatmap::factory()->state(['playmode' => $beatmapRulesetId])->create();
4047

41-
$this->actAsScopedUser(User::factory()->create());
42-
$this
43-
->put(route('api.beatmaps.tags.update', ['beatmap' => $this->beatmap->getKey(), 'tag' => $this->tag->getKey()]))
44-
->assertForbidden();
45-
}
48+
$user = User::factory()
49+
->has(Score::factory()->state(['beatmap_id' => $beatmap]), 'soloScores')
50+
->create();
4651

47-
public function testDestroy(): void
48-
{
49-
$this->expectCountChange(fn () => BeatmapTag::count(), -1);
52+
$this->expectCountChange(fn () => BeatmapTag::count(), $successful ? 1 : 0);
5053

51-
$this->actAsScopedUser($this->beatmapTag->user);
52-
$this
53-
->delete(route('api.beatmaps.tags.destroy', ['beatmap' => $this->beatmap->getKey(), 'tag' => $this->tag->getKey()]))
54-
->assertSuccessful();
54+
$this->actAsScopedUser($user);
55+
$request = $this->put(route('api.beatmaps.tags.update', ['beatmap' => $beatmap->getKey(), 'tag' => $tag->getKey()]));
56+
57+
if ($successful) {
58+
$request->assertSuccessful();
59+
} else {
60+
$request->assertStatus(422);
61+
}
5562
}
5663

57-
protected function setUp(): void
64+
public function testUpdateNoScore(): void
5865
{
59-
parent::setUp();
60-
61-
$this->tag = Tag::factory()->create();
62-
$this->beatmap = Beatmap::factory()->create();
63-
$this->beatmapTag = BeatmapTag::factory()->create([
64-
'tag_id' => $this->tag,
65-
'beatmap_id' => $this->beatmap,
66-
]);
66+
$tag = Tag::factory()->create();
67+
$beatmap = Beatmap::factory()->create();
68+
69+
$this->expectCountChange(fn () => BeatmapTag::count(), 0);
70+
71+
$this->actAsScopedUser(User::factory()->create());
72+
$this
73+
->put(route('api.beatmaps.tags.update', ['beatmap' => $beatmap->getKey(), 'tag' => $tag->getKey()]))
74+
->assertForbidden();
6775
}
6876
}

0 commit comments

Comments
 (0)