From d69e48cd07d78ed6e5be3f24a85001420c97b14e Mon Sep 17 00:00:00 2001 From: Samuel Ryan Date: Tue, 17 Apr 2018 16:41:17 +0100 Subject: [PATCH 1/3] Sets Entity type explicitly on each Entity --- src/Patreon/Entities/Address.php | 7 +++++++ src/Patreon/Entities/Campaign.php | 7 +++++++ src/Patreon/Entities/Card.php | 7 +++++++ src/Patreon/Entities/Entity.php | 19 +++++++++---------- src/Patreon/Entities/Goal.php | 7 +++++++ src/Patreon/Entities/Pledge.php | 7 +++++++ src/Patreon/Entities/Reward.php | 7 +++++++ src/Patreon/Entities/User.php | 7 +++++++ src/Patreon/Hydrator/EntityHydrator.php | 1 - 9 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/Patreon/Entities/Address.php b/src/Patreon/Entities/Address.php index 1e53a9b..35e3f58 100644 --- a/src/Patreon/Entities/Address.php +++ b/src/Patreon/Entities/Address.php @@ -4,6 +4,13 @@ class Address extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'address'; + /** * Name of the person to deliver to at this address. * Example: John Doe diff --git a/src/Patreon/Entities/Campaign.php b/src/Patreon/Entities/Campaign.php index 34ecaf2..5d26894 100644 --- a/src/Patreon/Entities/Campaign.php +++ b/src/Patreon/Entities/Campaign.php @@ -6,6 +6,13 @@ class Campaign extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'campaign'; + /** * Timestamp of the campaign creation, ISO 8601 combined date and time in UTC. * Example: "2017-12-01T16:33:48+00:00" diff --git a/src/Patreon/Entities/Card.php b/src/Patreon/Entities/Card.php index 5c99f45..c36faca 100644 --- a/src/Patreon/Entities/Card.php +++ b/src/Patreon/Entities/Card.php @@ -4,6 +4,13 @@ class Card extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'card'; + /** * Type of payment method. * Example: "card" diff --git a/src/Patreon/Entities/Entity.php b/src/Patreon/Entities/Entity.php index 5991d04..5fea7db 100644 --- a/src/Patreon/Entities/Entity.php +++ b/src/Patreon/Entities/Entity.php @@ -13,6 +13,7 @@ abstract class Entity */ const PATREON_URL = 'https://patreon.com'; + /** * The type of resource. * @@ -57,27 +58,25 @@ public function getEntityKey(): string } /** - * Set the Entity Resource type. + * Set the Entity ID. * - * @param string $type Type of Resource + * @param string $id ID of the Entity. * * @return void */ - public function setType(string $type): void + public function setId(string $id): void { - $this->type = $type; + $this->id = (int) $id; } /** - * Set the Entity ID. + * Get the Entity Resource Type. * - * @param string $id ID of the Entity. - * - * @return void + * @return string */ - public function setId(string $id): void + public function getType(): string { - $this->id = (int) $id; + return $this->type; } /** diff --git a/src/Patreon/Entities/Goal.php b/src/Patreon/Entities/Goal.php index 12e96d4..cbe8d30 100644 --- a/src/Patreon/Entities/Goal.php +++ b/src/Patreon/Entities/Goal.php @@ -4,6 +4,13 @@ class Goal extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'goal'; + /** * Amount in cents at which this goal is achieved. * diff --git a/src/Patreon/Entities/Pledge.php b/src/Patreon/Entities/Pledge.php index c1ef168..351419f 100644 --- a/src/Patreon/Entities/Pledge.php +++ b/src/Patreon/Entities/Pledge.php @@ -4,6 +4,13 @@ class Pledge extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'pledge'; + /** * Address associated with the Pledge if Reward `requires_shipping`. * diff --git a/src/Patreon/Entities/Reward.php b/src/Patreon/Entities/Reward.php index 3689c2e..45a040e 100644 --- a/src/Patreon/Entities/Reward.php +++ b/src/Patreon/Entities/Reward.php @@ -4,6 +4,13 @@ class Reward extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'reward'; + /** * Amount in cents required to be eligible for this reward. * Notes: This is legacy, do not use this. Use `amount_cents`. diff --git a/src/Patreon/Entities/User.php b/src/Patreon/Entities/User.php index 980dcc6..72b19a0 100644 --- a/src/Patreon/Entities/User.php +++ b/src/Patreon/Entities/User.php @@ -4,6 +4,13 @@ class User extends Entity { + /** + * Resource type (from Patreon). + * + * @var string + */ + protected $type = 'user'; + /** * "About Me" text. * Example: "Hello World!" diff --git a/src/Patreon/Hydrator/EntityHydrator.php b/src/Patreon/Hydrator/EntityHydrator.php index 3941616..a44ec54 100644 --- a/src/Patreon/Hydrator/EntityHydrator.php +++ b/src/Patreon/Hydrator/EntityHydrator.php @@ -83,7 +83,6 @@ protected function hydrateResource(ResourceObject $resource): Entity } $parent->setId($resource->id()); - $parent->setType($resource->type()); $this->saveEntityToCollection($parent); From 89db87ffffa5f895817a90b1e4b33bcab577523d Mon Sep 17 00:00:00 2001 From: Samuel Ryan Date: Tue, 17 Apr 2018 16:45:31 +0100 Subject: [PATCH 2/3] Explicitly defines fields to be included in the API response by Patreon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are optional fields available via the API (e.g: `total_historical_amount_cents`) but by explicitly requesting a field the API assumes that you only want that field, therefore, if we wish to access optional properties we must explicitly request every single property. This code can be improved as we’re requesting properties that don’t actually exist. --- src/Patreon/Resources/Campaigns.php | 16 ++++++--- src/Patreon/Resources/CurrentUser.php | 4 ++- src/Patreon/Resources/Pledges.php | 11 ++++-- src/Patreon/Resources/Resource.php | 29 +++++++++++++++ tests/Patreon/Unit/Resources/PledgesTest.php | 3 ++ tests/Patreon/Unit/Resources/ResourceTest.php | 35 +++++++++++++++++-- 6 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/Patreon/Resources/Campaigns.php b/src/Patreon/Resources/Campaigns.php index c7aa652..bfd32c2 100644 --- a/src/Patreon/Resources/Campaigns.php +++ b/src/Patreon/Resources/Campaigns.php @@ -15,7 +15,9 @@ public function getMyCampaign(): Campaign { $this->onlyAvailableAuthenticated('getMyCampaign'); - return $this->getHydratedEntity('current_user/campaigns'); + return $this->getHydratedEntity( + $this->buildUrl('current_user/campaigns', Campaign::class) + ); } /** @@ -27,7 +29,9 @@ public function getMyCampaignWithPledges(): Campaign { $this->onlyAvailableAuthenticated('getMyCampaignWithPledges'); - $campaign = $this->getHydratedEntity('current_user/campaigns'); + $campaign = $this->getHydratedEntity( + $this->buildUrl('current_user/campaigns', Campaign::class) + ); return $this->attachPledges($campaign); } @@ -41,7 +45,9 @@ public function getMyCampaignWithPledges(): Campaign */ public function getCampaign(int $id): Campaign { - return $this->getHydratedEntity("campaigns/{$id}"); + return $this->getHydratedEntity( + $this->buildUrl("campaigns/{$id}", Campaign::class) + ); } /** @@ -53,7 +59,9 @@ public function getCampaign(int $id): Campaign */ public function getCampaignWithPledges(int $id): Campaign { - $campaign = $this->getHydratedEntity("campaigns/{$id}"); + $campaign = $this->getHydratedEntity( + $this->buildUrl("campaigns/{$id}", Campaign::class) + ); return $this->attachPledges($campaign); } diff --git a/src/Patreon/Resources/CurrentUser.php b/src/Patreon/Resources/CurrentUser.php index a74119b..6043e60 100644 --- a/src/Patreon/Resources/CurrentUser.php +++ b/src/Patreon/Resources/CurrentUser.php @@ -15,6 +15,8 @@ public function get(): User { $this->onlyAvailableAuthenticated('get'); - return $this->getHydratedEntity('current_user'); + return $this->getHydratedEntity( + $this->buildUrl('current_user', User::class) + ); } } diff --git a/src/Patreon/Resources/Pledges.php b/src/Patreon/Resources/Pledges.php index 35d0d58..962f77d 100644 --- a/src/Patreon/Resources/Pledges.php +++ b/src/Patreon/Resources/Pledges.php @@ -2,6 +2,7 @@ namespace Squid\Patreon\Resources; +use Squid\Patreon\Entities\Pledge; use Squid\Patreon\Exceptions\SortOptionsAreInvalid; use Tightenco\Collect\Support\Collection; @@ -30,9 +31,11 @@ public function getCampaignPledges(int $campaign): Collection { $pledges = new Collection; + $url = $this->buildUrl("campaigns/{$campaign}/pledges", Pledge::class); + while (true) { $page = $this->client->get( - "campaigns/{$campaign}/pledges?" . ($filter ?? null), + $url . '&' . ($filter ?? null), $this->authenticated ); @@ -71,7 +74,9 @@ public function getPageOfCampaignPledges( throw SortOptionsAreInvalid::options($invalid, self::SORT_OPTIONS); } - $parameters = http_build_query( + $url = $this->buildUrl( + "campaigns/{$campaign}/pledges", + Pledge::class, [ 'page[count]' => $count, 'sort' => implode(',', $sort) ?: null, @@ -79,7 +84,7 @@ public function getPageOfCampaignPledges( ] ); - $result = $this->client->get("campaigns/{$campaign}/pledges?{$parameters}"); + $result = $this->client->get($url); return $this->hydrateDocument( $result->document() diff --git a/src/Patreon/Resources/Resource.php b/src/Patreon/Resources/Resource.php index 07c3e60..d7a8c8b 100644 --- a/src/Patreon/Resources/Resource.php +++ b/src/Patreon/Resources/Resource.php @@ -124,4 +124,33 @@ public function isAuthenticated(): bool { return $this->authenticated; } + + /** + * Build a URL including the Entity's fields as we need to request each field + * explicitly. + * + * @param string $path Path of the endpoint + * @param string $entity Entity class + * @param array $parameters Any additional parameters to include in the URL + * + * @return string + */ + protected function buildUrl( + string $path, + string $entity, + array $parameters = [] + ): string { + $entity = new $entity; + + $attributes = implode(array_keys(get_object_vars($entity)), ','); + + $parameters = array_merge( + $parameters, + [ + "fields[{$entity->getType()}]" => $attributes + ] + ); + + return $path . '?' . http_build_query($parameters); + } } diff --git a/tests/Patreon/Unit/Resources/PledgesTest.php b/tests/Patreon/Unit/Resources/PledgesTest.php index 1747fa2..0f42475 100644 --- a/tests/Patreon/Unit/Resources/PledgesTest.php +++ b/tests/Patreon/Unit/Resources/PledgesTest.php @@ -47,6 +47,9 @@ public function testGetPageOfCampaignPledgesCreatesValidRequestUrl(): void $path .= '?page%5Bcount%5D=25&sort=-updated'; $path .= '&page%5Bcursor%5D=2017-12-02T15%3A21%3A20.121892%2B00%3A00'; + $attributes = implode(array_keys(get_object_vars(new Pledge)), ','); + $path .= '&' . http_build_query(["fields[pledge]" => $attributes]); + $client = $this->createMock(Client::class); $client->expects($this->once()) ->method('get') diff --git a/tests/Patreon/Unit/Resources/ResourceTest.php b/tests/Patreon/Unit/Resources/ResourceTest.php index 523c3dc..13f4fd6 100644 --- a/tests/Patreon/Unit/Resources/ResourceTest.php +++ b/tests/Patreon/Unit/Resources/ResourceTest.php @@ -3,6 +3,7 @@ namespace Squid\Patreon\Tests\Unit\Resources; use Squid\Patreon\Api\Client; +use Squid\Patreon\Entities\Entity; use Squid\Patreon\Entities\User; use Squid\Patreon\Exceptions\PatreonReturnedError; use Squid\Patreon\Resources\Resource; @@ -29,14 +30,44 @@ public function testResourceThrowsExceptionWhenApiReturnsError(): void $this->expectException(PatreonReturnedError::class); - $user = (new ResourceExample($client))->get($response); + (new ExampleResource($client))->get($response); + } + + public function testBuildUrlReturnsCorrectUrl(): void + { + $client = $this->createMock(Client::class); + $resource = (new ExampleResource($client)); + + $this->assertEquals( + 'example?fields%5Bentity%5D=a%2Cb%2Cc%2Cd%2Cid', + $resource->url() + ); + + $this->assertEquals( + 'example?x=5&fields%5Bentity%5D=a%2Cb%2Cc%2Cd%2Cid', + $resource->url(['x' => 5]) + ); } } -class ResourceExample extends Resource +class ExampleResource extends Resource { public function get(JsonApiResponse $response): void { $this->hydrateDocument($response->document()); } + + public function url($parameters = []): string + { + return $this->buildUrl('example', ExampleEntity::class, $parameters); + } +} + +class ExampleEntity extends Entity +{ + protected $type = 'entity'; + public $a; + public $b; + public $c; + public $d; } From 1376dc6798a29e1dd1ce065498f8910f4cbff792 Mon Sep 17 00:00:00 2001 From: Samuel Ryan Date: Tue, 17 Apr 2018 16:45:48 +0100 Subject: [PATCH 3/3] Increments version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b4f6a78..8cbc148 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "squid/patreon", - "version": "1.1.0", + "version": "1.1.1", "keywords": ["Patreon"], "description": "PHP library for interacting with the Patreon API", "type": "library",