From c788cf5d87fd209eeaa759a3c13ce01be2c4996d Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Wed, 16 Sep 2015 12:25:35 -0400 Subject: [PATCH] Start of refactoring routing to be more convention based --- README.md | 5 +- app/config/config.php | 5 ++ app/config/routes.php | 40 +++++------ app/views/header.php | 4 +- index.php | 43 ++++------- src/Aviat/AnimeClient/Controller.php | 18 ----- src/Aviat/AnimeClient/Controller/Anime.php | 27 ++++++- .../AnimeClient/Controller/Collection.php | 14 +++- src/Aviat/AnimeClient/Controller/Manga.php | 16 ++++- src/Aviat/AnimeClient/Model.php | 2 + src/Aviat/AnimeClient/Model/API.php | 2 + .../AnimeClient/Model/AnimeCollection.php | 2 + src/Aviat/AnimeClient/Model/DB.php | 2 + src/Aviat/AnimeClient/Model/Stats.php | 2 +- src/Aviat/AnimeClient/Router.php | 39 +++++++--- src/Aviat/Ion/Base/Container.php | 4 ++ src/Aviat/Ion/Base/Page.php | 72 ------------------- tests/AnimeClient/RouterTest.php | 60 ++++++++++++++++ 18 files changed, 191 insertions(+), 166 deletions(-) delete mode 100644 src/Aviat/Ion/Base/Page.php diff --git a/README.md b/README.md index 4f8c5677..28053f2a 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,8 @@ A self-hosted client that allows custom formatting of data from the hummingbird ### Installation 1. Install dependencies via composer: `composer install` -2. Change the `WHOSE` constant declaration in `index.php` to your name -3. Configure settings in `app/config/config.php` and `app/config/routing.php` to your liking -4. Create the following directories if they don't exist, and make sure they are world writable +2. Configure settings in `app/config/config.php` and `app/config/routing.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 * public/images/anime diff --git a/app/config/config.php b/app/config/config.php index 198199fa..7e59cf92 100644 --- a/app/config/config.php +++ b/app/config/config.php @@ -5,6 +5,11 @@ $config = [ // ---------------------------------------------------------------------------- 'hummingbird_username' => 'timw4mail', + // ---------------------------------------------------------------------------- + // Whose list is it? + // ---------------------------------------------------------------------------- + 'whose_list' => 'Tim', + // ---------------------------------------------------------------------------- // General config // ---------------------------------------------------------------------------- diff --git a/app/config/routes.php b/app/config/routes.php index 92e46c47..154f0c4d 100644 --- a/app/config/routes.php +++ b/app/config/routes.php @@ -1,6 +1,14 @@ [ + 'default_namespace' => '\\Aviat\\AnimeClient\\Controller', + 'default_controller' => '\\Aviat\\AnimeClient\\Controller\\Anime', + 'default_method' => 'index' + ], + 'configuration' => [ + + ], // Routes on all controllers 'common' => [ 'update' => [ @@ -55,10 +63,6 @@ return [ 'view' => '[a-z_]+' ] ], - ], - // Routes on stats controller - 'stats' => [ - ], // Routes on anime controller 'anime' => [ @@ -79,7 +83,6 @@ return [ 'action' => ['anime_list'], 'params' => [ 'type' => 'all', - 'title' => WHOSE . " Anime List · All" ], 'tokens' => [ 'view' => '[a-z_]+' @@ -89,8 +92,7 @@ return [ 'path' => '/anime/watching{/view}', 'action' => ['anime_list'], 'params' => [ - 'type' => 'currently-watching', - 'title' => WHOSE . " Anime List · Watching" + 'type' => 'watching', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -100,8 +102,7 @@ return [ 'path' => '/anime/plan_to_watch{/view}', 'action' => ['anime_list'], 'params' => [ - 'type' => 'plan-to-watch', - 'title' => WHOSE . " Anime List · Plan to Watch" + 'type' => 'plan_to_watch', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -111,8 +112,7 @@ return [ 'path' => '/anime/on_hold{/view}', 'action' => ['anime_list'], 'params' => [ - 'type' => 'on-hold', - 'title' => WHOSE . " Anime List · On Hold" + 'type' => 'on_hold', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -123,7 +123,6 @@ return [ 'action' => ['anime_list'], 'params' => [ 'type' => 'dropped', - 'title' => WHOSE . " Anime List · Dropped" ], 'tokens' => [ 'view' => '[a-z_]+' @@ -134,7 +133,6 @@ return [ 'action' => ['anime_list'], 'params' => [ 'type' => 'completed', - 'title' => WHOSE . " Anime List · Completed" ], 'tokens' => [ 'view' => '[a-z_]+' @@ -156,7 +154,6 @@ return [ 'action' => ['manga_list'], 'params' => [ 'type' => 'all', - 'title' => WHOSE . " Manga List · All" ], 'tokens' => [ 'view' => '[a-z_]+' @@ -166,8 +163,7 @@ return [ 'path' => '/manga/reading{/view}', 'action' => ['manga_list'], 'params' => [ - 'type' => 'Reading', - 'title' => WHOSE . " Manga List · Reading" + 'type' => 'reading', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -177,8 +173,7 @@ return [ 'path' => '/manga/plan_to_read{/view}', 'action' => ['manga_list'], 'params' => [ - 'type' => 'Plan to Read', - 'title' => WHOSE . " Manga List · Plan to Read" + 'type' => 'plan_to_read', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -188,8 +183,7 @@ return [ 'path' => '/manga/on_hold{/view}', 'action' => ['manga_list'], 'params' => [ - 'type' => 'On Hold', - 'title' => WHOSE . " Manga List · On Hold" + 'type' => 'on_hold', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -199,8 +193,7 @@ return [ 'path' => '/manga/dropped{/view}', 'action' => ['manga_list'], 'params' => [ - 'type' => 'Dropped', - 'title' => WHOSE . " Manga List · Dropped" + 'type' => 'dropped', ], 'tokens' => [ 'view' => '[a-z_]+' @@ -210,8 +203,7 @@ return [ 'path' => '/manga/completed{/view}', 'action' => ['manga_list'], 'params' => [ - 'type' => 'Completed', - 'title' => WHOSE . " Manga List · Completed" + 'type' => 'completed', ], 'tokens' => [ 'view' => '[a-z_]+' diff --git a/app/views/header.php b/app/views/header.php index e1209b97..b9161ec5 100644 --- a/app/views/header.php +++ b/app/views/header.php @@ -13,14 +13,14 @@

- + whose_list ?>'s [ List] [">Logout] - ["> Login] + [">whose_list ?>'s Login]

diff --git a/index.php b/index.php index 243a05e1..5438dbd7 100644 --- a/index.php +++ b/index.php @@ -3,20 +3,7 @@ * Here begins everything! */ -// ----------------------------------------------------------------------------- -// ! Start config -// ----------------------------------------------------------------------------- - -/** - * Well, whose list is it? - */ -define('WHOSE', "Tim's"); - -// ----------------------------------------------------------------------------- -// ! End config -// ----------------------------------------------------------------------------- - -\session_start(); +session_start(); // Work around the silly timezone error $timezone = ini_get('date.timezone'); @@ -43,29 +30,25 @@ function _dir() return implode(DIRECTORY_SEPARATOR, func_get_args()); } +// Set up composer autoloader +require _dir(ROOT_DIR, '/vendor/autoload.php'); + /** * Set up autoloaders * * @codeCoverageIgnore * @return void */ -function _setup_autoloaders() -{ - require _dir(ROOT_DIR, '/vendor/autoload.php'); - spl_autoload_register(function ($class) { - $class_parts = explode('\\', $class); - $ns_path = SRC_DIR . '/' . implode('/', $class_parts) . ".php"; +spl_autoload_register(function ($class) { + $class_parts = explode('\\', $class); + $ns_path = SRC_DIR . '/' . implode('/', $class_parts) . ".php"; - if (file_exists($ns_path)) - { - require_once($ns_path); - return; - } - }); -} - -// Setup autoloaders -_setup_autoloaders(); + if (file_exists($ns_path)) + { + require_once($ns_path); + return; + } +}); // Do dependency injection, and go! require _dir(APP_DIR, 'bootstrap.php'); diff --git a/src/Aviat/AnimeClient/Controller.php b/src/Aviat/AnimeClient/Controller.php index 59a35319..c6e75d9e 100644 --- a/src/Aviat/AnimeClient/Controller.php +++ b/src/Aviat/AnimeClient/Controller.php @@ -53,7 +53,6 @@ class Controller { * Constructor * * @param Container $container - * @param array $web */ public function __construct(Container $container) { @@ -150,23 +149,6 @@ class Controller { $this->response->content->set($buffer); } - /** - * Output json with the proper content type - * - * @param mixed $data - * @return void - */ - public function outputJSON($data) - { - if ( ! is_string($data)) - { - $data = json_encode($data); - } - - $this->response->content->setType('application/json'); - $this->response->content->set($data); - } - /** * Redirect to the selected page * diff --git a/src/Aviat/AnimeClient/Controller/Anime.php b/src/Aviat/AnimeClient/Controller/Anime.php index 2f81cdf1..7befede0 100644 --- a/src/Aviat/AnimeClient/Controller/Anime.php +++ b/src/Aviat/AnimeClient/Controller/Anime.php @@ -50,6 +50,8 @@ class Anime extends BaseController { /** * Constructor + * + * @param Container $container */ public function __construct(Container $container) { @@ -89,17 +91,38 @@ class Anime extends BaseController { * * @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, $title, $view) + public function anime_list($type, $view) { + $type_title_map = [ + 'all' => 'All', + 'watching' => 'Currently Watching', + 'plan_to_watch' => 'Plan to Watch', + 'on_hold' => 'On Hold', + 'dropped' => 'Dropped', + 'completed' => 'Completed' + ]; + + $model_map = [ + 'watching' => 'currently-watching', + 'plan_to_watch' => 'plan-to-watch', + 'on_hold' => 'on-hold', + 'all' => 'all', + 'dropped' => 'dropped', + 'completed' => 'completed' + ]; + + $title = $this->config->whose_list . "'s Anime List · {$type_title_map[$type]}"; + $view_map = [ '' => 'cover', 'list' => 'list' ]; $data = ($type != 'all') - ? $this->model->get_list($type) + ? $this->model->get_list($model_map[$type]) : $this->model->get_all_lists(); $this->outputHTML('anime/' . $view_map[$view], [ diff --git a/src/Aviat/AnimeClient/Controller/Collection.php b/src/Aviat/AnimeClient/Controller/Collection.php index d280ea5d..0e7b5c34 100644 --- a/src/Aviat/AnimeClient/Controller/Collection.php +++ b/src/Aviat/AnimeClient/Controller/Collection.php @@ -8,6 +8,7 @@ namespace Aviat\AnimeClient\Controller; use Aviat\AnimeClient\Container; use Aviat\AnimeClient\Controller as BaseController; use Aviat\AnimeClient\Config; +use Aviat\AnimeClient\UrlGenerator; use Aviat\AnimeClient\Model\Anime as AnimeModel; use Aviat\AnimeClient\Model\AnimeCollection as AnimeCollectionModel; @@ -28,6 +29,12 @@ class Collection extends BaseController { */ protected $base_data; + /** + * Url Generator class + * @var UrlGenerator + */ + protected $urlGenerator; + /** * Route mapping for main navigation * @var array $nav_routes @@ -56,6 +63,7 @@ class Collection extends BaseController { unset($this->nav_routes['Collection']); } + $this->urlGenerator = $container->get('url-generator'); $this->collection_model = new AnimeCollectionModel($container); $this->base_data = array_merge($this->base_data, [ 'message' => '', @@ -93,7 +101,7 @@ class Collection extends BaseController { $data = $this->collection_model->get_collection(); $this->outputHTML('collection/' . $view_map[$view], [ - 'title' => WHOSE . " Anime Collection", + 'title' => $this->config->whose_list . "'s Anime Collection", 'sections' => $data, 'genres' => $this->collection_model->get_genre_list() ]); @@ -111,8 +119,8 @@ class Collection extends BaseController { $this->outputHTML('collection/'. strtolower($action), [ 'action' => $action, - 'action_url' => $this->config->full_url("collection/" . strtolower($action)), - 'title' => WHOSE . " Anime Collection · {$action}", + 'action_url' => $this->urlGenerator->full_url("collection/" . strtolower($action)), + 'title' => $this->config->whose_list . " Anime Collection · {$action}", 'media_items' => $this->collection_model->get_media_type_list(), 'item' => ($action === "Edit") ? $this->collection_model->get($id) : [] ]); diff --git a/src/Aviat/AnimeClient/Controller/Manga.php b/src/Aviat/AnimeClient/Controller/Manga.php index 9b58d705..1a2f7218 100644 --- a/src/Aviat/AnimeClient/Controller/Manga.php +++ b/src/Aviat/AnimeClient/Controller/Manga.php @@ -72,19 +72,29 @@ class Manga extends Controller { * Get a section of the manga list * * @param string $status - * @param string $title * @param string $view * @return void */ - public function manga_list($status, $title, $view) + public function manga_list($status, $view) { + $map = [ + 'all' => 'All', + 'plan_to_read' => 'Plan to Read', + 'reading' => 'Reading', + 'completed' => 'Completed', + 'dropped' => 'Dropped', + 'on_hold' => 'On Hold' + ]; + + $title = $this->config->whose_list . "' Manga List · {$map[$status]}"; + $view_map = [ '' => 'cover', 'list' => 'list' ]; $data = ($status !== 'all') - ? [$status => $this->model->get_list($status)] + ? [$map[$status] => $this->model->get_list($map[$status])] : $this->model->get_all_lists(); $this->outputHTML('manga/' . $view_map[$view], [ diff --git a/src/Aviat/AnimeClient/Model.php b/src/Aviat/AnimeClient/Model.php index 8e70f8bf..69c4d5b0 100644 --- a/src/Aviat/AnimeClient/Model.php +++ b/src/Aviat/AnimeClient/Model.php @@ -25,6 +25,8 @@ class Model { /** * Constructor + * + * @param Container $container */ public function __construct(Container $container) { diff --git a/src/Aviat/AnimeClient/Model/API.php b/src/Aviat/AnimeClient/Model/API.php index 1a0501ff..b774eda1 100644 --- a/src/Aviat/AnimeClient/Model/API.php +++ b/src/Aviat/AnimeClient/Model/API.php @@ -33,6 +33,8 @@ class API extends \Aviat\AnimeClient\Model { /** * Constructor + * + * @param Container $container */ public function __construct(Container $container) { diff --git a/src/Aviat/AnimeClient/Model/AnimeCollection.php b/src/Aviat/AnimeClient/Model/AnimeCollection.php index 20c2fc90..78c89ac7 100644 --- a/src/Aviat/AnimeClient/Model/AnimeCollection.php +++ b/src/Aviat/AnimeClient/Model/AnimeCollection.php @@ -28,6 +28,8 @@ class AnimeCollection extends DB { /** * Constructor + * + * @param Container $container */ public function __construct(Container $container) { diff --git a/src/Aviat/AnimeClient/Model/DB.php b/src/Aviat/AnimeClient/Model/DB.php index 0cbda864..2f813aee 100644 --- a/src/Aviat/AnimeClient/Model/DB.php +++ b/src/Aviat/AnimeClient/Model/DB.php @@ -24,6 +24,8 @@ class DB extends \Aviat\AnimeClient\Model { /** * Constructor + * + * @param Container $container */ public function __construct(Container $container) { diff --git a/src/Aviat/AnimeClient/Model/Stats.php b/src/Aviat/AnimeClient/Model/Stats.php index 32533fb1..993ea1be 100644 --- a/src/Aviat/AnimeClient/Model/Stats.php +++ b/src/Aviat/AnimeClient/Model/Stats.php @@ -15,7 +15,7 @@ class Stats extends DB { /** * Constructor * - * @param Config $config + * @param Container $container */ public function __construct(Container $container) { diff --git a/src/Aviat/AnimeClient/Router.php b/src/Aviat/AnimeClient/Router.php index 8b0b6b7e..f496b59f 100644 --- a/src/Aviat/AnimeClient/Router.php +++ b/src/Aviat/AnimeClient/Router.php @@ -24,12 +24,6 @@ class Router extends RoutingBase { */ protected $request; - /** - * Array containing request and response objects - * @var array $web - */ - protected $web; - /** * Routes added to router * @var array $output_routes @@ -46,7 +40,6 @@ class Router extends RoutingBase { parent::__construct($container); $this->router = $container->get('aura-router'); $this->request = $container->get('request'); - $this->web = [$this->request, $container->get('response')]; $this->output_routes = $this->_setup_routes(); } @@ -154,6 +147,35 @@ class Router extends RoutingBase { return $controller; } + /** + * Get the list of controllers in the default namespace + * + * @return array + */ + public function get_controller_list() + { + $convention_routing = $this->routes['convention']; + $default_namespace = $convention_routing['default_namespace']; + $path = str_replace('\\', '/', $default_namespace); + $path = trim($path, '/'); + $actual_path = \_dir(SRC_DIR, $path); + + $class_files = glob("{$actual_path}/*.php"); + + $controllers = []; + + foreach($class_files as $file) + { + $raw_class_name = basename(str_replace(".php", "", $file)); + $path = strtolower(basename($raw_class_name)); + $class_name = trim($default_namespace . '\\' . $raw_class_name, '\\'); + + $controllers[$path] = $class_name; + } + + return $controllers; + } + /** * Select controller based on the current url, and apply its relevent routes * @@ -176,7 +198,8 @@ class Router extends RoutingBase { $path = $route['path']; unset($route['path']); - $controller_class = '\\Aviat\\AnimeClient\\Controller\\' . ucfirst($route_type); + $controller_map = $this->get_controller_list(); + $controller_class = $controller_map[$route_type]; // Prepend the controller to the route parameters array_unshift($route['action'], $controller_class); diff --git a/src/Aviat/Ion/Base/Container.php b/src/Aviat/Ion/Base/Container.php index dd9af5d5..cfb40db3 100644 --- a/src/Aviat/Ion/Base/Container.php +++ b/src/Aviat/Ion/Base/Container.php @@ -8,12 +8,16 @@ namespace Aviat\Ion\Base; class Container { /** + * Array with class instances + * * @var array */ protected $container = []; /** * Constructor + * + * @param array $values (optional) */ public function __construct(array $values = []) { diff --git a/src/Aviat/Ion/Base/Page.php b/src/Aviat/Ion/Base/Page.php deleted file mode 100644 index e054be94..00000000 --- a/src/Aviat/Ion/Base/Page.php +++ /dev/null @@ -1,72 +0,0 @@ -request = $request; - $this->response = $response; - } - - /** - * __destruct function. - */ - public function __destruct() - { - $this->output(); - } - - /** - * Output the response to the client - */ - protected function output() - { - // send status - @header($this->response->status->get(), true, $this->response->status->getCode()); - - // headers - foreach($this->response->headers->get() as $label => $value) - { - @header("{$label}: {$value}"); - } - - // cookies - foreach($this->response->cookies->get() as $name => $cookie) - { - @setcookie( - $name, - $cookie['value'], - $cookie['expire'], - $cookie['path'], - $cookie['domain'], - $cookie['secure'], - $cookie['httponly'] - ); - } - - // send the actual response - echo $this->response->content->get(); - } -} -// End of Page.php \ No newline at end of file diff --git a/tests/AnimeClient/RouterTest.php b/tests/AnimeClient/RouterTest.php index 8d66198c..1fe6c295 100644 --- a/tests/AnimeClient/RouterTest.php +++ b/tests/AnimeClient/RouterTest.php @@ -58,6 +58,9 @@ class RouterTest extends AnimeClient_TestCase { { $default_config = array( 'routes' => [ + 'convention' => [ + 'default_namespace' => '\\Aviat\\AnimeClient\\Controller' + ], 'common' => [ 'login_form' => [ 'path' => '/login', @@ -171,6 +174,9 @@ class RouterTest extends AnimeClient_TestCase { 'default_list' => 'manga' ], 'routes' => [ + 'convention' => [ + 'default_namespace' => '\\Aviat\\AnimeClient\\Controller' + ], 'common' => [ 'login_form' => [ 'path' => '/login', @@ -207,4 +213,58 @@ class RouterTest extends AnimeClient_TestCase { $this->assertEquals('//localhost/anime/watching', $this->urlGenerator->default_url('anime'), "Incorrect default url"); $this->assertEquals('', $this->urlGenerator->default_url('foo'), "Incorrect default url"); } + + public function dataGetControllerList() + { + return array( + 'controller_list_sanity_check' => [ + 'config' => [ + 'routing' => [ + 'anime_path' => 'anime', + 'manga_path' => 'manga', + 'default_anime_path' => "/anime/watching", + 'default_manga_path' => '/manga/all', + 'default_list' => 'manga' + ], + 'routes' => [ + 'convention' => [ + 'default_namespace' => '\\Aviat\\AnimeClient\\Controller' + ] + ] + ], + 'expected' => [ + 'anime' => 'Aviat\AnimeClient\Controller\Anime', + 'manga' => 'Aviat\AnimeClient\Controller\Manga', + 'collection' => 'Aviat\AnimeClient\Controller\Collection', + 'stats' => 'Aviat\AnimeClient\Controller\Stats' + ] + ], + 'empty_controller_list' => [ + 'config' => [ + 'routing' => [ + 'anime_path' => 'anime', + 'manga_path' => 'manga', + 'default_anime_path' => "/anime/watching", + 'default_manga_path' => '/manga/all', + 'default_list' => 'manga' + ], + 'routes' => [ + 'convention' => [ + 'default_namespace' => '\\Aviat\\Ion\\Controller' + ] + ] + ], + 'expected' => [] + ] + ); + } + + /** + * @dataProvider dataGetControllerList + */ + public function testGetControllerList($config, $expected) + { + $this->_set_up($config, '/', 'localhost'); + $this->assertEquals($expected, $this->router->get_controller_list()); + } } \ No newline at end of file