I´m making a "very" simple MVC framework in order to learn, however I have trouble getting other pages than the index page to show. In views folder I have 2 files one index.php and one register.php that I´m trying on.
I have tried various ways but can´t get my head around it. I know it is probably best to put different controller classes in different files and maybe a loader controller page but I´m a beginner with php so would like to make it as simple as possible for me...
Any help appriciated!
I have a index.php as a landing file in the root folder to bind everything together:
<?php
/* index.php
*
*/
require_once 'model/load.php';
require_once 'controller/main.php';
new mainController();
In the controller folder i have a file called main.php:
<?php
/* controller/main.php
*
*/
class mainController
{
public $load;
public function __construct()
{
$urlValues = $_SERVER['REQUEST_URI'];
$this->urlValues = $_GET;
//index page
if ($this->urlValues['controller'] == "") {
$indexPage = array("key" => "Hello");
$this->load = new load();
$this->load->view('index.php', $indexPage);
}
//register page
if ($this->urlValues['controller'] == "register.php") {
$registerPage = array("key" => "Register");
$this->load = new load();
$this->load->view('register.php', $registerPage);
}
}
}
And then I have a file called load.php in the model folder:
<?php
/* model/load.php
*
*/
class load
{
/* This function takes parameter
* $file_name and match with file in views.
*/
function view($file_name, $data = null)
{
if (is_readable('views/' . $file_name)) {
if (is_array($data)) {
extract($data);
}
require 'views/' . $file_name;
} else {
echo $this->file;
die ('404 Not Found');
}
}
}
In your mainController class you don't have property with the name urlValues, but you use it: $this->urlValues = $_GET;. And what is more you have local variable with the same name, that you don't use: $urlValues = $_SERVER['REQUEST_URI'];
And how you URL for register.php looks like?
Related
While upgrading to PHP7, I encountered these problem.
The issue is am trying to create codes that I can reuse like a mini-framework and the RUN function where the problem is used to load the relevant template and supplying the variables. It complains about
undefined index
of these 2 variables
$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];
and it also complained about this line
$page = $controller->$action();
which displayed
Fatal error: Uncaught Error: Method name must be a string in...
public function run() {
$routes = $this->routes->getRoutes();
$authentication = $this->routes->getAuthentication();
if (isset($routes[$this->route]['login']) && !$authentication->isLoggedIn()) {
header('location: /login/error');
}
else if (isset($routes[$this->route]['permissions']) && !$this->routes->checkPermission($routes[$this->route]['permissions'])) {
header('location: /login/permissionserror');
}
else {
$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];
$page = $controller->$action();
$title = $page['title'];
if (isset($page['variables'])) {
$output = $this->loadTemplate($page['template'], $page['variables']);
}
else {
$output = $this->loadTemplate($page['template']);
}
echo $this->loadTemplate('layout.html.php', ['loggedIn' => $authentication->isLoggedIn(),
'output' => $output,
'title' => $title
]);
}
This is the index.php
try {
include __DIR__ . '/../includes/autoload.php';
$route = ltrim(strtok($_SERVER['REQUEST_URI'], '?'), '/');
$entryPoint = new \Ninja\EntryPoint($route, $_SERVER['REQUEST_METHOD'], new \Ijdb\IjdbRoutes());
$entryPoint->run();
}
catch (PDOException $e) {
$title = 'An error has occurred';
$output = 'Database error: ' . $e->getMessage() . ' in ' .
$e->getFile() . ':' . $e->getLine();
include __DIR__ . '/../templates/layout.html.php';
}
The code is much, so, I can't display the whole code here since am using MVC pattern, but if there is anything you still want to know, I will gladly post it here
This code is runnable in php 7.2.7 (MAMP and LAMP), your way of dynamic function calling is invalid and your two variables are empty. This is not exact as yours but you can take logic form this demo.
Ok i am just providing a very simple example of reflection with mapping url to class along with functions. I make folder structure like below-
Here .htaccess is used to redirect all the url to index.php (if no file exists).
index.php include all code that could initialize code(for now only three files were there - uri.php, urlMapping.php and actions.php)
URI.php - have function that provide values like basepath, baseurl, uri
urlMappig.php - that allows you to provide which url hit which class along with method
actions.php will call dynamic class and function (reflection)
now look into code of index.php
<?php
include_once('URI.php');
include_once('urlMapping.php');
include_once('actions.php');
?>
aNow code insise uri.php file -
<?php
// all function should be accessible to all file loaded now
// return full url
function full_url (){
return (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
}
// returns current directory
function directory() {
$parts = explode("/", __DIR__);
return $parts[count($parts)-1];
}
// return base url
function base_url(){
$dir = directory();
return substr(full_url(), 0, strpos(full_url(), $dir)+strlen($dir));
}
// return uri
function uri() {
return substr(full_url(), strlen(base_url())+1);
}
?>
Now code in urlMapping.php
Note - file name and name of the class must be same as you map url in
this file so that you can make call to dynamic classes and function on actions.php. If don't this will not work
<?php
// this $urlMap will be accessed in actions.php
$urlMap = [
// here we use only uri so .. https://example.com/login hit LoginController class along with login function, this is just only the mapping
'login' => ['class'=>'LoginController',
'function'=>'login'],
];
?>
Now actions.php code
<?php
// if call is not like example.com/ means no uri is there
if(uri()!='')
{
// if your uri exists in route collection or urlmapping collection then make call to your dynamic class and methods
if(array_key_exists(uri(), $urlMap))
{
// include the class file dynamically from controllers folder
include_once('controllers/'.$urlMap[uri()]['class'].'.php');
// making references for dynamic class
$controlerObject = new $urlMap[uri()]['class']();
// call function dynaically from the referece
$controlerObject->{$urlMap[uri()]['function']}();
}
else
{
// you can make 404 page not
echo 'No routing found';
}
}
// call for home page
else
{
include_once('index.html.php');
}
?>
Now controllres/LoginController.php class,
Note - As mentioned above file name and name of the class as in urlMapping.php
<?php
class LoginController{
public function login()
{
// .... something your code goes here
echo 'hello from the login function of login controller';
}
//...... other function you can define
}
?>
Before calling $page = $controller->$action(); check if controller and action are exists:
if (isset($routes[$this->route][$this->method]['controller'])
&& isset($routes[$this->route][$this->method]['action'])) {
$controller = $routes[$this->route][$this->method]['controller'];
$action = $routes[$this->route][$this->method]['action'];
$page = $controller->$action();
// ...
} else {
// return 404 Page not found
}
Im working on a website - just learning to improve my coding.
I have a routing system which works like this:
/config/routes.php:
$route->add('/' , function() {
require_once("/application/pages/index.php");
});
$route->add('/register', function() {
require_once("/application/pages/register.php");
});
$route->add('/login', function() {
require_once("/application/pages/login.php");
});
$route->add('/logout', function() {
require_once("/application/pages/logout.php");
});
$route->add('/panel', function() {
require_once('/application/pages/panel/index.php');
});
And in my index.php:
require_once('application/pages/header.php');
include('config/routes.php');
require_once('application/pages/footer.php');
Everything works fine but I need a different header.php and footer.php for when the user goes into the panel. file: /application/pages/panel/index.php
When I require_once a new header file in the panel/index.php then both the new and old header file is loaded. How can I unrequire the header and footer files in the /panel/index.php so I can require different ones? Any suggestions?
Note: Routing comes from an MVC design pattern, you should keep your controllers separate from your views.
Templates and Views could be kept separate, also. This meaning our directory set-up can look something like this:
- Templates
- header_one.php
- footer_one.php
- header_two.php
- footer_two.php
- Views
- index.php
- someOtherBody.php
Here is a simple, but unfinished (that is your challenge) example of an Object that could do what I am explaining:
class Page {
private $template_path = dirname(dirname(__FILE__)) . '/templates/';
private $view_path = dirname(dirname(__FILE__)) . '/views/';
protected $header;
protected $footer;
protected $body;
public function setHeader($file_name)
{
if(is_readable($this->template_path . $file_name))
{
$this->header = $this->template_path . $file_name;
return $this;
}
// add an exception
}
/* TODO: */
public function setFooter() {}
public function setBody() {}
/* Render page */
public function render()
{
$page = [$this->header,$this->body,$this->footer];
foreach($page as $file)
{
require_once($file);
}
}
}
The idea here is that we can set our page layout, using the above object, within the route method closure, then render / require all the files after the logic.
$route->add('/', function() {
$page = new Page();
$page->setHeader('header_one.php')
->setBody('index.php')
->setFooter('footer_one.php');
/* todo: add your logic here */
$page->render();
});
Each route can now have its own header, footer and body.
Hope this helped.
At your place, I will do something like that :
Use out buffer and check if the file is already required. I give you an quick example but adapt the code for you.
And check the function : http://php.net/manual/en/function.get-included-files.php
$route->add('/panel', function() {
include_once('YOUR_SPECIFIC_PATH/header.php');
require_once('/application/pages/panel/index.php');
include_once('YOUR_SPECIFIC_PATH_header/footer.php');
});
And :
ob_start();
include_once('config/routes.php');
$mainContent = ob_get_contents();
ob_end_clean();
include_once('application/pages/header.php');
echo $mainContent;
include_once('application/pages/footer.php');
I've not the time for help more sorry but I can explain later if you need
This solution requires you to have a header.php and footer.php in each folder where your sub-controllers (application/<module name>/index.php) are.
index.php only call your sub-controllers via routing:
// require not include, because "no routing" = "no web site" ;)
require_once('config/routes.php');
application/pages/index.php include appropriate header/footer w/ relative path
require_once('header.php');
// page code
require_once('footer.php');
application/register/index.php include appropriate header/footer w/ relative path
require_once('header.php');
// page code
require_once('footer.php');
etc
#KDOT , thanks you for your help but using your code I was getting an error that I could not fix:
Call to a member function setBody() on null
but thanks to your code, I managed to rewrite the class my way and now it works ;)
Thanks again #KDOT !
If anyone needs it:
class Page {
private $TEMPLATE_PATH = '/application/templates/';
private $VIEW_PATH = '/application/views/';
protected $header;
protected $footer;
protected $body;
public function __construct($header_file, $body_file, $footer_file) {
$this->header = $this->TEMPLATE_PATH . $header_file;
$this->body = $this->VIEW_PATH . $body_file;
$this->footer = $this->TEMPLATE_PATH . $footer_file;
}
public function render(){
$page = [$this->header, $this->body, $this->footer];
foreach($page as $file) {
require_once($file);
}
}
}
and:
$route->add('/', function() {
$page = new Page('header.php', 'home.php', 'footer.php');
$page->render();
});
this is my index.php source, when i run it instantiate the home.php object. So index will always by default display the contents of home.php which is exactly what i want. When i click on the Features hyperlink it directs me to the features.php page but no content which is understandable because the features object has not been instantiated hence no output. If i do instantiate the features object then i get the contents of features.php and home.php on the index.php page which i do not want. How do i approach this, i have tried instantiating the needed object with if($_SERVER['REQUEST_METHOD'] == $_GET logic but no success. To sum it up: when i click the Features link the Features object should be instantiated and the url should reflect it eg. test/features.php instead of test/index.php.
<?php
include_once 'pagefactory.php';
include_once 'home.php';
include_once 'features.php';
include_once 'contact.php';
/**
* Class to instantiate the needed page objects
*/
class Client {
private $page_factory;
function __construct($webpage) {
$this->page_factory = new PageFactory;
echo $this->page_factory->startFactory(new $webpage);
}
}
$worker = new Client('home');
?>
ive figured it out by doing the following:
<?php
include_once 'pagefactory.php';
function __autoload($class_name) {
include $class_name . '.php';
}
/**
* Class to instantiate the needed page objects
*/
class Client {
private $page_factory;
function __construct($webpage) {
$this->page_factory = new PageFactory;
echo $this->page_factory->startFactory(new $webpage);
}
}
if (isset($_GET['features'])) {
$new_page = 'features';
$worker = new Client($new_page);
}elseif (isset($_GET['contact'])) {
$new_page = 'contact';
$worker = new Client($new_page);
} else {
$new_page = 'home';
$worker = new Client($new_page);
}
?>
Ive also updated my HTML links to :
<li>Home</li>
<li>Features</li>
<li>Contact</li>
It worked perfect for me, any suggestions in any alternative methods? If so SHARE
I´m trying to pick up the correct path with parse_url and $_SERVER['REQUEST_URI'] in order to show the correct page, however the problem I´m having is that only my index.php page is showing.
The url to my index.php page should be localhost/mvc-framework which is all correct, but when I take:
localhost/mvc-framework/file.php it is also showing my index.php. Basically anything I type in after somefolder also show the index.php page...
I really hope someone can help me out as I´ve been struggling with this for almost 3 hole days.
I´m probably stupid:/
I have all files in "mvc-framework" and inside I have one index.php file that ties everything together and 3 folders: "controller", "model", "views".
In controller folder I have one file called main.php which is were I´m trying to pick up the url:
<?php
/* controller/main.php
*/
class mainController
{
public $load;
public $urlValues;
public function __construct()
{
$url = parse_url($_SERVER['REQUEST_URI']);
$url = explode('/', $url['path']);
$this->urlValues = array('controller' => $url[1]);
//index page
if ($this->urlValues['controller'] == "mvc-framework") {
$text = array("key" => "Hello");
$this->load = new load();
$this->load->view('index.php', $text);
}
//register page
elseif ($this->urlValues['controller'] == "mvc-framework/test.php") {
$text = array("key" => "Test");
$this->load = new load();
$this->load->view('test.php', $text);
} else {
echo 'error';
}
}
}
In the "model" folder I have one file called load.php which will take the url and point it to the the correct file in the views folder...
<?php
/* model/load.php
*/
class load
{
/* This function takes parameter
* $file_name and match with file in views.
*/
function view($file_name, $data = null)
{
if (is_readable('views/' . $file_name)) {
if (is_array($data)) {
extract($data);
}
require 'views/' . $file_name;
} else {
echo $this->file;
die ('404 Not Found');
}
}
}
In the views folder I have 2 files, one index.php and one test.php...
And here is my bootstrap/index.php file which is in the root folder and bind everything together...
<?php
/* index.php
*/
require_once 'model/load.php';
require_once 'controller/main.php';
new mainController();
try this
$url = explode('/', trim($url['path'], '/'));
and $this->urlValues['controller'] == 'test.php' for request localhost/mvc-framework/test.php
You always get the index.php beacuse of the .htaccess file, which always redirect the request to the frontend controller, index.php. I suppose...
I am writing a MVC Framework (for the purpose of learning and discovery as opposed to actually intending to use it) and I have came across a slight problem.
I have a config.php file:
$route['default'] = 'home';
$db['host'] = 'localhost';
$db['name'] = 'db-name';
$db['user'] = 'user-name';
$db['pass'] = 'user-pass';
$enc_key = 'enc_key'
I load these via a static method in my boot class:
public static function getConfig($type) {
/**
* static getConfig method gets configuration data from the config file
*
* #param string $type - variable to return from the config file.
* #return string|bool|array - the specified element from the config file, or FALSE on failure
*/
if (require_once \BASE . 'config.php') {
if (isset(${$type})) {
return ${$type};
} else {
throw new \Exception("Variable '{$type}' is undefined in " . \BASE . "config.php");
return FALSE;
}
} else {
throw new \Exception("Can not load config file at: " . \BASE . 'config.php');
return FALSE;
}
}
and then load the route like so:
public function routeURI($uri) {
...
$route = $this::getConfig('route');
...
}
which catches the exception:
"Variable 'route' is undefined in skeleton/config.php"
now, it works fine if I make the config.php file like so
$config['route']['default'] = 'home'
...
and change the two lines in the method like so:
if (isset($config[$type])) {
return $config[$type];
I have also tried using $$type instead of ${$type} with the same problem.
Is there something I am overlooking?
As written, this function can be called only once, because it uses require_once and on subsequent calls you won't bring in the local variables defined in config.php anymore. I suspect you are getting this error on your second call to getConfig().