Responses
Response main job is to take the result data and convert it to an object.
You are responsible to validate the:
- status code of the response (should not raise exception).
- build the data and set it in response (can raise an exception).
- build an error entity if there is an error
Currently, we are not throwing exceptions. Use isSuccesfull
to determine if there is an error. This allows
to work with the response with any state and adds more flexibility.
This can probably change. Maybe raising exception is always the best option.
TODO
- A convention to return error entity state (nullable) if isSuccessful() is false
Base implementation
- Override __construct and try to build the response (parse json / xml / etc).
- Create
private bool $isSuccessful = false;
and set it to true if the response is successful. - Implement
public function isSuccessful(): bool
- Create properties and entities for your data. Use getters instead of properties
Consumer functions
These functions are provided for the consumer:
isSuccessful: bool
- Indicates if the response is successfully and the data can be accessed.getResponse(): ResponseInterface
- Returns underlying response.
JSON response
To help parse JSON responses extend AbstractJsonResponse
that will try to parse response to json and check if keys are
present in an array.
- Raises
InvalidJsonResponseException
if the response is not json. - Raises
InvalidJsonResponseException
if the of json value is invalid. - Will not raise exception if any of desired keys is missing.
Consumer functions
These functions are provided for the consumer:
toArray(): array
- Returns the json. Will raise exception if the data is not valid.
Implementation
- Implement
parseJson(array $json): bool;
to parse the json. Return false if not valid. I recommend to create properties and getters instead using toArray function. - You can use
$this->hasKeys(json: $json, keys: [...]): bool
to check if the array contains given keys. - You can use WorksWithJson to easily get values from json with proper type.
JSON/XML response with items array
For step by step implementation check Start building / create response
Use this if you want to provide an easy access to transformed items (from XML/JSON to entity validation.
Consumer functions
These functions are provided for the consumer
getRawItems
- Returns raw items array.items
- Will return transformed data.loopItems
- Will provide a way to loop transformed items using closure.
Implementation
/**
* Class must use AbstractJsonTransformer base class.
*
* @return class-string<AbstractJsonTransformer>
*/
abstract protected function getTransformerClass(): string;
/**
* Return keys that are required for parsing the response. You can return and empty data.
*
* @return array<string>
*/
abstract protected function requiredRootKeys(): array;
/**
* Return key that holds the items (something like data, items) in the root json data. If you return null root json
* will be used for items.
*/
abstract protected function itemsKey(): ?string;
Then implement items
/ loopItems
. These methods are only to force you to set correct typehints.
/**
* @return array<UnitAvailabilityEntity>
*/
public function items(): array
{
return $this->transformUsingArray();
}
/**
* You will receive UnitAvailabilityEntity on each item. Returns false if items are empty. Faster
* than looping items.
*
* @param Closure(UnitAvailabilityEntity) $onItem
*/
public function loopItems(Closure $onItem): bool
{
return $this->transformUsingLoop($onItem);
}
Provided abstractions
We have implemented some abstraction to help re using same logic.
PaginatedResponse
Use this trait to expose methods for getting pagination.
public function getItemsPerPage(): int
public function getTotalItems(): int
public function getCurrentPage(): int
public function getTotalPages(): int;
public function onLastPage(): bool;
Implementation:
You need to set these properties, otherwise PHP will crash whenever function above is called.
protected int $itemsPerPage;
protected int $totalItems;
protected int $currentPage;
protected int $totalPages;
Example:
class UnitsResponse extends AbstractJsonResponse
{
use PaginatedResponse;
protected const KEY_ITEMS = 'items';
protected const KEY_PAGINATION = 'pagination';
protected function parseJson(array $json): bool
{
if ($this->hasKeys([self::KEY_ITEMS]) === false) {
return false;
}
// Can be null if items are empty
$result = $json[self::KEY_PAGINATION];
$this->totalItems = $result['totalItems'] ?? 0;
$this->totalPages = $result['totalPages'] ?? 1;
$this->itemsPerPage = $result['itemsPerPage'] ?? $this->totalItems;
$this->currentPage = $result['page'] ?? 1;
return true;
}
}