-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
411 additions
and
4 deletions.
There are no files selected for viewing
136 changes: 136 additions & 0 deletions
136
app/Http/Controllers/Api/v2/Minecraft/MinecraftBuildController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
<?php | ||
|
||
namespace App\Http\Controllers\Api\v2\Minecraft; | ||
|
||
use App\Core\Domains\MinecraftUUID\Data\MinecraftUUID; | ||
use App\Core\Domains\MinecraftUUID\Rules\MinecraftUUIDRule; | ||
use App\Http\Controllers\ApiController; | ||
use App\Models\MinecraftBuild; | ||
use App\Models\MinecraftBuildVote; | ||
use App\Models\MinecraftPlayer; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Support\Facades\DB; | ||
use Illuminate\Validation\Rule; | ||
|
||
final class MinecraftBuildController extends ApiController | ||
{ | ||
public function index(Request $request) | ||
{ | ||
$request->validate([ | ||
'page_size' => ['integer', 'gt:0'], | ||
]); | ||
|
||
$defaultSize = 25; | ||
$pageSize = min($defaultSize, $request->get('page_size', $defaultSize)); | ||
|
||
return MinecraftBuild::orderBy('votes', 'desc') | ||
->paginate($pageSize); | ||
} | ||
|
||
public function show(Request $request, MinecraftBuild $build) | ||
{ | ||
$build->load('player'); | ||
return $build; | ||
} | ||
|
||
public function store(Request $request) | ||
{ | ||
$input = $request->validate([ | ||
'player_uuid' => ['required', new MinecraftUUIDRule], | ||
'name' => ['required', Rule::unique(MinecraftBuild::tableName())], | ||
'world' => 'required|string', | ||
'x' => 'required|numeric', | ||
'y' => 'required|numeric', | ||
'z' => 'required|numeric', | ||
'pitch' => 'required|numeric', | ||
'yaw' => 'required|numeric', | ||
]); | ||
|
||
$player = MinecraftPlayer::firstOrCreate( | ||
uuid: new MinecraftUUID($input['player_uuid']), | ||
alias: $request->get('alias'), | ||
); | ||
$input['player_id'] = $player->getKey(); | ||
|
||
return MinecraftBuild::create($input); | ||
} | ||
|
||
public function update(Request $request, MinecraftBuild $build) | ||
{ | ||
$input = $request->validate([ | ||
'player_uuid' => ['required', new MinecraftUUIDRule], | ||
'name' => ['required', Rule::unique(MinecraftBuild::tableName())->ignore($build)], | ||
'world' => 'required|string', | ||
'x' => 'required|numeric', | ||
'y' => 'required|numeric', | ||
'z' => 'required|numeric', | ||
'pitch' => 'required|numeric', | ||
'yaw' => 'required|numeric', | ||
]); | ||
|
||
$this->assertHasWriteAccess(build: $build, uuid: $input['player_uuid']); | ||
|
||
$build->update($request->all()); | ||
|
||
return $build; | ||
} | ||
|
||
public function patch(Request $request, MinecraftBuild $build) | ||
{ | ||
$input = $request->validate([ | ||
'player_uuid' => ['required', new MinecraftUUIDRule], | ||
'name' => [Rule::unique(MinecraftBuild::tableName())->ignore($build)], | ||
]); | ||
|
||
$this->assertHasWriteAccess(build: $build, uuid: $input['player_uuid']); | ||
|
||
if ($request->has('name')) { | ||
$name = $request->get('name'); | ||
$build->name = empty($name) ? null : $name; | ||
} | ||
if ($request->has('description')) { | ||
$description = $request->get('description'); | ||
$build->description = empty($description) ? null : $description; | ||
} | ||
if ($request->has('lore')) { | ||
$lore = $request->get('lore'); | ||
$build->lore = empty($lore) ? null : $lore; | ||
} | ||
$build->save(); | ||
|
||
return $build; | ||
} | ||
|
||
public function destroy(Request $request, MinecraftBuild $build) | ||
{ | ||
$input = $request->validate([ | ||
'player_uuid' => ['required', new MinecraftUUIDRule], | ||
]); | ||
|
||
$this->assertHasWriteAccess(build: $build, uuid: $input['player_uuid']); | ||
|
||
DB::transaction(function () use ($build) { | ||
MinecraftBuildVote::where('build_id', $build->getKey())->delete(); | ||
$build->delete(); | ||
}); | ||
|
||
return response()->json(); | ||
} | ||
|
||
/** | ||
* Ensures the given player UUID has the ability to modify the given build. | ||
* Basically checks the UUID is the build owner or a staff member. | ||
*/ | ||
private function assertHasWriteAccess(MinecraftBuild $build, string $uuid): void | ||
{ | ||
$uuid = new MinecraftUUID($uuid); | ||
$player = MinecraftPlayer::whereUuid($uuid)->with('account.groups')->first(); | ||
abort_if($player === null, 403, "Player not found"); | ||
|
||
$isBuildOwner = $build->player_id === $player->getKey(); | ||
|
||
$groups = $player->account?->groups ?? collect(); | ||
$isStaff = $groups->where('group_type', 'staff')->isNotEmpty(); | ||
abort_if(!$isBuildOwner && !$isStaff, 403, "Only the build owner can edit this build"); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
app/Http/Controllers/Api/v2/Minecraft/MinecraftBuildNameController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
namespace App\Http\Controllers\Api\v2\Minecraft; | ||
|
||
use App\Http\Controllers\ApiController; | ||
use App\Models\MinecraftBuild; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Validation\Rule; | ||
|
||
final class MinecraftBuildNameController extends ApiController | ||
{ | ||
public function index(Request $request) | ||
{ | ||
return MinecraftBuild::get(['id', 'name']); | ||
} | ||
} |
86 changes: 86 additions & 0 deletions
86
app/Http/Controllers/Api/v2/Minecraft/MinecraftBuildVoteController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
namespace App\Http\Controllers\Api\v2\Minecraft; | ||
|
||
use App\Core\Domains\MinecraftUUID\Data\MinecraftUUID; | ||
use App\Core\Domains\MinecraftUUID\Rules\MinecraftUUIDRule; | ||
use App\Http\Controllers\ApiController; | ||
use App\Models\MinecraftBuild; | ||
use App\Models\MinecraftBuildVote; | ||
use App\Models\MinecraftPlayer; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Support\Facades\DB; | ||
use Illuminate\Validation\ValidationException; | ||
|
||
final class MinecraftBuildVoteController extends ApiController | ||
{ | ||
public function store(Request $request, MinecraftBuild $build) | ||
{ | ||
$input = $request->validate([ | ||
'player_uuid' => ['required', new MinecraftUUIDRule], | ||
'alias' => ['string'], | ||
]); | ||
|
||
$player = MinecraftPlayer::firstOrCreate( | ||
uuid: MinecraftUUID::tryParse($input['player_uuid']), | ||
alias: $request->get('alias'), | ||
); | ||
|
||
if ($build->player_id === $player->getKey()) { | ||
throw ValidationException::withMessages([ | ||
'error' => 'You cannot vote for your own build', | ||
]); | ||
} | ||
|
||
$alreadyVoted = MinecraftBuildVote::where('player_id', $player->getKey()) | ||
->where('build_id', $build->getKey()) | ||
->exists(); | ||
|
||
if ($alreadyVoted) { | ||
throw ValidationException::withMessages([ | ||
'error' => 'You have already voted for this build', | ||
]); | ||
} | ||
|
||
DB::transaction(function () use ($player, $build) { | ||
$build->votes = $build->votes + 1; | ||
$build->save(); | ||
|
||
MinecraftBuildVote::create([ | ||
'player_id' => $player->getKey(), | ||
'build_id' => $build->getKey(), | ||
]); | ||
}); | ||
|
||
return response()->json($build); | ||
} | ||
|
||
public function destroy(Request $request, MinecraftBuild $build) | ||
{ | ||
$input = $request->validate([ | ||
'player_uuid' => ['required', new MinecraftUUIDRule], | ||
]); | ||
|
||
$player = MinecraftPlayer::whereUuid(MinecraftUUID::tryParse($input['player_uuid'])) | ||
->first(); | ||
|
||
$vote = MinecraftBuildVote::where('player_id', $player->getKey()) | ||
->where('build_id', $build->getKey()) | ||
->first(); | ||
|
||
if ($vote === null) { | ||
throw ValidationException::withMessages([ | ||
'error' => 'You have not voted for this build', | ||
]); | ||
} | ||
|
||
DB::transaction(function () use ($vote, $build) { | ||
$build->votes = $build->votes - 1; | ||
$build->save(); | ||
|
||
$vote->delete(); | ||
}); | ||
|
||
return response()->json($build); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace App\Models; | ||
|
||
use App\Core\Utilities\Traits\HasStaticTable; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
|
||
class MinecraftBuild extends Model | ||
{ | ||
use HasFactory; | ||
use HasStaticTable; | ||
|
||
protected $table = 'minecraft_builds'; | ||
|
||
protected $fillable = [ | ||
'player_id', | ||
'name', | ||
'description', | ||
'lore', | ||
'votes', | ||
'world', | ||
'x', | ||
'y', | ||
'z', | ||
'pitch', | ||
'yaw', | ||
'created_at', | ||
'updated_at', | ||
]; | ||
|
||
protected $casts = [ | ||
'pitch' => 'decimal:1', | ||
'yaw' => 'decimal:1', | ||
]; | ||
|
||
public function player(): BelongsTo | ||
{ | ||
return $this->belongsTo( | ||
related: MinecraftPlayer::class, | ||
foreignKey: 'player_id', | ||
ownerKey: MinecraftPlayer::primaryKey(), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
namespace App\Models; | ||
|
||
use App\Core\Utilities\Traits\HasStaticTable; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
|
||
class MinecraftBuildVote extends Model | ||
{ | ||
use HasFactory; | ||
use HasStaticTable; | ||
|
||
protected $table = 'minecraft_build_votes'; | ||
|
||
protected $fillable = [ | ||
'build_id', | ||
'player_id', | ||
'created_at', | ||
'updated_at', | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace Database\Factories; | ||
|
||
use App\Models\MinecraftBuild; | ||
|
||
class MinecraftBuildFactory extends Factory | ||
{ | ||
protected $model = MinecraftBuild::class; | ||
|
||
public function definition() | ||
{ | ||
return [ | ||
'name' => strtolower($this->faker->unique()->name), | ||
'world' => $this->faker->randomElement(['creative', 'survival', 'monarch']), | ||
'x' => $this->faker->numberBetween(0, 20000), | ||
'y' => $this->faker->numberBetween(0, 20000), | ||
'z' => $this->faker->numberBetween(0, 20000), | ||
'pitch' => $this->faker->randomFloat(nbMaxDecimals: 1, min: 0, max: 100), | ||
'yaw' => $this->faker->randomFloat(nbMaxDecimals: 1, min: 0, max: 100), | ||
'created_at' => $this->faker->dateTime, | ||
'updated_at' => $this->faker->dateTime, | ||
]; | ||
} | ||
} |
Oops, something went wrong.