Version 5.1 - All the GraphQL #32
@ -54,7 +54,9 @@ class JsonAPI {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Reorganize included data
|
// Reorganize included data
|
||||||
$included = static::organizeIncluded($data['included']);
|
$included = (array_key_exists('included', $data))
|
||||||
|
? static::organizeIncluded($data['included'])
|
||||||
|
: [];
|
||||||
|
|
||||||
// Inline organized data
|
// Inline organized data
|
||||||
foreach($data['data'] as $i => &$item)
|
foreach($data['data'] as $i => &$item)
|
||||||
@ -138,10 +140,7 @@ class JsonAPI {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$relationship[$typeKey][$idKey] = array_merge(
|
$relationship[$typeKey][$idKey][$j] = $included[$typeKey][$idKey];
|
||||||
$included[$typeKey][$idKey],
|
|
||||||
$relationship[$typeKey][$idKey] ?? []
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,6 +148,8 @@ class JsonAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data['data']['included'] = $included;
|
||||||
|
|
||||||
return $data['data'];
|
return $data['data'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,11 +203,11 @@ class JsonAPI {
|
|||||||
{
|
{
|
||||||
foreach($items as $id => $item)
|
foreach($items as $id => $item)
|
||||||
{
|
{
|
||||||
if (array_key_exists('relationships', $item))
|
if (array_key_exists('relationships', $item) && is_array($item['relationships']))
|
||||||
{
|
{
|
||||||
foreach($item['relationships'] as $relType => $props)
|
foreach($item['relationships'] as $relType => $props)
|
||||||
{
|
{
|
||||||
if (array_key_exists('data', $props))
|
if (array_key_exists('data', $props) && is_array($props['data']) && array_key_exists('id', $props['data']))
|
||||||
{
|
{
|
||||||
if (array_key_exists($props['data']['id'], $organized[$props['data']['type']]))
|
if (array_key_exists($props['data']['id'], $organized[$props['data']['type']]))
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ use function Amp\wait;
|
|||||||
|
|
||||||
use Amp\Artax\{Client, Request};
|
use Amp\Artax\{Client, Request};
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
use Aviat\AnimeClient\AnimeClient;
|
||||||
use Aviat\AnimeClient\API\Kitsu as K;
|
use Aviat\AnimeClient\API\{FailedResponseException, Kitsu as K};
|
||||||
use Aviat\Ion\Json;
|
use Aviat\Ion\Json;
|
||||||
|
|
||||||
trait KitsuTrait {
|
trait KitsuTrait {
|
||||||
@ -142,8 +142,10 @@ trait KitsuTrait {
|
|||||||
{
|
{
|
||||||
if ($logger)
|
if ($logger)
|
||||||
{
|
{
|
||||||
$logger->warning('Non 200 response for api call', (array)$response->getBody());
|
$logger->warning('Non 200 response for api call', (array)$response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new FailedResponseException('Failed to get the proper response from the API');
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json::decode($response->getBody(), TRUE);
|
return Json::decode($response->getBody(), TRUE);
|
||||||
|
@ -160,13 +160,16 @@ class Model {
|
|||||||
*/
|
*/
|
||||||
public function getCharacter(string $slug): array
|
public function getCharacter(string $slug): array
|
||||||
{
|
{
|
||||||
// @todo catch non-existent characters and show 404
|
|
||||||
$data = $this->getRequest('/characters', [
|
$data = $this->getRequest('/characters', [
|
||||||
'query' => [
|
'query' => [
|
||||||
'filter' => [
|
'filter' => [
|
||||||
'name' => $slug
|
'slug' => $slug,
|
||||||
],
|
],
|
||||||
// 'include' => 'primaryMedia,castings'
|
'fields' => [
|
||||||
|
'anime' => 'canonicalTitle,titles,slug,posterImage',
|
||||||
|
'manga' => 'canonicalTitle,titles,slug,posterImage'
|
||||||
|
],
|
||||||
|
'include' => 'castings.person,castings.media'
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -14,38 +14,128 @@
|
|||||||
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient\Controller;
|
namespace Aviat\AnimeClient\Controller;
|
||||||
|
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
|
use Aviat\AnimeClient\API\JsonAPI;
|
||||||
/**
|
use Aviat\Ion\ArrayWrapper;
|
||||||
* Controller for character description pages
|
|
||||||
*/
|
/**
|
||||||
class Character extends BaseController {
|
* Controller for character description pages
|
||||||
|
*/
|
||||||
public function index(string $slug)
|
class Character extends BaseController {
|
||||||
{
|
|
||||||
$model = $this->container->get('kitsu-model');
|
use ArrayWrapper;
|
||||||
|
|
||||||
$data = $model->getCharacter($slug);
|
public function index(string $slug)
|
||||||
|
{
|
||||||
if (( ! array_key_exists('data', $data)) || empty($data['data']))
|
$model = $this->container->get('kitsu-model');
|
||||||
{
|
|
||||||
return $this->notFound(
|
$rawData = $model->getCharacter($slug);
|
||||||
$this->formatTitle(
|
|
||||||
'Characters',
|
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||||
'Character not found'
|
{
|
||||||
),
|
return $this->notFound(
|
||||||
'Character Not Found'
|
$this->formatTitle(
|
||||||
);
|
'Characters',
|
||||||
}
|
'Character not found'
|
||||||
|
),
|
||||||
$this->outputHTML('character', [
|
'Character Not Found'
|
||||||
'title' => $this->formatTitle(
|
);
|
||||||
'Characters',
|
}
|
||||||
$data['data'][0]['attributes']['name']
|
|
||||||
),
|
$data = JsonAPI::organizeData($rawData);
|
||||||
'data' => $data['data'][0]['attributes']
|
|
||||||
]);
|
$viewData = [
|
||||||
}
|
'title' => $this->formatTitle(
|
||||||
|
'Characters',
|
||||||
|
$data[0]['attributes']['name']
|
||||||
|
),
|
||||||
|
'data' => $data,
|
||||||
|
'castings' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
if (array_key_exists('included', $data) && array_key_exists('castings', $data['included']))
|
||||||
|
{
|
||||||
|
$viewData['castings'] = $this->organizeCast($data['included']['castings']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->outputHTML('character', $viewData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dedupeCast(array $cast): array
|
||||||
|
{
|
||||||
|
$output = [];
|
||||||
|
$people = [];
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
foreach ($cast as &$role)
|
||||||
|
{
|
||||||
|
if (empty($role['attributes']['role']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$person = current($role['relationships']['person']['people'])['attributes'];
|
||||||
|
|
||||||
|
if ( ! array_key_exists($person['name'], $people))
|
||||||
|
{
|
||||||
|
$people[$person['name']] = $i;
|
||||||
|
$role['relationships']['media']['anime'] = [current($role['relationships']['media']['anime'])];
|
||||||
|
$output[$i] = $role;
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(array_key_exists($person['name'], $people))
|
||||||
|
{
|
||||||
|
if (array_key_exists('anime', $role['relationships']['media']))
|
||||||
|
{
|
||||||
|
$key = $people[$person['name']];
|
||||||
|
$output[$key]['relationships']['media']['anime'][] = current($role['relationships']['media']['anime']);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function organizeCast(array $cast): array
|
||||||
|
{
|
||||||
|
$cast = $this->dedupeCast($cast);
|
||||||
|
$output = [];
|
||||||
|
|
||||||
|
foreach($cast as $id => $role)
|
||||||
|
{
|
||||||
|
if (empty($role['attributes']['role']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$language = $role['attributes']['language'];
|
||||||
|
$roleName = $role['attributes']['role'];
|
||||||
|
$isVA = $role['attributes']['voiceActor'];
|
||||||
|
|
||||||
|
if ($isVA)
|
||||||
|
{
|
||||||
|
$person = current($role['relationships']['person']['people'])['attributes'];
|
||||||
|
$name = $person['name'];
|
||||||
|
$item = [
|
||||||
|
'person' => $person,
|
||||||
|
'series' => $role['relationships']['media']['anime']
|
||||||
|
];
|
||||||
|
|
||||||
|
$output[$roleName][$language][] = $item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output[$roleName][] = $role['relationships']['person']['people'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
}
|
}
|
@ -26,6 +26,7 @@ use const Aviat\AnimeClient\{
|
|||||||
|
|
||||||
use function Aviat\Ion\_dir;
|
use function Aviat\Ion\_dir;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\API\FailedResponseException;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\Ion\Friend;
|
use Aviat\Ion\Friend;
|
||||||
|
|
||||||
@ -256,13 +257,24 @@ class Dispatcher extends RoutingBase {
|
|||||||
{
|
{
|
||||||
$logger = $this->container->getLogger('default');
|
$logger = $this->container->getLogger('default');
|
||||||
|
|
||||||
$controller = new $controllerName($this->container);
|
try
|
||||||
|
{
|
||||||
|
$controller = new $controllerName($this->container);
|
||||||
|
|
||||||
// Run the appropriate controller method
|
// Run the appropriate controller method
|
||||||
$logger->debug('Dispatcher - controller arguments');
|
$logger->debug('Dispatcher - controller arguments', $params);
|
||||||
$logger->debug(print_r($params, TRUE));
|
|
||||||
|
call_user_func_array([$controller, $method], $params);
|
||||||
|
}
|
||||||
|
catch (FailedResponseException $e)
|
||||||
|
{
|
||||||
|
$controllerName = DEFAULT_CONTROLLER;
|
||||||
|
$controller = new $controllerName($this->container);
|
||||||
|
$controller->errorPage(500,
|
||||||
|
'API request timed out',
|
||||||
|
'Failed to retrieve data from API ☹️');
|
||||||
|
}
|
||||||
|
|
||||||
call_user_func_array([$controller, $method], $params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user