diff --git a/README.md b/README.md index d17daff..a979510 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,14 @@ $files = $client->getModFiles($options); ``` In this case the options are not optional as the mod id is required. +The latest files and the categories of a mod are included in the mod, so no request is needed. +They are returned as `File` and `Category` wrappers: +```php +$latestFiles = $mod->getLatestFiles(); +$categories = $mod->getCategories(); +$primaryCategory = $mod->getPrimaryCategory(); +``` + Just like with mods, you can fetch files individually or in bulk: ```php $file = $client->getModFile(420561, 3787455); diff --git a/src/Client/Mod.php b/src/Client/Mod.php index 5234f8e..56301af 100644 --- a/src/Client/Mod.php +++ b/src/Client/Mod.php @@ -5,6 +5,8 @@ use Aternos\CurseForgeApi\ApiException; use Aternos\CurseForgeApi\Client\List\PaginatedFilesList; use Aternos\CurseForgeApi\Client\Options\ModFilesOptions; +use Aternos\CurseForgeApi\Model\Category as CategoryModel; +use Aternos\CurseForgeApi\Model\File as FileModel; use Aternos\CurseForgeApi\Model\Mod as ModModel; class Mod extends ModModel @@ -62,20 +64,46 @@ public function fetchGame(): Game return $this->client->getGame($this->getGameId()); } + /** + * Get the categories of this mod + * @return Category[]|null + */ + public function getCategories(): ?array + { + $categories = parent::getCategories(); + if ($categories === null) { + return null; + } + return array_map(fn(CategoryModel $category) => new Category($this->client, $category), $categories); + } + /** * Get the primary category of this mod * @return Category|null */ public function getPrimaryCategory(): ?Category { - foreach ($this->getCategories() as $category) { + foreach ($this->getCategories() ?? [] as $category) { if ($category->getId() === $this->getPrimaryCategoryId()) { - return new Category($this->client, $category); + return $category; } } return null; } + /** + * Get the latest files of this mod + * @return File[]|null + */ + public function getLatestFiles(): ?array + { + $files = parent::getLatestFiles(); + if ($files === null) { + return null; + } + return array_map(fn(FileModel $file) => new File($this->client, $file), $files); + } + /** * Fetch the category class of this mod * This sends a request to the CurseForge API every time it is called. diff --git a/tests/Unit/Client/CurseForgeAPIClientTest.php b/tests/Unit/Client/CurseForgeAPIClientTest.php index 8c15b3a..769b7ab 100644 --- a/tests/Unit/Client/CurseForgeAPIClientTest.php +++ b/tests/Unit/Client/CurseForgeAPIClientTest.php @@ -457,6 +457,10 @@ public function testWrapperIsSerializedLikeModel(): void $this->assertSame(420561, $json["id"]); $this->assertSame("mclo-gs", $json["slug"]); $this->assertArrayNotHasKey("client", $json); + $this->assertSame(435, $json["categories"][0]["id"]); + $this->assertArrayNotHasKey("client", $json["categories"][0]); + $this->assertSame(3553911, $json["latestFiles"][0]["id"]); + $this->assertArrayNotHasKey("client", $json["latestFiles"][0]); } /** diff --git a/tests/Unit/Client/ModTest.php b/tests/Unit/Client/ModTest.php index 7c5d7c8..5e8d6db 100644 --- a/tests/Unit/Client/ModTest.php +++ b/tests/Unit/Client/ModTest.php @@ -139,4 +139,63 @@ public function testGetPrimaryCategoryDoesNotSendRequest(): void $this->assertSame(435, $category->getId()); $this->assertRequestCount(0); } + + /** + * @throws ApiException + */ + public function testGetCategoriesReturnsWrappers(): void + { + $client = $this->createClient([ + new Response(200, [], file_get_contents(__DIR__ . "/Fixtures/getMod.json")), + new Response(200, [], file_get_contents(__DIR__ . "/Fixtures/getGame.json")), + ]); + $mod = $client->getMod(420561); + + $categories = $mod->getCategories(); + + $this->assertCount(1, $categories); + $this->assertContainsOnlyInstancesOf(Category::class, $categories); + $this->assertSame(435, $categories[0]->getId()); + $this->assertRequestCount(1); + + $categories[0]->fetchGame(); + $this->assertGetRequest("/v1/games/432", [], 1); + } + + public function testGetCategoriesWithoutCategoriesReturnsNull(): void + { + $mod = new Mod($this->createClient(), new ModModel(["id" => 420561])); + + $this->assertNull($mod->getCategories()); + $this->assertNull($mod->getPrimaryCategory()); + } + + /** + * @throws ApiException + */ + public function testGetLatestFilesReturnsWrappers(): void + { + $client = $this->createClient([ + new Response(200, [], file_get_contents(__DIR__ . "/Fixtures/getMod.json")), + new Response(200, [], file_get_contents(__DIR__ . "/Fixtures/getMod.json")), + ]); + $mod = $client->getMod(420561); + + $files = $mod->getLatestFiles(); + + $this->assertCount(2, $files); + $this->assertContainsOnlyInstancesOf(File::class, $files); + $this->assertSame([3553911, 6235017], array_map(fn(File $file) => $file->getId(), $files)); + $this->assertRequestCount(1); + + $files[0]->fetchMod(); + $this->assertGetRequest("/v1/mods/420561", [], 1); + } + + public function testGetLatestFilesWithoutFilesReturnsNull(): void + { + $mod = new Mod($this->createClient(), new ModModel(["id" => 420561])); + + $this->assertNull($mod->getLatestFiles()); + } }