* @copyright 2015 - 2020 Timothy J. Warren * @license http://www.opensource.org/licenses/mit-license.html MIT License * @version 5.1 * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient */ namespace Aviat\AnimeClient\API\Kitsu\Transformer; use Aviat\AnimeClient\API\JsonAPI; use Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\Types\Character; use Aviat\Ion\Transformer\AbstractTransformer; use Locale; /** * Data transformation class for character pages */ final class CharacterTransformer extends AbstractTransformer { /** * @param array $characterData * @return Character */ public function transform($characterData): Character { $data = $characterData['data']['findCharacterBySlug'] ?? []; $castings = []; $media = [ 'anime' => [], 'manga' => [], ]; $names = array_unique( array_merge( [$data['names']['canonical']], array_values($data['names']['localized']) ) ); $name = array_shift($names); if (isset($data['media']['nodes'])) { [$media, $castings] = $this->organizeMediaAndVoices($data['media']['nodes']); } return Character::from([ 'castings' => $castings, 'description' => $data['description']['en'], 'id' => $data['id'], 'media' => $media, 'name' => $name, 'names' => $names, 'otherNames' => $data['names']['alternatives'], ]); } protected function organizeMediaAndVoices (array $data): array { if (empty($data)) { return [[], []]; } $titleSort = fn ($a, $b) => $a['title'] <=> $b['title']; $rawMedia = array_column($data, 'media'); $rawAnime = array_filter($rawMedia, fn ($item) => $item['type'] === 'Anime'); $rawManga = array_filter($rawMedia, fn ($item) => $item['type'] === 'Manga'); $anime = array_map(static function ($item) { $output = $item; unset($output['titles']); $output['title'] = $item['titles']['canonical']; $output['titles'] = Kitsu::getFilteredTitles($item['titles']); return $output; }, $rawAnime); $manga = array_map(static function ($item) { $output = $item; unset($output['titles']); $output['title'] = $item['titles']['canonical']; $output['titles'] = Kitsu::getFilteredTitles($item['titles']); return $output; }, $rawManga); uasort($anime, $titleSort); uasort($manga, $titleSort); $media = [ 'anime' => $anime, 'manga' => $manga, ]; $rawVoices = array_filter($data, fn($item) => count((array)$item['voices']['nodes']) > 0); if (empty($rawVoices)) { return [$media, []]; } $castings = [ 'Voice Actor' => [], ]; foreach ($rawVoices as $voiceMap) { foreach ($voiceMap['voices']['nodes'] as $voice) { $lang = Locale::getDisplayLanguage($voice['locale'], 'en'); $id = $voice['person']['name']; $seriesId = $voiceMap['media']['id']; if ( ! array_key_exists($lang, $castings['Voice Actor'])) { $castings['Voice Actor'][$lang] = []; } if ( ! array_key_exists($id, $castings['Voice Actor'][$lang])) { $castings['Voice Actor'][$lang][$id] = [ 'person' => [ 'id' => $voice['person']['id'], 'slug' => $voice['person']['slug'], 'image' => $voice['person']['image']['original']['url'], 'name' => $voice['person']['name'], ], 'series' => [] ]; } $castings['Voice Actor'][$lang][$id]['series'][$seriesId] = [ 'id' => $seriesId, 'slug' => $voiceMap['media']['slug'], 'title' => $voiceMap['media']['titles']['canonical'], 'titles' => Kitsu::getFilteredTitles($voiceMap['media']['titles']), 'posterImage' => $voiceMap['media']['posterImage']['views'][1]['url'], ]; uasort($castings['Voice Actor'][$lang][$id]['series'], $titleSort); ksort($castings['Voice Actor'][$lang]); } } return [$media, $castings]; } /** * @param array $characterData * @return Character */ public function oldTransform($characterData): Character { $data = JsonAPI::organizeData($characterData); $attributes = $data[0]['attributes']; $castings = []; $names = array_unique( array_merge( [$attributes['canonicalName']], $attributes['names'] ) ); $name = array_shift($names); if (array_key_exists('included', $data)) { if (array_key_exists('anime', $data['included'])) { uasort($data['included']['anime'], static function ($a, $b) { return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle']; }); } if (array_key_exists('manga', $data['included'])) { uasort($data['included']['manga'], static function ($a, $b) { return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle']; }); } if (array_key_exists('castings', $data['included'])) { $castings = $this->organizeCast($data['included']['castings']); } } return Character::from([ 'castings' => $castings, 'description' => $attributes['description'], 'id' => $data[0]['id'], 'media' => [ 'anime' => $data['included']['anime'] ?? [], 'manga' => $data['included']['manga'] ?? [], ], 'name' => $name, 'names' => $names, 'otherNames' => $attributes['otherNames'], ]); } /** * Organize VA => anime relationships * * @param array $cast * @return array */ 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']; $hasName = array_key_exists($person['name'], $people); if ( ! $hasName) { $people[$person['name']] = $i; $role['relationships']['media']['anime'] = [current($role['relationships']['media']['anime'])]; $output[$i] = $role; $i++; continue; } 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; } protected 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) { foreach ($role['relationships']['person']['people'] as $pid => $peoples) { $p = $peoples; $person = $p['attributes']; $person['id'] = $pid; $person['image'] = $person['image']['original'] ?? ''; uasort($role['relationships']['media']['anime'], static function ($a, $b) { return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle']; }); $item = [ 'person' => $person, 'series' => $role['relationships']['media']['anime'] ]; $output[$roleName][$language][] = $item; } } else { foreach ($role['relationships']['person']['people'] as $pid => $person) { $person['id'] = $pid; $output[$roleName][$pid] = $person; } } } return $output; } }