Version 5.1 - All the GraphQL #32
@ -31,14 +31,14 @@ A self-hosted client that allows custom formatting of data from the hummingbird
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
* PHP 5.4+
|
* PHP 5.5+
|
||||||
* PDO SQLite (For collection tab)
|
* PDO SQLite (For collection tab)
|
||||||
* GD
|
* GD
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. Install dependencies via composer: `composer install`
|
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
|
3. Create the following directories if they don't exist, and make sure they are world writable
|
||||||
* app/cache
|
* app/cache
|
||||||
* public/images/manga
|
* public/images/manga
|
||||||
|
@ -7,6 +7,7 @@ namespace Aviat\AnimeClient;
|
|||||||
|
|
||||||
use \Whoops\Handler\PrettyPageHandler;
|
use \Whoops\Handler\PrettyPageHandler;
|
||||||
use \Whoops\Handler\JsonResponseHandler;
|
use \Whoops\Handler\JsonResponseHandler;
|
||||||
|
use Aura\Html\HelperLocatorFactory;
|
||||||
use \Aura\Web\WebFactory;
|
use \Aura\Web\WebFactory;
|
||||||
use \Aura\Router\RouterFactory;
|
use \Aura\Router\RouterFactory;
|
||||||
use \Aura\Session\SessionFactory;
|
use \Aura\Session\SessionFactory;
|
||||||
@ -51,6 +52,15 @@ $di = function() {
|
|||||||
$aura_router = (new RouterFactory())->newInstance();
|
$aura_router = (new RouterFactory())->newInstance();
|
||||||
$container->set('aura-router', $aura_router);
|
$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
|
// Create Request/Response Objects
|
||||||
$web_factory = new WebFactory([
|
$web_factory = new WebFactory([
|
||||||
'_GET' => $_GET,
|
'_GET' => $_GET,
|
||||||
@ -79,4 +89,4 @@ $di = function() {
|
|||||||
|
|
||||||
$di()->get('router')->dispatch();
|
$di()->get('router')->dispatch();
|
||||||
|
|
||||||
// End of bootstrap.php
|
// End of bootstrap.php
|
@ -13,6 +13,7 @@ $base_config = [
|
|||||||
'img_cache_path' => _dir(ROOT_DIR, 'public/images'),
|
'img_cache_path' => _dir(ROOT_DIR, 'public/images'),
|
||||||
|
|
||||||
// Included config files
|
// Included config files
|
||||||
'routes' => require __DIR__ . '/routes.php',
|
|
||||||
'database' => require __DIR__ . '/database.php',
|
'database' => require __DIR__ . '/database.php',
|
||||||
|
'menus' => require __DIR__ . '/menus.php',
|
||||||
|
'routes' => require __DIR__ . '/routes.php',
|
||||||
];
|
];
|
@ -14,9 +14,12 @@ $config = [
|
|||||||
// General config
|
// General config
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// do you wish to show the anime collection tab?
|
// do you wish to show the anime collection?
|
||||||
'show_anime_collection' => TRUE,
|
'show_anime_collection' => TRUE,
|
||||||
|
|
||||||
|
// do you wish to show the manga collection?
|
||||||
|
'show_manga_collection' => TRUE,
|
||||||
|
|
||||||
// path to public directory on the server
|
// path to public directory on the server
|
||||||
'asset_dir' => realpath(__DIR__ . '/../../public'),
|
'asset_dir' => realpath(__DIR__ . '/../../public'),
|
||||||
|
|
||||||
|
61
app/config/menus.php
Normal file
61
app/config/menus.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'top' => [
|
||||||
|
'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'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
@ -9,99 +9,56 @@ return [
|
|||||||
// Routes on all controllers
|
// Routes on all controllers
|
||||||
'common' => [
|
'common' => [
|
||||||
'update' => [
|
'update' => [
|
||||||
'path' => '/update',
|
'path' => '/{controller}/update',
|
||||||
'action' => ['update'],
|
'action' => 'update',
|
||||||
'verb' => 'post'
|
'verb' => 'post'
|
||||||
],
|
],
|
||||||
'login_form' => [
|
'login_form' => [
|
||||||
'path' => '/login',
|
'path' => '/{controller}/login',
|
||||||
'action' => ['login'],
|
'action' => 'login',
|
||||||
'verb' => 'get'
|
'verb' => 'get'
|
||||||
],
|
],
|
||||||
'login_action' => [
|
'login_action' => [
|
||||||
'path' => '/login',
|
'path' => '/{controller}/login',
|
||||||
'action' => ['login_action'],
|
'action' => 'login_action',
|
||||||
'verb' => 'post'
|
'verb' => 'post'
|
||||||
],
|
],
|
||||||
'logout' => [
|
'logout' => [
|
||||||
'path' => '/logout',
|
'path' => '/{controller}/logout',
|
||||||
'action' => ['logout']
|
'action' => 'logout'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
// Routes on collection controller
|
// Routes on collection controller
|
||||||
'collection' => [
|
'collection' => [
|
||||||
'collection_add_form' => [
|
'collection_add_form' => [
|
||||||
'path' => '/collection/add',
|
'path' => '/collection/add',
|
||||||
'action' => ['form'],
|
'action' => 'form',
|
||||||
'params' => [],
|
'params' => [],
|
||||||
],
|
],
|
||||||
'collection_edit_form' => [
|
'collection_edit_form' => [
|
||||||
'path' => '/collection/edit/{id}',
|
'path' => '/collection/edit/{id}',
|
||||||
'action' => ['form'],
|
'action' => 'form',
|
||||||
'tokens' => [
|
'tokens' => [
|
||||||
'id' => '[0-9]+'
|
'id' => '[0-9]+'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'collection_add' => [
|
'collection_add' => [
|
||||||
'path' => '/collection/add',
|
'path' => '/collection/add',
|
||||||
'action' => ['add'],
|
'action' => 'add',
|
||||||
'verb' => 'post'
|
'verb' => 'post'
|
||||||
],
|
],
|
||||||
'collection_edit' => [
|
'collection_edit' => [
|
||||||
'path' => '/collection/edit',
|
'path' => '/collection/edit',
|
||||||
'action' => ['edit'],
|
'action' => 'edit',
|
||||||
'verb' => 'post'
|
'verb' => 'post'
|
||||||
],
|
],
|
||||||
'collection' => [
|
'collection' => [
|
||||||
'path' => '/collection/view{/view}',
|
'path' => '/collection/view{/view}',
|
||||||
'action' => ['index'],
|
'action' => 'index',
|
||||||
'params' => [],
|
'params' => [],
|
||||||
'tokens' => [
|
'tokens' => [
|
||||||
'view' => '[a-z_]+'
|
'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_]+'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// Subfolder prefix for url
|
// Subfolder prefix for url, if in a subdirectory of the web root
|
||||||
'subfolder_prefix' => '',
|
'subfolder_prefix' => '',
|
||||||
|
|
||||||
// Path to public directory, where images/css/javascript are located,
|
// Path to public directory, where images/css/javascript are located,
|
||||||
@ -16,9 +16,9 @@ return [
|
|||||||
'default_list' => 'anime', // anime or manga
|
'default_list' => 'anime', // anime or manga
|
||||||
|
|
||||||
// Default pages for anime/manga
|
// Default pages for anime/manga
|
||||||
'default_anime_path' => "/anime/watching",
|
'default_anime_list_path' => "watching", // watching|plan_to_watch|on_hold|dropped|completed|all
|
||||||
'default_manga_path' => '/manga/all',
|
'default_manga_list_path' => "all", // reading|plan_to_read|on_hold|dropped|completed|all
|
||||||
|
|
||||||
// Default to list view?
|
// Default view type (cover_view/list_view)
|
||||||
'default_to_list_view' => FALSE,
|
'default_view_type' => 'cover_view',
|
||||||
];
|
];
|
@ -11,6 +11,6 @@
|
|||||||
<template name="clean" />
|
<template name="clean" />
|
||||||
</transformations>
|
</transformations>
|
||||||
<files>
|
<files>
|
||||||
<directory>src/Aviat/AnimeClient</directory>
|
<directory>src/Aviat</directory>
|
||||||
</files>
|
</files>
|
||||||
</phpdoc>
|
</phpdoc>
|
@ -4,8 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aura\Web\ResponseSender;
|
|
||||||
|
|
||||||
use \Aviat\Ion\Di\ContainerInterface;
|
use \Aviat\Ion\Di\ContainerInterface;
|
||||||
use \Aviat\Ion\View\HttpView;
|
use \Aviat\Ion\View\HttpView;
|
||||||
use \Aviat\Ion\View\HtmlView;
|
use \Aviat\Ion\View\HtmlView;
|
||||||
@ -97,6 +95,7 @@ class Controller {
|
|||||||
public function load_partial($view, $template, $data=[])
|
public function load_partial($view, $template, $data=[])
|
||||||
{
|
{
|
||||||
$errorHandler = $this->container->get('error-handler');
|
$errorHandler = $this->container->get('error-handler');
|
||||||
|
$errorHandler->addDataTable('Template Data', $data);
|
||||||
$router = $this->container->get('router');
|
$router = $this->container->get('router');
|
||||||
|
|
||||||
if (isset($this->base_data))
|
if (isset($this->base_data))
|
||||||
@ -107,7 +106,7 @@ class Controller {
|
|||||||
$route = $router->get_route();
|
$route = $router->get_route();
|
||||||
$data['route_path'] = ($route) ? $router->get_route()->path : "";
|
$data['route_path'] = ($route) ? $router->get_route()->path : "";
|
||||||
|
|
||||||
$errorHandler->addDataTable('Template Data', $data);
|
|
||||||
$template_path = _dir($this->config->__get('view_path'), "{$template}.php");
|
$template_path = _dir($this->config->__get('view_path'), "{$template}.php");
|
||||||
|
|
||||||
if ( ! is_file($template_path))
|
if ( ! is_file($template_path))
|
||||||
@ -170,7 +169,7 @@ class Controller {
|
|||||||
{
|
{
|
||||||
$url = $this->urlGenerator->full_url($path, $type);
|
$url = $this->urlGenerator->full_url($path, $type);
|
||||||
$http = new HttpView($this->container);
|
$http = new HttpView($this->container);
|
||||||
|
|
||||||
$http->redirect($url, $code);
|
$http->redirect($url, $code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ namespace Aviat\AnimeClient\Controller;
|
|||||||
|
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\AnimeClient\Controller as BaseController;
|
use Aviat\AnimeClient\Controller as BaseController;
|
||||||
|
use Aviat\AnimeClient\Enum\Hummingbird\AnimeWatchingStatus;
|
||||||
use Aviat\AnimeClient\Model\Anime as AnimeModel;
|
use Aviat\AnimeClient\Model\Anime as AnimeModel;
|
||||||
use Aviat\AnimeClient\Model\AnimeCollection as AnimeCollectionModel;
|
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
|
* Search for anime
|
||||||
*
|
*
|
||||||
@ -87,11 +93,10 @@ class Anime extends BaseController {
|
|||||||
* Show a portion, or all of the anime list
|
* Show a portion, or all of the anime list
|
||||||
*
|
*
|
||||||
* @param string $type - The section of the list
|
* @param string $type - The section of the list
|
||||||
* @param string $title - The title of the page
|
|
||||||
* @param string $view - List or cover view
|
* @param string $view - List or cover view
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function anime_list($type, $view)
|
protected function anime_list($type, $view)
|
||||||
{
|
{
|
||||||
$type_title_map = [
|
$type_title_map = [
|
||||||
'all' => 'All',
|
'all' => 'All',
|
||||||
@ -103,12 +108,12 @@ class Anime extends BaseController {
|
|||||||
];
|
];
|
||||||
|
|
||||||
$model_map = [
|
$model_map = [
|
||||||
'watching' => 'currently-watching',
|
'watching' => AnimeWatchingStatus::WATCHING,
|
||||||
'plan_to_watch' => 'plan-to-watch',
|
'plan_to_watch' => AnimeWatchingStatus::PLAN_TO_WATCH,
|
||||||
'on_hold' => 'on-hold',
|
'on_hold' => AnimeWatchingStatus::ON_HOLD,
|
||||||
'all' => 'all',
|
'all' => 'all',
|
||||||
'dropped' => 'dropped',
|
'dropped' => AnimeWatchingStatus::DROPPED,
|
||||||
'completed' => 'completed'
|
'completed' => AnimeWatchingStatus::COMPLETED
|
||||||
];
|
];
|
||||||
|
|
||||||
$title = $this->config->whose_list . "'s Anime List · {$type_title_map[$type]}";
|
$title = $this->config->whose_list . "'s Anime List · {$type_title_map[$type]}";
|
||||||
|
@ -58,6 +58,11 @@ class Manga extends Controller {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function index($status="all", $view="")
|
||||||
|
{
|
||||||
|
return $this->manga_list($status, $view);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update an anime item
|
* Update an anime item
|
||||||
*
|
*
|
||||||
@ -75,15 +80,15 @@ class Manga extends Controller {
|
|||||||
* @param string $view
|
* @param string $view
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function manga_list($status, $view)
|
protected function manga_list($status, $view)
|
||||||
{
|
{
|
||||||
$map = [
|
$map = [
|
||||||
'all' => 'All',
|
'all' => 'All',
|
||||||
'plan_to_read' => 'Plan to Read',
|
'plan_to_read' => MangaModel::PLAN_TO_READ,
|
||||||
'reading' => 'Reading',
|
'reading' => MangaModel::READING,
|
||||||
'completed' => 'Completed',
|
'completed' => MangaModel::COMPLETED,
|
||||||
'dropped' => 'Dropped',
|
'dropped' => MangaModel::DROPPED,
|
||||||
'on_hold' => 'On Hold'
|
'on_hold' => MangaModel::ON_HOLD
|
||||||
];
|
];
|
||||||
|
|
||||||
$title = $this->config->whose_list . "'s Manga List · {$map[$status]}";
|
$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])]
|
? [$map[$status] => $this->model->get_list($map[$status])]
|
||||||
: $this->model->get_all_lists();
|
: $this->model->get_all_lists();
|
||||||
|
|
||||||
|
//throw new \ErrorException("Data :" . print_r($data, TRUE));
|
||||||
|
|
||||||
$this->outputHTML('manga/' . $view_map[$view], [
|
$this->outputHTML('manga/' . $view_map[$view], [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'sections' => $data,
|
'sections' => $data,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of MangaController.php
|
// End of MangaController.php
|
20
src/Aviat/AnimeClient/Helper/Menu.php
Normal file
20
src/Aviat/AnimeClient/Helper/Menu.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Helper;
|
||||||
|
|
||||||
|
use Aura\Html\Helper\AbstractHelper;
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\MenuGenerator;
|
||||||
|
|
||||||
|
class Menu extends AbstractHelper {
|
||||||
|
|
||||||
|
use \Aviat\Ion\Di\ContainerAware;
|
||||||
|
|
||||||
|
public function __invoke($menu_name)
|
||||||
|
{
|
||||||
|
$generator = new MenuGenerator($this->container);
|
||||||
|
return $generator->generate($menu_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// End of Menu.php
|
19
src/Aviat/AnimeClient/Helper/UrlHelper.php
Normal file
19
src/Aviat/AnimeClient/Helper/UrlHelper.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient\Helper;
|
||||||
|
|
||||||
|
use Aura\Html\Helper\AbstractHelper;
|
||||||
|
use Aviat\AnimeClient\UrlGenerator;
|
||||||
|
|
||||||
|
class UrlHelper extends AbstractHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper entry point
|
||||||
|
*
|
||||||
|
* @return UrlHelper
|
||||||
|
*/
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
106
src/Aviat/AnimeClient/MenuGenerator.php
Normal file
106
src/Aviat/AnimeClient/MenuGenerator.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper object to manage menu creation and selection
|
||||||
|
*/
|
||||||
|
class MenuGenerator extends RoutingBase {
|
||||||
|
|
||||||
|
use \Aviat\Ion\Di\ContainerAware;
|
||||||
|
use \Aviat\Ion\StringWrapper;
|
||||||
|
use \Aviat\Ion\ArrayWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Html generation helper
|
||||||
|
*
|
||||||
|
* @var Aura\Html\HelperLocator
|
||||||
|
*/
|
||||||
|
protected $helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu config array
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $menus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create menu generator
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
parent::__construct($container);
|
||||||
|
$this->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
|
@ -4,14 +4,16 @@
|
|||||||
*/
|
*/
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
|
||||||
use abeautifulsite\SimpleImage;
|
use abeautifulsite\SimpleImage;
|
||||||
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common base for all Models
|
* Common base for all Models
|
||||||
*/
|
*/
|
||||||
class Model {
|
class Model {
|
||||||
|
|
||||||
|
use \Aviat\Ion\StringWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global configuration object
|
* The global configuration object
|
||||||
* @var Config
|
* @var Config
|
||||||
@ -65,11 +67,11 @@ class Model {
|
|||||||
// Cache the file if it doesn't already exist
|
// Cache the file if it doesn't already exist
|
||||||
if ( ! file_exists($cached_path))
|
if ( ! file_exists($cached_path))
|
||||||
{
|
{
|
||||||
if (ini_get('allow_url_fopen'))
|
/*if (ini_get('allow_url_fopen'))
|
||||||
{
|
{
|
||||||
copy($api_path, $cached_path);
|
copy($api_path, $cached_path);
|
||||||
}
|
}
|
||||||
elseif (function_exists('curl_init'))
|
else*/if (function_exists('curl_init'))
|
||||||
{
|
{
|
||||||
$ch = curl_init($api_path);
|
$ch = curl_init($api_path);
|
||||||
$fp = fopen($cached_path, 'wb');
|
$fp = fopen($cached_path, 'wb');
|
||||||
@ -79,7 +81,7 @@ class Model {
|
|||||||
]);
|
]);
|
||||||
curl_exec($ch);
|
curl_exec($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
fclose($ch);
|
fclose($fp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
namespace Aviat\AnimeClient\Model;
|
namespace Aviat\AnimeClient\Model;
|
||||||
|
|
||||||
use \GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use \GuzzleHttp\Cookie\CookieJar;
|
use GuzzleHttp\Cookie\CookieJar;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
use Aviat\AnimeClient\Model as BaseModel;
|
use Aviat\AnimeClient\Model as BaseModel;
|
||||||
|
|
||||||
@ -42,7 +44,8 @@ class API extends BaseModel {
|
|||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->cookieJar = new CookieJar();
|
$this->cookieJar = new CookieJar();
|
||||||
$this->client = new Client([
|
$this->client = new Client([
|
||||||
'base_url' => $this->base_url,
|
'base_uri' => $this->base_url,
|
||||||
|
'cookies' => TRUE,
|
||||||
'defaults' => [
|
'defaults' => [
|
||||||
'cookies' => $this->cookieJar,
|
'cookies' => $this->cookieJar,
|
||||||
'headers' => [
|
'headers' => [
|
||||||
|
@ -27,6 +27,18 @@ class Anime extends API {
|
|||||||
*/
|
*/
|
||||||
protected $base_url = "https://hummingbird.me/api/v1/";
|
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
|
* Update the selected anime
|
||||||
*
|
*
|
||||||
@ -59,32 +71,11 @@ class Anime extends API {
|
|||||||
self::COMPLETED => [],
|
self::COMPLETED => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
$data = $this->_get_list();
|
$data = $this->_get_list_from_api();
|
||||||
|
|
||||||
foreach($data as $datum)
|
foreach($data as $datum)
|
||||||
{
|
{
|
||||||
switch($datum['status'])
|
$output[$this->const_map[$datum['watching_status']]][] = $datum;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort anime by name
|
// Sort anime by name
|
||||||
@ -104,19 +95,11 @@ class Anime extends API {
|
|||||||
*/
|
*/
|
||||||
public function get_list($status)
|
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);
|
$data = $this->_get_list_From_api($status);
|
||||||
$this->sort_by_name($data);
|
$this->sort_by_name($data);
|
||||||
|
|
||||||
$output = [];
|
$output = [];
|
||||||
$output[$map[$status]] = $data;
|
$output[$this->const_map[$status]] = $data;
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
@ -170,10 +153,11 @@ class Anime extends API {
|
|||||||
/**
|
/**
|
||||||
* Retrieve data from the api
|
* Retrieve data from the api
|
||||||
*
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
* @param string $status
|
* @param string $status
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function _get_list_from_api($status="all")
|
protected function _get_list_from_api($status="all")
|
||||||
{
|
{
|
||||||
$config = [
|
$config = [
|
||||||
'allow_redirects' => FALSE
|
'allow_redirects' => FALSE
|
||||||
@ -198,29 +182,30 @@ class Anime extends API {
|
|||||||
/**
|
/**
|
||||||
* Handle caching of transformed api data
|
* Handle caching of transformed api data
|
||||||
*
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
* @param string $status
|
* @param string $status
|
||||||
* @param \GuzzleHttp\Message\Response
|
* @param \GuzzleHttp\Message\Response
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function _check_cache($status, $response)
|
protected function _check_cache($status, $response)
|
||||||
{
|
{
|
||||||
$cache_file = "{$this->config->data_cache_path}/anime-{$status}.json";
|
$cache_file = _dir($this->config->data_cache_path, "anime-{$status}.json");
|
||||||
$transformed_cache_file = "{$this->config->data_cache_path}/anime-{$status}-transformed.json";
|
$transformed_cache_file = _dir($this->config->data_cache_path, "anime-{$status}-transformed.json");
|
||||||
|
|
||||||
$cached = json_decode(file_get_contents($cache_file), TRUE);
|
$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));
|
return json_decode(file_get_contents($transformed_cache_file),TRUE);
|
||||||
$transformer = new AnimeListTransformer();
|
|
||||||
$transformed = $transformer->transform_collection($api);
|
|
||||||
file_put_contents($transformed_cache_file, json_encode($transformed));
|
|
||||||
return $transformed;
|
|
||||||
}
|
}
|
||||||
else
|
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
|
* @param array $array
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function sort_by_name(&$array)
|
protected function sort_by_name(&$array)
|
||||||
{
|
{
|
||||||
$sort = array();
|
$sort = array();
|
||||||
|
|
||||||
|
@ -6,12 +6,31 @@ namespace Aviat\AnimeClient\Model;
|
|||||||
|
|
||||||
use Aviat\AnimeClient\Model\API;
|
use Aviat\AnimeClient\Model\API;
|
||||||
use Aviat\AnimeClient\Transformer\Hummingbird;
|
use Aviat\AnimeClient\Transformer\Hummingbird;
|
||||||
|
use Aviat\AnimeClient\Enum\Hummingbird\MangaReadingStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model for handling requests dealing with the manga list
|
* Model for handling requests dealing with the manga list
|
||||||
*/
|
*/
|
||||||
class Manga extends API {
|
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
|
* The base url for api requests
|
||||||
* @var string
|
* @var string
|
||||||
@ -44,9 +63,9 @@ class Manga extends API {
|
|||||||
*/
|
*/
|
||||||
public function get_all_lists()
|
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);
|
$this->sort_by_name($val);
|
||||||
}
|
}
|
||||||
@ -62,24 +81,14 @@ class Manga extends API {
|
|||||||
*/
|
*/
|
||||||
public function get_list($status)
|
public function get_list($status)
|
||||||
{
|
{
|
||||||
$data = $this->_get_list($status);
|
$data = $this->_get_list_from_api($status);
|
||||||
|
|
||||||
$this->sort_by_name($data);
|
$this->sort_by_name($data);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function _get_list_from_api($status="All")
|
||||||
* Massage the list of manga entries into something more usable
|
|
||||||
*
|
|
||||||
* @param string $status
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function _get_list($status="all")
|
|
||||||
{
|
{
|
||||||
$errorHandler = $this->container->get('error-handler');
|
|
||||||
|
|
||||||
$cache_file = _dir($this->config->data_cache_path, 'manga.json');
|
|
||||||
|
|
||||||
$config = [
|
$config = [
|
||||||
'query' => [
|
'query' => [
|
||||||
@ -89,81 +98,70 @@ class Manga extends API {
|
|||||||
];
|
];
|
||||||
|
|
||||||
$response = $this->client->get('manga_library_entries', $config);
|
$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))
|
return json_decode(file_get_contents($transformed_cache_file), TRUE);
|
||||||
{
|
|
||||||
throw new DomainException($response->getEffectiveUrl());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$raw_data = json_decode(file_get_contents($cache_file), TRUE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Reorganize data to be more usable
|
file_put_contents($cache_file, json_encode($api_data));
|
||||||
$raw_data = $response->json();
|
|
||||||
|
|
||||||
// Attempt to create the cache dir if it doesn't exist
|
$zippered_data = $this->zipper_lists($api_data);
|
||||||
if ( ! is_dir($this->config->data_cache_path))
|
$transformer = new Hummingbird\MangaListTransformer();
|
||||||
{
|
$transformed_data = $transformer->transform_collection($zippered_data);
|
||||||
mkdir($this->config->data_cache_path);
|
file_put_contents($transformed_cache_file, json_encode($transformed_data));
|
||||||
}
|
return $transformed_data;
|
||||||
|
|
||||||
// Cache data in case of downtime
|
|
||||||
file_put_contents($cache_file, json_encode($raw_data));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Bail out early if there isn't any manga data
|
/**
|
||||||
if ( ! array_key_exists('manga', $raw_data)) return [];
|
* Map transformed anime data to be organized by reading status
|
||||||
|
*
|
||||||
$data = [
|
* @param array $data
|
||||||
'Reading' => [],
|
* @return array
|
||||||
'Plan to Read' => [],
|
*/
|
||||||
'On Hold' => [],
|
private function map_by_status($data)
|
||||||
'Dropped' => [],
|
{
|
||||||
'Completed' => [],
|
$output = [
|
||||||
|
self::READING => [],
|
||||||
|
self::PLAN_TO_READ => [],
|
||||||
|
self::ON_HOLD => [],
|
||||||
|
self::DROPPED => [],
|
||||||
|
self::COMPLETED => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
// Massage the two lists into one
|
foreach($data as &$entry)
|
||||||
$manga_data = $this->zipper_lists($raw_data);
|
|
||||||
|
|
||||||
// Filter data by status
|
|
||||||
foreach($manga_data as &$entry)
|
|
||||||
{
|
{
|
||||||
// Cache poster images
|
$entry['manga']['image'] = $this->get_cached_image($entry['manga']['image'], $entry['manga']['slug'], 'manga');
|
||||||
$entry['manga']['poster_image'] = $this->get_cached_image($entry['manga']['poster_image'], $entry['manga']['id'], 'manga');
|
$key = $this->const_map[$entry['reading_status']];
|
||||||
|
$output[$key][] = $entry;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (array_key_exists($status, $data)) ? $data[$status] : $data;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -189,10 +187,10 @@ class Manga extends API {
|
|||||||
|
|
||||||
foreach($array as $key => $item)
|
foreach($array as $key => $item)
|
||||||
{
|
{
|
||||||
$sort[$key] = $item['manga']['romaji_title'];
|
$sort[$key] = $item['manga']['title'];
|
||||||
}
|
}
|
||||||
|
|
||||||
array_multisort($sort, SORT_ASC, $array);
|
array_multisort($sort, SORT_ASC, $array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of MangaModel.php
|
// End of MangaModel.php
|
21
src/Aviat/Ion/ArrayWrapper.php
Normal file
21
src/Aviat/Ion/ArrayWrapper.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aviat\Ion;
|
||||||
|
|
||||||
|
use Aviat\Ion\Type\ArrayType;
|
||||||
|
|
||||||
|
trait ArrayWrapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for wrapping an array
|
||||||
|
* with the array type class
|
||||||
|
*
|
||||||
|
* @param array $arr
|
||||||
|
* @return ArrayType
|
||||||
|
*/
|
||||||
|
public function arr(array $arr)
|
||||||
|
{
|
||||||
|
return new ArrayType($arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End of ArrayWrapper.php
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Aviat\Ion;
|
namespace Aviat\Ion;
|
||||||
|
|
||||||
use Stringy\Stringy as S;
|
use Aviat\Ion\Type\StringType;
|
||||||
|
|
||||||
trait StringWrapper {
|
trait StringWrapper {
|
||||||
|
|
||||||
@ -10,11 +10,11 @@ trait StringWrapper {
|
|||||||
* Wrap the String in the Stringy class
|
* Wrap the String in the Stringy class
|
||||||
*
|
*
|
||||||
* @param string $str
|
* @param string $str
|
||||||
* @return Stringy\Stringy
|
* @return StringType
|
||||||
*/
|
*/
|
||||||
public function string($str)
|
public function string($str)
|
||||||
{
|
{
|
||||||
return S::create($str);
|
return StringType::create($str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// End of StringWrapper.php
|
// End of StringWrapper.php
|
142
src/Aviat/Ion/Type/ArrayType.php
Normal file
142
src/Aviat/Ion/Type/ArrayType.php
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aviat\Ion\Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for native array methods for convenience
|
||||||
|
*/
|
||||||
|
class ArrayType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current array
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $arr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map generated methods to their native implementations
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $native_methods = [
|
||||||
|
'chunk' => '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
|
10
src/Aviat/Ion/Type/StringType.php
Normal file
10
src/Aviat/Ion/Type/StringType.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Aviat\Ion\Type;
|
||||||
|
|
||||||
|
use Stringy\Stringy;
|
||||||
|
|
||||||
|
class StringType extends Stringy {
|
||||||
|
|
||||||
|
}
|
||||||
|
// End of StringType.php
|
@ -2,19 +2,27 @@
|
|||||||
|
|
||||||
namespace Aviat\Ion\View;
|
namespace Aviat\Ion\View;
|
||||||
|
|
||||||
use Aura\Html\HelperLocatorFactory;
|
|
||||||
|
|
||||||
use Aviat\Ion\View\HttpView;
|
use Aviat\Ion\View\HttpView;
|
||||||
use Aviat\Ion\Di\ContainerInterface;
|
use Aviat\Ion\Di\ContainerInterface;
|
||||||
|
|
||||||
class HtmlView extends HttpView {
|
class HtmlView extends HttpView {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML generator/escaper helper
|
||||||
|
*
|
||||||
|
* @var Aura\Html\HelperLocator
|
||||||
|
*/
|
||||||
protected $helper;
|
protected $helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Html View
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
*/
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->helper = (new HelperLocatorFactory)->newInstance();
|
$this->helper = $container->get('html-helper');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
54
tests/Ion/Type/ArrayTypeTest.php
Normal file
54
tests/Ion/Type/ArrayTypeTest.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ArrayTypeTest extends AnimeClient_TestCase {
|
||||||
|
use Aviat\Ion\ArrayWrapper;
|
||||||
|
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMerge()
|
||||||
|
{
|
||||||
|
$obj = $this->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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user