Personal Website
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
6.9 KiB

<?php
namespace App\Classes;
use Klein\Klein;
use App\Classes\Db;
use App\Model\SectionModel;
use App\Model\PageModel;
class Router {
protected static $router;
protected static $routes = [];
public static function _init(): void {
self::$router = new Klein();
}
//-------------------------------------//
/**
* Load all routes into the Klein object
*
* @return void
*/
public static function fire(): void
{
$path = parse_url($_SERVER['REQUEST_URI'])['path'];
$check = str_replace('.', '', $path);
// If it's a dynamic file or the file doesn't exist, go through the router.
if ($path == $check || !file_exists(getcwd() . $path)) {
Db::load();
self::setDefaultLayout();
self::loadConfigRoutes();
self::loadDbRoutes();
// Process basic routes
foreach (self::$routes as $route) {
// ["/example/my-page", "ExampleController", "action", "" : ["view", "title", "description"]],
self::addRoute(['GET', 'POST'], $route);
}
self::createNavigation();
self::setHttpError();
self::$router->dispatch();
}
}
/**
* Add CRUD routes
* Example usage: Router::resource('/example', 'CrudController');
*
* @param string $route The URL location
* @param string $controller Controller to handle this route
*
* @return void
*/
public static function resource(string $route, string $controller): void
{
/*
* HTTP Verb Part (URL) Action (Method)
*
* GET /route indexAction
* GET /route/create createAction
* POST /route storeAction
* GET /route/{id} showAction
* GET /route/{id}/edit editAction
* PUT/PATCH /route/{id} updateAction
* DELETE /route/{id} destroyAction
*/
self::addRoute(['GET'], [$route, $controller, 'index']);
self::addRoute(['GET'], [$route . '/create', $controller, 'create']);
self::addRoute(['POST'], [$route, $controller, 'store']);
self::addRoute(['GET'], [$route . '/[i:id]', $controller, 'show']);
self::addRoute(['GET'], [$route . '/[i:id]/edit', $controller, 'edit']);
self::addRoute(['PUT', 'PATCH'], [$route . '/[i:id]', $controller, 'update']);
self::addRoute(['DELETE'], [$route . '/[i:id]', $controller, 'destroy']);
}
//-------------------------------------//
protected static function setDefaultLayout(): void
{
self::$router->respond(function ($request, $response, $service) {
$service->layout('../app/views/layouts/default.php');
});
}
protected static function loadConfigRoutes(): void
{
if (file_exists('../route.php')) {
self::$routes = require_once '../route.php';
}
}
/**
* Add all pages in the Db to self::$routes
*
* @return void
*/
protected static function loadDbRoutes(): void
{
// Load all sections from Db
$sections = SectionModel::selectAll('*', 'WHERE `active` = 1 ORDER BY `order` ASC');
// Return if no sections
if (!_exists($sections)) {
return;
}
// Load all pages from Db
$pages = PageModel::selectAll('DISTINCT page.*', '
LEFT JOIN page_has_content ON page_has_content.page_id = page.id
LEFT JOIN content ON content.id = page_has_content.content_id
WHERE page.active = 1 AND content.active = 1
ORDER BY page.order ASC;
');
// Return if no pages
if (!_exists($pages)) {
return;
}
// Select id column
$section = array_column($sections, 'section', 'id');
// Loop through all pages
foreach ($pages as $pageKey => $page) {
// Skip if section isn't created / active
if (!_exists($section, $page['section_id'])) { continue; }
// url = /section/page
$url = '/' . $section[$page['section_id']] . '/' . $page['page'];
// Add route
self::$routes[] = [$url, 'PageController', 'route', [$page['id']]];
}
// Cache sections and pages
Db::setSections($sections);
Db::setPages($pages);
}
protected static function addRoute(array $method = [], array $data = []): void
{
if (!_exists($method) || !_exists($data) || count($data) < 2) {
return;
}
$route = $data[0] ?? null;
$controller = $data[1] ?? null;
$action = $data[2] ?? null;
$parameters = $data[3] ?? [];
if (!$route || !$controller || !is_array($parameters)) {
return;
}
// Create Klein route
self::$router->respond($method, $route, function($request, $response, $service) use($controller, $action, $parameters) {
// Create new Controller object
$controller = '\App\Controllers\\' . $controller;
$controller = new $controller(self::$router);
// Complete action variable
if (!$action) {
$action = 'indexAction';
}
else {
$action .= 'Action';
}
// If method does not exist in object
if (!method_exists($controller, $action)) {
return $controller->throw404();
}
// If no valid permissions
if ($controller->getAdminSection() && !$controller->getLoggedIn()) {
return $controller->throw404();
}
// Get URL parameters
$namedParameters = array_filter($request->paramsNamed()->all(), 'is_string', ARRAY_FILTER_USE_KEY);
$namedParameters = array_values($namedParameters);
// Append URL parameters to the hard-coded parameters
array_push($parameters, ...$namedParameters);
// Call Controller action
return $controller->{$action}(...$parameters);
});
}
public static function createNavigation(): void
{
// Pull from cache
$sections = Db::getSections();
$pages = Db::getPages();
// [
// [
// 'section url',
// 'title',
// ['page url', 'title'],
// ['page url', 'titleOfPage2'],
// ],
// [],
// [],
// ]
$navigation = [];
// Generate sections
foreach ($sections as $section) {
// Skip hidden sections
if ($section['hide_navigation'] == '1') {
continue;
}
// Add URL, title to ID
$navigation[$section['id']] = [
$section['section'], $section['title']
];
}
// Generate pages
foreach ($pages as $page) {
// Skip hidden sections
if (!_exists($navigation, $page['section_id'])) {
continue;
}
// Skip hidden pages
if ($page['hide_navigation'] == '1') {
continue;
}
// Add [URL, title] to ID
$navigation[$page['section_id']][] = [
$page['page'], $page['title']
];
}
self::$router->service()->navigation = $navigation;
}
//-------------------------------------//
protected static function setHttpError(): void
{
self::$router->onHttpError(function($code) {
$service = self::$router->service();
switch ($code) {
case 404:
4 years ago
// @Todo: find out why this was needed and remove if possible
$service->escape = function (?string $string) {
return htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8');
};
self::$router->response()->sendHeaders(true, true);
$service->pageTitle = 'Error 404 (Not Found)';
$service->render('../app/views/errors/404.php');
break;
}
});
}
//-------------------------------------//
public static function getRoutes(): array
{
return self::$routes;
}
}
Router::_init();