Skip to content
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
logs
!logs/.keep
vendor

composer.lock
12 changes: 12 additions & 0 deletions App/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ class Config
*/
const DB_PASSWORD = 'your-database-password';

/**
* Default controller value
* @var string
*/
const DEFAULT_CONTROLLER = 'Home';

/**
* Default action value
* @var string
*/
const DEFAULT_ACTION = 'index';

/**
* Show or hide error messages on screen
* @var boolean
Expand Down
30 changes: 28 additions & 2 deletions App/Controllers/Home.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,34 @@ class Home extends \Core\Controller
*
* @return void
*/
public function indexAction()
public function indexAction($params)
{
View::renderTemplate('Home/index.html');
$mainArgs = [
'title' => 'Fixed navbar',
'description' => 'Fixed navbar: Bootstrap 4 example template',
'author' => 'Twitter',
'menu' => [
[
'title' => 'Home',
'class' => 'active',
'href' => '#',
],
[
'title' => 'Link',
'class' => null,
'href' => '#',
],
[
'title' => 'Disabled',
'class' => 'disabled',
'href' => '#',
],
],
];
$bootstrapArgs = [
'navbar' => View::render('navbar.phtml', $mainArgs),
'main' => View::render('Home/index.phtml', $mainArgs),
];
echo View::render('bootstrap.phtml', $mainArgs + $bootstrapArgs);
}
}
6 changes: 6 additions & 0 deletions App/Views/Home/index.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div class="jumbotron">
<h1>Fixed navbar</h1>
<h4>Bootstrap 4 example template</h4>
<p class="lead">This example is a quick exercise to illustrate how fixed to top navbar works. As you scroll, it will remain fixed to the top of your browser's viewport.</p>
<a class="btn btn-lg btn-primary" href="https://getbootstrap.com/docs/4.0/components/navbar/" role="button">View navbar docs &raquo;</a>
</div>
36 changes: 36 additions & 0 deletions App/Views/bootstrap.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="<?= $description ?>">
<meta name="author" content="<?= $author ?>">
<link rel="icon" href="https://getbootstrap.com/docs/4.0/assets/img/favicons/favicon.ico">

<title><?= $title ?></title>

<link rel="canonical" href="https://getbootstrap.com/docs/4.0/examples/navbar-fixed/">

<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

<!-- Custom styles for this template -->
<link href="https://getbootstrap.com/docs/4.0/examples/navbar-fixed/navbar-top-fixed.css" rel="stylesheet">
</head>

<body>

<?= $navbar ?>

<main role="main" class="container">
<?= $main ?>
</main>

<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions App/Views/navbar.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<a class="navbar-brand" href="./" title="<?= $description ?>"><?= $title ?></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<?php foreach ($menu as $m) { ?>
<li class="nav-item<?= ' ' . $m['class'] ?>">
<a class="nav-link" href="<?= $m['href'] ?>"><?= $m['title'] ?>
<?php if ($m['class'] == 'active') { ?>
<span class="sr-only">(current)</span>
<?php } ?></a>
</li>
<?php } ?>
</ul>
<form class="form-inline mt-2 mt-md-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
60 changes: 59 additions & 1 deletion Core/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
*/
class Router
{

/**
* Request URI index constants
*/
const CONTROLLER_URI_INDEX = 1;
const ACTION_URI_INDEX = 2;
const ID_URI_INDEX = 3;

/**
* Associative array of routes (the routing table)
* @var array
Expand Down Expand Up @@ -131,6 +137,58 @@ public function dispatch($url)
}
}

/**
* Dispatch the request URI, creating the controller object
* and running the action method with id and other params.
* An automatic routing without using routing table
*
* Request URI format:
* [/controller[/action[/id]]][?param1=value1[&param2=value2...]]
*
* @param string|null $requestUri The request URI
*
* @return void
*/
public function dispatchRequest($requestUri = null)
{
// Parse request URI
$requestUri = $requestUri ?? $_SERVER['REQUEST_URI'];
[$request, $query] = strpos($requestUri, '?') === false ? [$requestUri, ''] : explode('?', $requestUri);
$request = strpos($request, '/') === false ? [] : explode('/', $request);
$queryParams = [];
parse_str($query, $queryParams);

// Dispatch request
$controller = !empty($request[self::CONTROLLER_URI_INDEX]) ? $request[self::CONTROLLER_URI_INDEX] : \App\Config::DEFAULT_CONTROLLER;
$controller = $this->convertToStudlyCaps($controller);
$this->params['controller'] = $controller;
$controller = $this->getNamespace() . $controller;
$action = !empty($request[self::ACTION_URI_INDEX]) ? $request[self::ACTION_URI_INDEX] : \App\Config::DEFAULT_ACTION;
$action = $this->convertToCamelCase($action);
$this->params['action'] = $action;
if ($id = $request[self::ID_URI_INDEX] ?? null) {
if (array_key_exists('id', $queryParams)) {
$queryParams['id'] = $id;
} else {
$queryParams = ['id' => $id] + $queryParams;
}
}
$this->params['id'] = $id;
$this->params['queryParams'] = $queryParams;

if (class_exists($controller)) {
$controller_object = new $controller($this->params);

if (preg_match('/action$/i', $action) == 0) {
$controller_object->$action($queryParams);
} else {
throw new \Exception("Method $action in controller $controller cannot be called directly - remove the Action suffix to call this method");
}
} else {
throw new \Exception("Controller class $controller not found");
}
}

/**
* Convert the string with hyphens to StudlyCaps,
* e.g. post-authors => PostAuthors
Expand Down
10 changes: 7 additions & 3 deletions Core/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class View
* @param string $view The view file
* @param array $args Associative array of data to display in the view (optional)
*
* @return void
* @return string
*/
public static function render($view, $args = [])
{
Expand All @@ -25,7 +25,11 @@ public static function render($view, $args = [])
$file = dirname(__DIR__) . "/App/Views/$view"; // relative to Core directory

if (is_readable($file)) {
ob_start();
require $file;
$ob_contents = ob_get_contents();
ob_end_clean();
return $ob_contents;
} else {
throw new \Exception("$file not found");
}
Expand All @@ -37,7 +41,7 @@ public static function render($view, $args = [])
* @param string $template The template file
* @param array $args Associative array of data to display in the view (optional)
*
* @return void
* @return string
*/
public static function renderTemplate($template, $args = [])
{
Expand All @@ -48,6 +52,6 @@ public static function renderTemplate($template, $args = [])
$twig = new \Twig\Environment($loader);
}

echo $twig->render($template, $args);
return $twig->render($template, $args);
}
}
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ You can also specify a namespace for the controller:
$router->add('admin/{controller}/{action}', ['namespace' => 'Admin']);
```

Another way - an automatic routing without using routing table:
```php
$router->dispatchRequest($_SERVER['REQUEST_URI']); die;
```

## Controllers

Controllers respond to user actions (clicking on a link, submitting a form etc.). Controllers are classes that extend the [Core\Controller](Core/Controller.php) class.
Expand Down Expand Up @@ -90,7 +95,7 @@ protected function after()
Views are used to display information (normally HTML). View files go in the `App/Views` folder. Views can be in one of two formats: standard PHP, but with just enough PHP to show the data. No database access or anything like that should occur in a view file. You can render a standard PHP view in a controller, optionally passing in variables, like this:

```php
View::render('Home/index.php', [
echo View::render('Home/index.php', [
'name' => 'Dave',
'colours' => ['red', 'green', 'blue']
]);
Expand All @@ -99,7 +104,7 @@ View::render('Home/index.php', [
The second format uses the [Twig](http://twig.sensiolabs.org/) templating engine. Using Twig allows you to have simpler, safer templates that can take advantage of things like [template inheritance](http://twig.sensiolabs.org/doc/templates.html#template-inheritance). You can render a Twig template like this:

```php
View::renderTemplate('Home/index.html', [
echo View::renderTemplate('Home/index.html', [
'name' => 'Dave',
'colours' => ['red', 'green', 'blue']
]);
Expand Down
7 changes: 6 additions & 1 deletion public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* PHP version 7.0
*/

if (!ob_start('ob_gzhandler')) ob_start();

/**
* Composer
*/
Expand All @@ -25,8 +27,11 @@
*/
$router = new Core\Router();

// Uncomment the next line for an automatic routing without using routing table
//$router->dispatchRequest($_SERVER['REQUEST_URI']); die;

// Add the routes
$router->add('', ['controller' => 'Home', 'action' => 'index']);
$router->add('{controller}/{action}');

$router->dispatch($_SERVER['QUERY_STRING']);
$router->dispatch($_SERVER['QUERY_STRING'] ?? '');
7 changes: 0 additions & 7 deletions vendor/autoload.php

This file was deleted.

Loading