Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jenky committed Mar 9, 2024
1 parent 23a4167 commit 88e5ee2
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 80 deletions.
55 changes: 35 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@

# This is my package jsonplaceholder
# JsonPlaceholder PHP SDK example

[![Latest Version on Packagist][ico-version]][link-packagist]
[![Github Actions][ico-gh-actions]][link-gh-actions]
[![Codecov][ico-codecov]][link-codecov]
[![Total Downloads][ico-downloads]][link-downloads]
[![Software License][ico-license]](LICENSE.md)

This is where your description should go. Try and limit it to a paragraph or two. Consider adding a small example.
This repository serves as a demonstration of how you can build your SDK/integration with [JsonPlaceholder](https://jsonplaceholder.typicode.com/) service using [Fansipan](https://github.com/phanxipang/fansipan) library.

## Installation

Expand All @@ -19,9 +19,30 @@ composer require jenky/jsonplaceholder

## Usage

Create new SDK instance

```php
$sdk = new Jenky\JsonPlaceholder();
```

Get list of users

```php
// GET https://jsonplaceholder.typicode.com/users
$sdk->users()->get();

// GET https://jsonplaceholder.typicode.com/users?_limit=5
$sdk->users()->get(limit: 5);

// GET https://jsonplaceholder.typicode.com/users?_page=2
$sdk->users()->get(page: 2);
```

Get an user by ID

```php
$jsonplaceholder = new Jenky\JsonPlaceholder();
echo $jsonplaceholder->echoPhrase('Hello, Jenky!');
// GET https://jsonplaceholder.typicode.com/users/1
$sdk->users()->id(1)->find();
```

## Testing
Expand All @@ -40,7 +61,7 @@ Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT

## Security

If you discover any security related issues, please email jenky.w0w@gmail.com instead of using the issue tracker.
If you discover any security related issues, please email contact@lynh.me instead of using the issue tracker.

## Credits

Expand All @@ -51,20 +72,14 @@ If you discover any security related issues, please email jenky.w0w@gmail.com in

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

[ico-version]: https://img.shields.io/packagist/v/jenky/JsonPlaceholder.svg?style=for-the-badge
[ico-version]: https://img.shields.io/packagist/v/jenky/jsonplaceholder.svg?style=for-the-badge
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=for-the-badge
[ico-travis]: https://img.shields.io/travis/jenky/JsonPlaceholder/master.svg?style=for-the-badge
[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/jenky/JsonPlaceholder.svg?style=for-the-badge
[ico-code-quality]: https://img.shields.io/scrutinizer/g/jenky/JsonPlaceholder.svg?style=for-the-badge
[ico-gh-actions]: https://img.shields.io/github/actions/workflow/status/jenky/JsonPlaceholder/testing.yml?branch=main&label=actions&logo=github&style=for-the-badge
[ico-codecov]: https://img.shields.io/codecov/c/github/jenky/JsonPlaceholder?logo=codecov&style=for-the-badge
[ico-downloads]: https://img.shields.io/packagist/dt/jenky/JsonPlaceholder.svg?style=for-the-badge

[link-packagist]: https://packagist.org/packages/jenky/JsonPlaceholder
[link-travis]: https://travis-ci.org/jenky/JsonPlaceholder
[link-scrutinizer]: https://scrutinizer-ci.com/g/jenky/JsonPlaceholder/code-structure
[link-code-quality]: https://scrutinizer-ci.com/g/jenky/JsonPlaceholder
[link-gh-actions]: https://github.com/jenky/jenky/JsonPlaceholder
[link-codecov]: https://codecov.io/gh/jenky/JsonPlaceholder
[link-downloads]: https://packagist.org/packages/jenky/JsonPlaceholder
[ico-gh-actions]: https://img.shields.io/github/actions/workflow/status/jenky/jsonplaceholder/testing.yml?branch=main&label=actions&logo=github&style=for-the-badge
[ico-codecov]: https://img.shields.io/codecov/c/github/jenky/jsonplaceholder?logo=codecov&style=for-the-badge
[ico-downloads]: https://img.shields.io/packagist/dt/jenky/jsonplaceholder.svg?style=for-the-badge

[link-packagist]: https://packagist.org/packages/jenky/jsonplaceholder
[link-gh-actions]: https://github.com/jenky/jenky/jsonplaceholder
[link-codecov]: https://codecov.io/gh/jenky/jsonplaceholder
[link-downloads]: https://packagist.org/packages/jenky/jsonplaceholder

6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jenky/jsonplaceholder",
"description": "Example of Atlas using JSON Placeholder",
"description": "JSON Placeholder SDK",
"keywords": [
"atlas",
"jsonplaceholder",
Expand All @@ -17,8 +17,8 @@
],
"require": {
"php": "^8.1",
"cuyz/valinor": "^1.4",
"fansipan/fansipan": "^0.8",
"cuyz/valinor": "^1.9",
"fansipan/fansipan": "^1.0",
"guzzlehttp/guzzle": "^7.7",
"ramsey/collection": "^2.0"
},
Expand Down
21 changes: 21 additions & 0 deletions src/DTO/ErrorResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Jenky\JsonPlaceholder\DTO;

use Psr\Http\Message\ResponseInterface;

final class ErrorResponse
{
public function __construct(
public readonly int $statusCode,
public readonly string $reason,
) {
}

public static function fromResponse(ResponseInterface $response): self
{
return new self($response->getStatusCode(), $response->getReasonPhrase());
}
}
28 changes: 28 additions & 0 deletions src/HasPagination.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Jenky\JsonPlaceholder;

trait HasPagination
{
private ?int $limit = null;

private ?int $page = null;

public function limit(int $limit): self
{
$clone = clone $this;
$clone->page = $limit;

return $clone;
}

public function page(int $page): self
{
$clone = clone $this;
$clone->page = $page;

return $clone;
}
}
29 changes: 4 additions & 25 deletions src/JsonPlaceholder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@

use Fansipan\Contracts\ConnectorInterface;
use Fansipan\Traits\ConnectorTrait;
use Jenky\JsonPlaceholder\DTO\PostCollection;
use Jenky\JsonPlaceholder\DTO\UserCollection;
use Jenky\JsonPlaceholder\Post\GetPostsRequest;
use Jenky\JsonPlaceholder\Post\PostResource;
use Jenky\JsonPlaceholder\User\GetUsersRequest;
use Jenky\JsonPlaceholder\User\UserResource;

final class JsonPlaceholder implements ConnectorInterface
Expand All @@ -25,30 +21,13 @@ public static function baseUri(): ?string
/**
* Get list of users.
*/
public function users(): UserCollection
public function users(): UserResource
{
return $this->send(new GetUsersRequest())
->throw()
->object();
return new UserResource($this);
}

public function user(int $id): UserResource
public function posts(): PostResource
{
return new UserResource($this, $id);
}

/**
* Get list of posts.
*/
public function posts(): PostCollection
{
return $this->send(new GetPostsRequest())
->throw()
->object();
}

public function post(int $id): PostResource
{
return new PostResource($this, $id);
return new PostResource($this);
}
}
3 changes: 2 additions & 1 deletion src/Post/FindPostRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

use Fansipan\Contracts\DecoderInterface;
use Fansipan\Request;
use Jenky\JsonPlaceholder\DTO\ErrorResponse;
use Jenky\JsonPlaceholder\DTO\Post;
use Jenky\JsonPlaceholder\ValinorDecoder;

/**
* @extends Request<Post>
* @extends Request<Post|ErrorResponse>
*/
final class FindPostRequest extends Request
{
Expand Down
14 changes: 13 additions & 1 deletion src/Post/GetPostsRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,31 @@

use Fansipan\Contracts\DecoderInterface;
use Fansipan\Request;
use Jenky\JsonPlaceholder\DTO\ErrorResponse;
use Jenky\JsonPlaceholder\DTO\PostCollection;
use Jenky\JsonPlaceholder\HasPagination;
use Jenky\JsonPlaceholder\ValinorDecoder;

/**
* @extends Request<PostCollection>
* @extends Request<PostCollection|ErrorResponse>
*/
final class GetPostsRequest extends Request
{
use HasPagination;

public function endpoint(): string
{
return '/posts';
}

protected function defaultQuery(): array
{
return \array_filter([
'_page' => $this->page,
'_limit' => $this->limit,
]);
}

public function decoder(): DecoderInterface
{
return new ValinorDecoder(PostCollection::class);
Expand Down
37 changes: 29 additions & 8 deletions src/Post/PostResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,45 @@

namespace Jenky\JsonPlaceholder\Post;

use Jenky\JsonPlaceholder\DTO\ErrorResponse;
use Jenky\JsonPlaceholder\DTO\Post;
use Jenky\JsonPlaceholder\DTO\PostCollection;
use Jenky\JsonPlaceholder\JsonPlaceholder;
use Jenky\JsonPlaceholder\ResourceBuilder;
use Jenky\JsonPlaceholder\User\GetUserPostsRequest;
use Jenky\JsonPlaceholder\User\UserResource;

final class PostResource
/**
* @extends ResourceBuilder<JsonPlaceholder>
*/
final class PostResource extends ResourceBuilder
{
public function __construct(
private JsonPlaceholder $connector,
private int $id
) {
/**
* Get list of posts for user.
*/
public function get(?int $page = null, ?int $limit = null): PostCollection|ErrorResponse
{
$userId = $this->refs[UserResource::class] ?? null;
$request = $userId ? new GetUserPostsRequest($userId) : new GetPostsRequest();

if ($page) {
$request = $request->page($page);
}

if ($limit) {
$request = $request->page($limit);
}

return $this->connector->send($request)
->object();
}

/**
* Get a single post.
*/
public function find(): Post
public function find(): Post|ErrorResponse
{
return $this->connector->send(new FindPostRequest($this->id))
->throw()
return $this->connector->send(new FindPostRequest((int) $this->id))
->object();
}
}
46 changes: 46 additions & 0 deletions src/ResourceBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Jenky\JsonPlaceholder;

use Fansipan\Contracts\ConnectorInterface;

/**
* @template T of ConnectorInterface
*/
abstract class ResourceBuilder
{
protected string|int $id;

/**
* @param T $connector
*/
public function __construct(
protected ConnectorInterface $connector,
protected array $refs = [],
) {
}

public function id(string|int $id): static
{
$clone = clone $this;
$clone->id = $id;
$clone->refs[static::class] = $id;

return $clone;
}

/**
* @template TResource of ResourceBuilder
*
* @param class-string<TResource> $builder
* @return TResource
*/
protected function forward(string $builder): ResourceBuilder
{
\assert(\is_subclass_of($builder, ResourceBuilder::class, true));

return new $builder($this->connector, $this->refs);
}
}
3 changes: 2 additions & 1 deletion src/User/FindUserRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

use Fansipan\Contracts\DecoderInterface;
use Fansipan\Request;
use Jenky\JsonPlaceholder\DTO\ErrorResponse;
use Jenky\JsonPlaceholder\DTO\User;
use Jenky\JsonPlaceholder\ValinorDecoder;

/**
* @extends Request<User>
* @extends Request<User|ErrorResponse>
*/
final class FindUserRequest extends Request
{
Expand Down
Loading

0 comments on commit 88e5ee2

Please sign in to comment.