diff --git a/README.md b/README.md
index 28053f2a..1e8b4a65 100644
--- a/README.md
+++ b/README.md
@@ -31,14 +31,14 @@ A self-hosted client that allows custom formatting of data from the hummingbird
### Requirements
-* PHP 5.4+
+* PHP 5.5+
* PDO SQLite (For collection tab)
* GD
### Installation
1. Install dependencies via composer: `composer install`
-2. Configure settings in `app/config/config.php` and `app/config/routing.php` to your liking
+2. Configure settings in `app/config/config.php` to your liking
3. Create the following directories if they don't exist, and make sure they are world writable
* app/cache
* public/images/manga
diff --git a/app/bootstrap.php b/app/bootstrap.php
index 79a6fa17..ccca2cf8 100644
--- a/app/bootstrap.php
+++ b/app/bootstrap.php
@@ -7,6 +7,7 @@ namespace Aviat\AnimeClient;
use \Whoops\Handler\PrettyPageHandler;
use \Whoops\Handler\JsonResponseHandler;
+use Aura\Html\HelperLocatorFactory;
use \Aura\Web\WebFactory;
use \Aura\Router\RouterFactory;
use \Aura\Session\SessionFactory;
@@ -51,6 +52,15 @@ $di = function() {
$aura_router = (new RouterFactory())->newInstance();
$container->set('aura-router', $aura_router);
+ // Create Html helper Object
+ $html_helper = (new HelperLocatorFactory)->newInstance();
+ $html_helper->set('menu', function() use ($container) {
+ $menu_helper = new Helper\Menu();
+ $menu_helper->setContainer($container);
+ return $menu_helper;
+ });
+ $container->set('html-helper', $html_helper);
+
// Create Request/Response Objects
$web_factory = new WebFactory([
'_GET' => $_GET,
@@ -79,4 +89,4 @@ $di = function() {
$di()->get('router')->dispatch();
-// End of bootstrap.php
+// End of bootstrap.php
\ No newline at end of file
diff --git a/app/config/base_config.php b/app/config/base_config.php
index e78b5338..95cd0380 100644
--- a/app/config/base_config.php
+++ b/app/config/base_config.php
@@ -13,6 +13,7 @@ $base_config = [
'img_cache_path' => _dir(ROOT_DIR, 'public/images'),
// Included config files
- 'routes' => require __DIR__ . '/routes.php',
'database' => require __DIR__ . '/database.php',
+ 'menus' => require __DIR__ . '/menus.php',
+ 'routes' => require __DIR__ . '/routes.php',
];
\ No newline at end of file
diff --git a/app/config/config.php b/app/config/config.php
index 7e59cf92..15bfb5c1 100644
--- a/app/config/config.php
+++ b/app/config/config.php
@@ -14,9 +14,12 @@ $config = [
// General config
// ----------------------------------------------------------------------------
- // do you wish to show the anime collection tab?
+ // do you wish to show the anime collection?
'show_anime_collection' => TRUE,
+ // do you wish to show the manga collection?
+ 'show_manga_collection' => TRUE,
+
// path to public directory on the server
'asset_dir' => realpath(__DIR__ . '/../../public'),
diff --git a/app/config/menus.php b/app/config/menus.php
new file mode 100644
index 00000000..1d4ce011
--- /dev/null
+++ b/app/config/menus.php
@@ -0,0 +1,61 @@
+ [
+ 'default' => '',
+ 'items' => [
+ 'anime_list' => '{anime_list}',
+ 'manga_list' => '{manga_list}',
+ 'collection' => '{collection}'
+ ]
+ ],
+ 'view_type' => [
+ 'is_parent' => FALSE,
+ 'default' => 'cover_view',
+ 'items' => [
+ 'cover_view' => '{parent}',
+ 'list_view' => '{parent}/list'
+ ]
+ ],
+ 'anime_list' => [
+ 'default' => '',
+ 'route_prefix' => '/anime',
+ 'items' => [
+ 'watching' => '/watching',
+ 'plan_to_watch' => '/plan_to_watch',
+ 'on_hold' => '/on_hold',
+ 'dropped' => '/dropped',
+ 'completed' => '/completed',
+ 'all' => '/all'
+ ],
+ 'children' => [
+ 'view_type'
+ ]
+ ],
+ 'manga_list' => [
+ 'default' => '',
+ 'route_prefix' => '/manga',
+ 'items' => [
+ 'reading' => '/reading',
+ 'plan_to_read' => '/plan_to_read',
+ 'on_hold' => '/on_hold',
+ 'dropped' => '/dropped',
+ 'completed' => '/completed',
+ 'all' => '/all'
+ ],
+ 'children' => [
+ 'view_type'
+ ]
+ ],
+ 'collection' => [
+ 'default' => '',
+ 'route_prefix' => '/collection',
+ 'items' => [
+ 'anime' => '/anime',
+ 'manga' => '/manga',
+ ],
+ 'children' => [
+ 'view_type'
+ ]
+ ]
+];
\ No newline at end of file
diff --git a/app/config/routes.php b/app/config/routes.php
index 1320249a..2b8e18ba 100644
--- a/app/config/routes.php
+++ b/app/config/routes.php
@@ -9,99 +9,56 @@ return [
// Routes on all controllers
'common' => [
'update' => [
- 'path' => '/update',
- 'action' => ['update'],
+ 'path' => '/{controller}/update',
+ 'action' => 'update',
'verb' => 'post'
],
'login_form' => [
- 'path' => '/login',
- 'action' => ['login'],
+ 'path' => '/{controller}/login',
+ 'action' => 'login',
'verb' => 'get'
],
'login_action' => [
- 'path' => '/login',
- 'action' => ['login_action'],
+ 'path' => '/{controller}/login',
+ 'action' => 'login_action',
'verb' => 'post'
],
'logout' => [
- 'path' => '/logout',
- 'action' => ['logout']
+ 'path' => '/{controller}/logout',
+ 'action' => 'logout'
],
],
// Routes on collection controller
'collection' => [
'collection_add_form' => [
'path' => '/collection/add',
- 'action' => ['form'],
+ 'action' => 'form',
'params' => [],
],
'collection_edit_form' => [
'path' => '/collection/edit/{id}',
- 'action' => ['form'],
+ 'action' => 'form',
'tokens' => [
'id' => '[0-9]+'
]
],
'collection_add' => [
'path' => '/collection/add',
- 'action' => ['add'],
+ 'action' => 'add',
'verb' => 'post'
],
'collection_edit' => [
'path' => '/collection/edit',
- 'action' => ['edit'],
+ 'action' => 'edit',
'verb' => 'post'
],
'collection' => [
'path' => '/collection/view{/view}',
- 'action' => ['index'],
+ 'action' => 'index',
'params' => [],
'tokens' => [
'view' => '[a-z_]+'
]
],
],
- // Routes on anime controller
- 'anime' => [
- 'index' => [
- 'path' => '/',
- 'action' => ['redirect'],
- 'params' => [
- 'url' => '', // Determined by config
- 'code' => '301',
- 'type' => 'anime'
- ]
- ],
- 'search' => [
- 'path' => '/anime/search',
- 'action' => ['search'],
- ],
- 'anime_list' => [
- 'path' => '/anime/{type}{/view}',
- 'action' => ['anime_list'],
- 'tokens' => [
- 'type' => '[a-z_]+',
- 'view' => '[a-z_]+'
- ]
- ],
- ],
- 'manga' => [
- 'index' => [
- 'path' => '/',
- 'action' => ['redirect'],
- 'params' => [
- 'url' => '', // Determined by config
- 'code' => '301',
- 'type' => 'manga'
- ]
- ],
- 'manga_list' => [
- 'path' => '/manga/{type}{/view}',
- 'action' => ['manga_list'],
- 'tokens' => [
- 'type' => '[a-z_]+',
- 'view' => '[a-z_]+'
- ]
- ]
- ]
];
diff --git a/app/config/routing.php b/app/config/routing.php
index b1626e00..16068c3e 100644
--- a/app/config/routing.php
+++ b/app/config/routing.php
@@ -5,7 +5,7 @@
// ----------------------------------------------------------------------------
return [
- // Subfolder prefix for url
+ // Subfolder prefix for url, if in a subdirectory of the web root
'subfolder_prefix' => '',
// Path to public directory, where images/css/javascript are located,
@@ -16,9 +16,9 @@ return [
'default_list' => 'anime', // anime or manga
// Default pages for anime/manga
- 'default_anime_path' => "/anime/watching",
- 'default_manga_path' => '/manga/all',
+ 'default_anime_list_path' => "watching", // watching|plan_to_watch|on_hold|dropped|completed|all
+ 'default_manga_list_path' => "all", // reading|plan_to_read|on_hold|dropped|completed|all
- // Default to list view?
- 'default_to_list_view' => FALSE,
+ // Default view type (cover_view/list_view)
+ 'default_view_type' => 'cover_view',
];
\ No newline at end of file
diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml
index 221e6285..194e906e 100644
--- a/phpdoc.dist.xml
+++ b/phpdoc.dist.xml
@@ -11,6 +11,6 @@
- src/Aviat/AnimeClient
+ src/Aviat
\ No newline at end of file
diff --git a/src/Aviat/AnimeClient/Controller.php b/src/Aviat/AnimeClient/Controller.php
index 34b78c44..74eb652d 100644
--- a/src/Aviat/AnimeClient/Controller.php
+++ b/src/Aviat/AnimeClient/Controller.php
@@ -4,8 +4,6 @@
*/
namespace Aviat\AnimeClient;
-use Aura\Web\ResponseSender;
-
use \Aviat\Ion\Di\ContainerInterface;
use \Aviat\Ion\View\HttpView;
use \Aviat\Ion\View\HtmlView;
@@ -97,6 +95,7 @@ class Controller {
public function load_partial($view, $template, $data=[])
{
$errorHandler = $this->container->get('error-handler');
+ $errorHandler->addDataTable('Template Data', $data);
$router = $this->container->get('router');
if (isset($this->base_data))
@@ -107,7 +106,7 @@ class Controller {
$route = $router->get_route();
$data['route_path'] = ($route) ? $router->get_route()->path : "";
- $errorHandler->addDataTable('Template Data', $data);
+
$template_path = _dir($this->config->__get('view_path'), "{$template}.php");
if ( ! is_file($template_path))
@@ -170,7 +169,7 @@ class Controller {
{
$url = $this->urlGenerator->full_url($path, $type);
$http = new HttpView($this->container);
-
+
$http->redirect($url, $code);
}
diff --git a/src/Aviat/AnimeClient/Controller/Anime.php b/src/Aviat/AnimeClient/Controller/Anime.php
index 8367b2af..7001d9ee 100644
--- a/src/Aviat/AnimeClient/Controller/Anime.php
+++ b/src/Aviat/AnimeClient/Controller/Anime.php
@@ -7,6 +7,7 @@ namespace Aviat\AnimeClient\Controller;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\AnimeClient\Controller as BaseController;
+use Aviat\AnimeClient\Enum\Hummingbird\AnimeWatchingStatus;
use Aviat\AnimeClient\Model\Anime as AnimeModel;
use Aviat\AnimeClient\Model\AnimeCollection as AnimeCollectionModel;
@@ -72,6 +73,11 @@ class Anime extends BaseController {
]);
}
+ public function index($type="watching", $view='')
+ {
+ return $this->anime_list($type, $view);
+ }
+
/**
* Search for anime
*
@@ -87,11 +93,10 @@ class Anime extends BaseController {
* Show a portion, or all of the anime list
*
* @param string $type - The section of the list
- * @param string $title - The title of the page
* @param string $view - List or cover view
* @return void
*/
- public function anime_list($type, $view)
+ protected function anime_list($type, $view)
{
$type_title_map = [
'all' => 'All',
@@ -103,12 +108,12 @@ class Anime extends BaseController {
];
$model_map = [
- 'watching' => 'currently-watching',
- 'plan_to_watch' => 'plan-to-watch',
- 'on_hold' => 'on-hold',
+ 'watching' => AnimeWatchingStatus::WATCHING,
+ 'plan_to_watch' => AnimeWatchingStatus::PLAN_TO_WATCH,
+ 'on_hold' => AnimeWatchingStatus::ON_HOLD,
'all' => 'all',
- 'dropped' => 'dropped',
- 'completed' => 'completed'
+ 'dropped' => AnimeWatchingStatus::DROPPED,
+ 'completed' => AnimeWatchingStatus::COMPLETED
];
$title = $this->config->whose_list . "'s Anime List · {$type_title_map[$type]}";
diff --git a/src/Aviat/AnimeClient/Controller/Manga.php b/src/Aviat/AnimeClient/Controller/Manga.php
index 5be58558..74d06207 100644
--- a/src/Aviat/AnimeClient/Controller/Manga.php
+++ b/src/Aviat/AnimeClient/Controller/Manga.php
@@ -58,6 +58,11 @@ class Manga extends Controller {
]);
}
+ public function index($status="all", $view="")
+ {
+ return $this->manga_list($status, $view);
+ }
+
/**
* Update an anime item
*
@@ -75,15 +80,15 @@ class Manga extends Controller {
* @param string $view
* @return void
*/
- public function manga_list($status, $view)
+ protected function manga_list($status, $view)
{
$map = [
'all' => 'All',
- 'plan_to_read' => 'Plan to Read',
- 'reading' => 'Reading',
- 'completed' => 'Completed',
- 'dropped' => 'Dropped',
- 'on_hold' => 'On Hold'
+ 'plan_to_read' => MangaModel::PLAN_TO_READ,
+ 'reading' => MangaModel::READING,
+ 'completed' => MangaModel::COMPLETED,
+ 'dropped' => MangaModel::DROPPED,
+ 'on_hold' => MangaModel::ON_HOLD
];
$title = $this->config->whose_list . "'s Manga List · {$map[$status]}";
@@ -97,10 +102,12 @@ class Manga extends Controller {
? [$map[$status] => $this->model->get_list($map[$status])]
: $this->model->get_all_lists();
+ //throw new \ErrorException("Data :" . print_r($data, TRUE));
+
$this->outputHTML('manga/' . $view_map[$view], [
'title' => $title,
'sections' => $data,
]);
}
}
-// End of MangaController.php
+// End of MangaController.php
\ No newline at end of file
diff --git a/src/Aviat/AnimeClient/Helper/Menu.php b/src/Aviat/AnimeClient/Helper/Menu.php
new file mode 100644
index 00000000..536ac7d0
--- /dev/null
+++ b/src/Aviat/AnimeClient/Helper/Menu.php
@@ -0,0 +1,20 @@
+container);
+ return $generator->generate($menu_name);
+ }
+
+}
+// End of Menu.php
\ No newline at end of file
diff --git a/src/Aviat/AnimeClient/Helper/UrlHelper.php b/src/Aviat/AnimeClient/Helper/UrlHelper.php
new file mode 100644
index 00000000..6c92a874
--- /dev/null
+++ b/src/Aviat/AnimeClient/Helper/UrlHelper.php
@@ -0,0 +1,19 @@
+menus = $this->config->menus;
+ $this->helper = $container->get('html-helper');
+ }
+
+ /**
+ * Generate the full menu structure from the config files
+ *
+ * @return array
+ */
+ protected function parse_config()
+ {
+ // Note: Children menus have urls based on the
+ // current url path
+ /*
+ $parsed = [
+ 'menu_name' => [
+ 'items' => [
+ 'title' => 'full_url_path',
+ ],
+ 'children' => [
+ 'title' => 'full_url_path'
+ ]
+ ]
+ ]
+ */
+
+ $parsed = [];
+
+ foreach($this->menus as $name => $menu)
+ {
+ $parsed[$name] = [];
+ foreach($menu['items'] as $path_name => $partial_path)
+ {
+ $title = $this->string($path_name)->humanize()->titleize();
+ $parsed[$name]['items'][$title] = $this->string($menu['route_prefix'])->append($partial_path);
+ }
+
+ // @TODO: Handle child menu(s)
+ if (count($menu['children']) > 0)
+ {
+
+ }
+ }
+
+ return $parsed;
+ }
+
+ /**
+ * Generate the html structure of the menu selected
+ *
+ * @param string $menu
+ * @return string
+ */
+ public function generate($menu)
+ {
+ $parsed_config = $this->parse_config();
+ $menu_config = $parsed_config[$menu];
+
+ // Array of list items to add to the main menu
+ $main_menu = [];
+
+
+ // Start the menu list
+ $helper->ul();
+
+
+ }
+}
+// End of MenuGenerator.php
\ No newline at end of file
diff --git a/src/Aviat/AnimeClient/Model.php b/src/Aviat/AnimeClient/Model.php
index c8f07acb..95b8305a 100644
--- a/src/Aviat/AnimeClient/Model.php
+++ b/src/Aviat/AnimeClient/Model.php
@@ -4,14 +4,16 @@
*/
namespace Aviat\AnimeClient;
-use Aviat\Ion\Di\ContainerInterface;
use abeautifulsite\SimpleImage;
+use Aviat\Ion\Di\ContainerInterface;
/**
* Common base for all Models
*/
class Model {
+ use \Aviat\Ion\StringWrapper;
+
/**
* The global configuration object
* @var Config
@@ -65,11 +67,11 @@ class Model {
// Cache the file if it doesn't already exist
if ( ! file_exists($cached_path))
{
- if (ini_get('allow_url_fopen'))
+ /*if (ini_get('allow_url_fopen'))
{
copy($api_path, $cached_path);
}
- elseif (function_exists('curl_init'))
+ else*/if (function_exists('curl_init'))
{
$ch = curl_init($api_path);
$fp = fopen($cached_path, 'wb');
@@ -79,7 +81,7 @@ class Model {
]);
curl_exec($ch);
curl_close($ch);
- fclose($ch);
+ fclose($fp);
}
else
{
diff --git a/src/Aviat/AnimeClient/Model/API.php b/src/Aviat/AnimeClient/Model/API.php
index 5250aa32..eee0919f 100644
--- a/src/Aviat/AnimeClient/Model/API.php
+++ b/src/Aviat/AnimeClient/Model/API.php
@@ -4,8 +4,10 @@
*/
namespace Aviat\AnimeClient\Model;
-use \GuzzleHttp\Client;
-use \GuzzleHttp\Cookie\CookieJar;
+use GuzzleHttp\Client;
+use GuzzleHttp\Cookie\CookieJar;
+use GuzzleHttp\Psr7\Request;
+
use Aviat\Ion\Di\ContainerInterface;
use Aviat\AnimeClient\Model as BaseModel;
@@ -42,7 +44,8 @@ class API extends BaseModel {
parent::__construct($container);
$this->cookieJar = new CookieJar();
$this->client = new Client([
- 'base_url' => $this->base_url,
+ 'base_uri' => $this->base_url,
+ 'cookies' => TRUE,
'defaults' => [
'cookies' => $this->cookieJar,
'headers' => [
diff --git a/src/Aviat/AnimeClient/Model/Anime.php b/src/Aviat/AnimeClient/Model/Anime.php
index 5201a541..4cc2dff7 100644
--- a/src/Aviat/AnimeClient/Model/Anime.php
+++ b/src/Aviat/AnimeClient/Model/Anime.php
@@ -27,6 +27,18 @@ class Anime extends API {
*/
protected $base_url = "https://hummingbird.me/api/v1/";
+ /**
+ * Map of API status constants to display constants
+ * @var array
+ */
+ protected $const_map = [
+ AnimeWatchingStatus::WATCHING => self::WATCHING,
+ AnimeWatchingStatus::PLAN_TO_WATCH => self::PLAN_TO_WATCH,
+ AnimeWatchingStatus::ON_HOLD => self::ON_HOLD,
+ AnimeWatchingStatus::DROPPED => self::DROPPED,
+ AnimeWatchingStatus::COMPLETED => self::COMPLETED,
+ ];
+
/**
* Update the selected anime
*
@@ -59,32 +71,11 @@ class Anime extends API {
self::COMPLETED => [],
];
- $data = $this->_get_list();
+ $data = $this->_get_list_from_api();
foreach($data as $datum)
{
- switch($datum['status'])
- {
- case AnimeWatchingStatus::COMPLETED:
- $output[self::COMPLETED][] = $datum;
- break;
-
- case AnimeWatchingStatus::PLAN_TO_WATCH:
- $output[self::PLAN_TO_WATCH][] = $datum;
- break;
-
- case AnimeWatchingStatus::DROPPED:
- $output[self::DROPPED][] = $datum;
- break;
-
- case AnimeWatchingStatus::ON_HOLD:
- $output[self::ON_HOLD][] = $datum;
- break;
-
- case AnimeWatchingStatus::WATCHING:
- $output[self::WATCHING][] = $datum;
- break;
- }
+ $output[$this->const_map[$datum['watching_status']]][] = $datum;
}
// Sort anime by name
@@ -104,19 +95,11 @@ class Anime extends API {
*/
public function get_list($status)
{
- $map = [
- AnimeWatchingStatus::WATCHING => self::WATCHING,
- AnimeWatchingStatus::PLAN_TO_WATCH => self::PLAN_TO_WATCH,
- AnimeWatchingStatus::ON_HOLD => self::ON_HOLD,
- AnimeWatchingStatus::DROPPED => self::DROPPED,
- AnimeWatchingStatus::COMPLETED => self::COMPLETED,
- ];
-
$data = $this->_get_list_From_api($status);
$this->sort_by_name($data);
$output = [];
- $output[$map[$status]] = $data;
+ $output[$this->const_map[$status]] = $data;
return $output;
}
@@ -170,10 +153,11 @@ class Anime extends API {
/**
* Retrieve data from the api
*
+ * @codeCoverageIgnore
* @param string $status
* @return array
*/
- private function _get_list_from_api($status="all")
+ protected function _get_list_from_api($status="all")
{
$config = [
'allow_redirects' => FALSE
@@ -198,29 +182,30 @@ class Anime extends API {
/**
* Handle caching of transformed api data
*
+ * @codeCoverageIgnore
* @param string $status
* @param \GuzzleHttp\Message\Response
* @return array
*/
- private function _check_cache($status, $response)
+ protected function _check_cache($status, $response)
{
- $cache_file = "{$this->config->data_cache_path}/anime-{$status}.json";
- $transformed_cache_file = "{$this->config->data_cache_path}/anime-{$status}-transformed.json";
+ $cache_file = _dir($this->config->data_cache_path, "anime-{$status}.json");
+ $transformed_cache_file = _dir($this->config->data_cache_path, "anime-{$status}-transformed.json");
$cached = json_decode(file_get_contents($cache_file), TRUE);
- $api = $response->json();
+ $api_data = json_decode($response->getBody(), TRUE);
- if ($api !== $cached)
+ if ($api_data === $cached && file_exists($transformed_cache_file))
{
- file_put_contents($cache_file, json_encode($api));
- $transformer = new AnimeListTransformer();
- $transformed = $transformer->transform_collection($api);
- file_put_contents($transformed_cache_file, json_encode($transformed));
- return $transformed;
+ return json_decode(file_get_contents($transformed_cache_file),TRUE);
}
else
{
- return json_decode(file_get_contents($transformed_cache_file),TRUE);
+ file_put_contents($cache_file, json_encode($api_data));
+ $transformer = new AnimeListTransformer();
+ $transformed = $transformer->transform_collection($api_data);
+ file_put_contents($transformed_cache_file, json_encode($transformed));
+ return $transformed;
}
}
@@ -230,7 +215,7 @@ class Anime extends API {
* @param array $array
* @return void
*/
- private function sort_by_name(&$array)
+ protected function sort_by_name(&$array)
{
$sort = array();
diff --git a/src/Aviat/AnimeClient/Model/Manga.php b/src/Aviat/AnimeClient/Model/Manga.php
index 9346be39..3ce4f256 100644
--- a/src/Aviat/AnimeClient/Model/Manga.php
+++ b/src/Aviat/AnimeClient/Model/Manga.php
@@ -6,12 +6,31 @@ namespace Aviat\AnimeClient\Model;
use Aviat\AnimeClient\Model\API;
use Aviat\AnimeClient\Transformer\Hummingbird;
+use Aviat\AnimeClient\Enum\Hummingbird\MangaReadingStatus;
/**
* Model for handling requests dealing with the manga list
*/
class Manga extends API {
+ const READING = 'Reading';
+ const PLAN_TO_READ = 'Plan to Read';
+ const DROPPED = 'Dropped';
+ const ON_HOLD = 'On Hold';
+ const COMPLETED = 'Completed';
+
+ /**
+ * Map API constants to display constants
+ * @var array
+ */
+ protected $const_map = [
+ MangaReadingStatus::READING => self::READING,
+ MangaReadingStatus::PLAN_TO_READ => self::PLAN_TO_READ,
+ MangaReadingStatus::ON_HOLD => self::ON_HOLD,
+ MangaReadingStatus::DROPPED => self::DROPPED,
+ MangaReadingStatus::COMPLETED => self::COMPLETED
+ ];
+
/**
* The base url for api requests
* @var string
@@ -44,9 +63,9 @@ class Manga extends API {
*/
public function get_all_lists()
{
- $data = $this->_get_list();
+ $data = $this->_get_list_from_api();
- foreach ($data as $key => &$val)
+ foreach($data as $key => &$val)
{
$this->sort_by_name($val);
}
@@ -62,24 +81,14 @@ class Manga extends API {
*/
public function get_list($status)
{
- $data = $this->_get_list($status);
-
+ $data = $this->_get_list_from_api($status);
$this->sort_by_name($data);
return $data;
}
- /**
- * Massage the list of manga entries into something more usable
- *
- * @param string $status
- * @return array
- */
- private function _get_list($status="all")
+ private function _get_list_from_api($status="All")
{
- $errorHandler = $this->container->get('error-handler');
-
- $cache_file = _dir($this->config->data_cache_path, 'manga.json');
$config = [
'query' => [
@@ -89,81 +98,70 @@ class Manga extends API {
];
$response = $this->client->get('manga_library_entries', $config);
+ $data = $this->_check_cache($status, $response);
+ $output = $this->map_by_status($data);
- $errorHandler->addDataTable('response', (array)$response);
+ return (array_key_exists($status, $output)) ? $output[$status] : $output;
+ }
- if ($response->getStatusCode() != 200)
+ /**
+ * Check the status of the cache and return the appropriate response
+ *
+ * @param string $status
+ * @param \GuzzleHttp\Message\Response $response
+ * @return array
+ */
+ private function _check_cache($status, $response)
+ {
+ // Bail out early if there isn't any manga data
+ $api_data = json_decode($response->getBody(), TRUE);
+ if ( ! array_key_exists('manga', $api_data)) return [];
+
+ $cache_file = _dir($this->config->data_cache_path, 'manga.json');
+ $transformed_cache_file = _dir($this->config->data_cache_path, 'manga-transformed.json');
+
+ $cached_data = json_decode(file_get_contents($cache_file), TRUE);
+
+ if ($cached_data === $api_data && file_exists($transformed_cache_file))
{
- if ( ! file_exists($cache_file))
- {
- throw new DomainException($response->getEffectiveUrl());
- }
- else
- {
- $raw_data = json_decode(file_get_contents($cache_file), TRUE);
- }
+ return json_decode(file_get_contents($transformed_cache_file), TRUE);
}
else
{
- // Reorganize data to be more usable
- $raw_data = $response->json();
+ file_put_contents($cache_file, json_encode($api_data));
- // Attempt to create the cache dir if it doesn't exist
- if ( ! is_dir($this->config->data_cache_path))
- {
- mkdir($this->config->data_cache_path);
- }
-
- // Cache data in case of downtime
- file_put_contents($cache_file, json_encode($raw_data));
+ $zippered_data = $this->zipper_lists($api_data);
+ $transformer = new Hummingbird\MangaListTransformer();
+ $transformed_data = $transformer->transform_collection($zippered_data);
+ file_put_contents($transformed_cache_file, json_encode($transformed_data));
+ return $transformed_data;
}
+ }
- // Bail out early if there isn't any manga data
- if ( ! array_key_exists('manga', $raw_data)) return [];
-
- $data = [
- 'Reading' => [],
- 'Plan to Read' => [],
- 'On Hold' => [],
- 'Dropped' => [],
- 'Completed' => [],
+ /**
+ * Map transformed anime data to be organized by reading status
+ *
+ * @param array $data
+ * @return array
+ */
+ private function map_by_status($data)
+ {
+ $output = [
+ self::READING => [],
+ self::PLAN_TO_READ => [],
+ self::ON_HOLD => [],
+ self::DROPPED => [],
+ self::COMPLETED => [],
];
- // Massage the two lists into one
- $manga_data = $this->zipper_lists($raw_data);
-
- // Filter data by status
- foreach($manga_data as &$entry)
+ foreach($data as &$entry)
{
- // Cache poster images
- $entry['manga']['poster_image'] = $this->get_cached_image($entry['manga']['poster_image'], $entry['manga']['id'], 'manga');
-
- switch($entry['status'])
- {
- case "Plan to Read":
- $data['Plan to Read'][] = $entry;
- break;
-
- case "Dropped":
- $data['Dropped'][] = $entry;
- break;
-
- case "On Hold":
- $data['On Hold'][] = $entry;
- break;
-
- case "Currently Reading":
- $data['Reading'][] = $entry;
- break;
-
- case "Completed":
- default:
- $data['Completed'][] = $entry;
- break;
- }
+ $entry['manga']['image'] = $this->get_cached_image($entry['manga']['image'], $entry['manga']['slug'], 'manga');
+ $key = $this->const_map[$entry['reading_status']];
+ $output[$key][] = $entry;
}
- return (array_key_exists($status, $data)) ? $data[$status] : $data;
+ return $output;
}
/**
@@ -189,10 +187,10 @@ class Manga extends API {
foreach($array as $key => $item)
{
- $sort[$key] = $item['manga']['romaji_title'];
+ $sort[$key] = $item['manga']['title'];
}
array_multisort($sort, SORT_ASC, $array);
}
}
-// End of MangaModel.php
+// End of MangaModel.php
\ No newline at end of file
diff --git a/src/Aviat/Ion/ArrayWrapper.php b/src/Aviat/Ion/ArrayWrapper.php
new file mode 100644
index 00000000..2550b9af
--- /dev/null
+++ b/src/Aviat/Ion/ArrayWrapper.php
@@ -0,0 +1,21 @@
+ 'array_chunk',
+ 'pluck' => 'array_column',
+ 'assoc_diff' => 'array_diff_assoc',
+ 'key_diff' => 'array_diff_key',
+ 'diff' => 'array_diff',
+ 'filter' => 'array_filter',
+ 'flip' => 'array_flip',
+ 'intersect' => 'array_intersect',
+ 'has_key' => 'array_key_exists',
+ 'keys' => 'array_keys',
+ 'merge' => 'array_merge',
+ 'pad' => 'array_pad',
+ 'pop' => 'array_pop',
+ 'product' => 'array_product',
+ 'push' => 'array_push',
+ 'random' => 'array_rand',
+ 'reduce' => 'array_reduce',
+ 'reverse' => 'array_reverse',
+ 'shift' => 'array_shift',
+ 'sum' => 'array_sum',
+ 'unique' => 'array_unique',
+ 'unshift' => 'array_unshift',
+ 'values' => 'array_values',
+ ];
+
+ /**
+ * Native methods that modify the passed in array
+ *
+ * @var array
+ */
+ protected $native_in_place_methods = [
+ 'shuffle' => 'shuffle',
+ ];
+
+ /**
+ * Create an ArrayType wrapper class
+ *
+ * @param array $arr
+ */
+ public function __construct(Array $arr)
+ {
+ $this->arr =& $arr;
+ }
+
+ /**
+ * Call one of the dynamically created methods
+ *
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ */
+ public function __call($method, $args)
+ {
+ // Simple mapping for the majority of methods
+ if (array_key_exists($method, $this->native_methods))
+ {
+ $func = $this->native_methods[$method];
+ // Set the current array as the first argument of the method
+ array_unshift($args, $this->arr);
+ return call_user_func_array($func, $args);
+ }
+
+ // Mapping for in-place methods
+ if (array_key_exists($method, $this->native_in_place_methods))
+ {
+ $func = $this->native_in_place_methods[$method];
+ $func($this->arr);
+ return $this->arr;
+ }
+ }
+
+ /**
+ * Fill an array with the specified value
+ *
+ * @param int $start_index
+ * @param int $num
+ * @param mixed $value
+ * @return array
+ */
+ public function fill($start_index, $num, $value)
+ {
+ return array_fill($start_index, $num, $value);
+ }
+
+ /**
+ * Call a callback on each item of the array
+ *
+ * @param callable $callback
+ * @return array
+ */
+ public function map(callable $callback)
+ {
+ return array_map($callback, $this->arr);
+ }
+
+ /**
+ * Find an array key by its associated value
+ *
+ * @param mixed $value
+ * @param bool $strict
+ * @return string
+ */
+ public function search($value, $strict=FALSE)
+ {
+ return array_search($value, $this->arr, $strict);
+ }
+
+ /**
+ * Determine if the array has the passed value
+ *
+ * @param mixed $value
+ * @param bool $strict
+ * @return bool
+ */
+ public function has($value, $strict=FALSE)
+ {
+ return in_array($value, $this->arr, $strict);
+ }
+}
+// End of ArrayType.php
\ No newline at end of file
diff --git a/src/Aviat/Ion/Type/StringType.php b/src/Aviat/Ion/Type/StringType.php
new file mode 100644
index 00000000..e9bc4d1b
--- /dev/null
+++ b/src/Aviat/Ion/Type/StringType.php
@@ -0,0 +1,10 @@
+helper = (new HelperLocatorFactory)->newInstance();
+ $this->helper = $container->get('html-helper');
}
/**
diff --git a/tests/Ion/Type/ArrayTypeTest.php b/tests/Ion/Type/ArrayTypeTest.php
new file mode 100644
index 00000000..bc96c3b8
--- /dev/null
+++ b/tests/Ion/Type/ArrayTypeTest.php
@@ -0,0 +1,54 @@
+arr([1, 3, 5, 7]);
+ $even_array = [2, 4, 6, 8];
+ $expected = [1, 3, 5, 7, 2, 4, 6, 8];
+
+ $actual = $obj->merge($even_array);
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testShuffle()
+ {
+ $original = [1, 2, 3, 4];
+ $test = [1, 2, 3, 4];
+ $obj = $this->arr($test);
+ $actual = $obj->shuffle();
+
+ $this->assertNotEquals($actual, $original);
+ $this->assertTrue(is_array($actual));
+ }
+
+ public function testHas()
+ {
+ $obj = $this->arr([1, 2, 6, 8, 11]);
+ $this->assertTrue($obj->has(8));
+ $this->assertFalse($obj->has(8745));
+ }
+
+ public function testSearch()
+ {
+ $obj = $this->arr([1, 2, 5, 7, 47]);
+ $actual = $obj->search(47);
+ $this->assertEquals(4, $actual);
+ }
+
+ public function testFill()
+ {
+ $obj = $this->arr([]);
+ $expected = ['?', '?', '?'];
+ $actual = $obj->fill(0, 3, '?');
+ $this->assertEquals($actual, $expected);
+ }
+}
\ No newline at end of file