I am trying to figure out the best approach when linking to static pages using a loosely followed MVC design pattern.
I begin by rewriting all requests to the index.php which handles all request and break them down the url into the controller, action and parameters. However if i don't want to follow this url structure and just want to visit a static page such as 'http://example.com/home/' without having to call some action how would i achieve this without getting a php error caused by my router/dispatcher trying to request a file that does not exist?
I thought about setting up some switch statement or a if statement as shown below that checks if the url is set to something then uses a custom defined controller and action, or i wasn't sure whether to take the static resources out of the MVC directory and have it seperate and link to it that way?
<?php
class Router
{
static public function parse($url, $request)
{
$url = trim($url);
if ($url == "/")
{
$request->controller = "tasks";
$request->action = "index";
$request->params = [];
}
else
{
$explode_url = explode('/', $url);
$explode_url = array_slice($explode_url, 2);
$request->controller = $explode_url[0];
$request->action = $explode_url[1];
$request->params = array_slice($explode_url, 2);
}
}
}
?>
This works, but i'd rather not have a huge router setup for many different static resources as it feels tacky and that i am just patching together code. Would putting static pages in its own directory outside of MVC and linking to them in the views be a valid option? i'm relatively new to MVC so any guidance would be great.
Your application shouldn't receive request it is not supposed to handle, you can solve this on a webserver level:
if you are using apache for example, you can setup in the .htaccess file that the request should be directed to your front controller (ex: index.php) only if the requested resource does not exist
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L]
Related
I am creating a web application in Codeignitor using Laragon as my local server. When I try to "redirect" to a Controller - I get "404 Page Not Found". If I redirect to View - it works. I can access Controllers with other methods such as "Form Open".
Here is my .htaccess file:
RewriteEngine on
RewriteCond $1 !^(index\.php|assets|images|js|css|uploads|favicon.png)
RewriteCond %(REQUEST_FILENAME) !-f
RewriteCond %(REQUEST_FILENAME) !-d
RewriteRule ^(.*)$ index.php/$1 [L]
This is my Controller - for a test I used Redirect to a View "page-login" and a Controller "Private-area". I can access the View, but the Controller sends to 404 Page Not Found.
if($this->form_validation->run()){
$result = $this->login_model->can_login($this->input->post('user_email'), $this->input->post('user_password'));
if($result == ''){
redirect('private_area');
}
else {
$this->session->set_flashdata('message', $result);
redirect('page-login');
FYI I can access Controllers (in this example "Register") using other methods such as Form Open like this:
<?php echo form_open('register/validation'); ?>
Why do I get the 404 error?
Your redirect syntax is incorrect:
the CI function redirect() is structured like this:
redirect($uri = '', $method = 'auto', $code = NULL)
keep in mind to use a relative path like '/my_controller/my_function'
as in this example:
redirect('/login/form/');
you need to autoload/load the URL helper with: $this->load->helper('url');
the form_open() syntax is correct, you need to autoload/load the Form Helper with: $this->load->helper('form');
redirect() function
Does a “header redirect” to the URI specified. If you specify the full site URL that link will be built, but for local links simply providing the URI segments to the controller you want to direct to will create the link. The function will build the URL based on your config file values. (- source)
So, the url must be
redirect("/controller/method/parameters");
Or full url
In your code, Codeigniter will look for the index() method of private_area controller.
Thanks All!
So I think #Don'tPanic nailed it. I thought "Redirect" would point to a Controller - but it points to a Route. So I created a Route in my Routes.php file where
$route['private_area'] = 'private_area';
And everything works. Is this the correct way to do this?
Ie to define Routes in the Routes.php file...then call upon them as required using "Redirect"?
I'm trying to setup a blog script on a website running on the CodeIgniter framework. I want do this without making any major code changes to my existing website's code. I figured that creating a sub domain pointing to another Controller would be the cleanest method of doing this.
The steps that I took to setup my new Blog controller involved:
Creating an A record pointing to my server's ip address.
Adding new rules to CodeIgniter's routes.php file.
Here is what I came up with:
switch ($_SERVER['HTTP_HOST']) {
case 'blog.notedu.mp':
$route['default_controller'] = "blog";
$route['latest'] = "blog/latest";
break;
default:
$route['default_controller'] = "main";
break;
}
This should point blog.notedu.mp and blog.notedu.mp/latest to my blog controller.
Now here is the problem...
Accessing blog.notedu.mp or blog.notedu.mp/index.php/blog/latest works fine, however accessing blog.notedu.mp/latest takes me to a 404 page for some reason...
My .htaccess file looks like this (the default for removing index.php from the url):
RewriteEngine on
RewriteCond $1 !^(index\.php|images|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]
And my Blog controller contains the following code:
class Blog extends CI_Controller {
public function _remap($method){
echo "_remap function called.\n";
echo "The method called was: ".$method;
}
public function index()
{
$this->load->helper('url');
$this->load->helper('../../global/helpers/base');
$this->load->view('blog');
}
public function latest(){
echo "latest working";
}
}
What am I missing out on or doing wrong here? I've been searching for a solution to this problem for days :(
After 4 days of trial and error, I've finally fixed this issue!
Turns out it was a .htaccess problem and the following rules fixed it:
RewriteEngine on
RewriteCond $1 !^(index\.php|images|robots\.txt)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
Thanks to everyone that read or answered this question.
Does blog.domain.co/blog/latest also show a 404?
maybe you could also take a look at the _remap() function for your default controller.
http://ellislab.com/codeigniter/user-guide/general/controllers.html#default
Basically, CodeIgniter uses the second segment of the URI to determine which function in the controller gets called. You to override this behavior through the use of the _remap() function.
Straight from the user guide,
If your controller contains a function named _remap(), it will always
get called regardless of what your URI contains. It overrides the
normal behavior in which the URI determines which function is called,
allowing you to define your own function routing rules.
public function _remap($method)
{
if ($method == 'some_method')
{
$this->$method();
}
else
{
$this->default_method();
}
}
Hope this helps.
have a "AllowOverride All" in the configuration file of the subdomain in apache?
without it "blog.notedu.mp/index.php/blog/latest" work perfectly, but "blog.notedu.mp/latest" no
$route['latest'] = "index";
means that the URL http://blog.example.com/latest will look for an index() method in an index controller.
You want
$route['latest'] = "blog/latest";
Codeigniter user guide has a clear explanation about routes here
I'm trying to learn MVC design pattern for web applications so I decided to write my own PHP MVC framework. Before writing this post I read a lot of tutorials and forums about MVC. Now I pretty well understanding the MVC idea, and how communicate controller-model-view. I have write router and few modules (login, categories, ...) - seems it's working.
Now I'm confused a bit:
If I call localhost/LogIn I get only login form, if I call localhost/categories I get category list. Everything OK, but I want to create index controller and when calling localhost/index I want see login form, categories and a lot more modules.
Should I call controllers (login, categories) from indexController.php?
I need advice how to concatenate needed modules in one page.
No, controllers shouldn't be calling each other's functions. Some frameworks introduce "helpers" to implement what you need.
Controllers can use the same models, and views anyway are going to be different, so you can use your Categories model to provide you categories to display (e.g. $categories->getCategoriesList()) and then using it in category controller view and also in index controller view.
A legitimate method of calling one controller from another is by forming an HTTP request - e.g. receiving an HTML snippet (another controller rendered view) to display in your view via AJAX or using an iframe with a source pointing to your another controller (which is a clumsy solution, mostly for idea illustration).
You need several things:
You need a .htaccess file that will cause all requests to go through your index file, here is a simple one:
RewriteEngine On
RewriteBase /demo
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
In the index.php file you need to set the include path, so you won't have to explicitly include the modules/controllers/views or any other class you choose:
define("APPLICATION_PATH", realpath('.'));
$paths = array(
APPLICATION_PATH.'/controllers',
APPLICATION_PATH.'/models',
APPLICATION_PATH.'/views',
APPLICATION_PATH.'/libs',
APPLICATION_PATH.'/includes',
get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $paths));
now add the 'magic method' for autoloading classes (called automatically) and initialize your Bootsrap class:
function __autoload($className){
$fileName = str_replace('\\','/', $className);
require_once "$fileName.php";
}
new Bootstrap();
Bootstrap.php:
<?php
class Bootstrap {
public function __construct() {
$url = $_GET['url'];
$params = explode('/', $url);
//if controller exist - call it, else call login controller
if (isset($params[0]) && $params[0]){
$controller = new $params[0]();
}
else{
$controller = new login();
}
//if method exist - call it, else call index method
if (isset($params[1]) && $params[1]){
//if parameter exit - call method with param, else call witout param
if (isset($params[2]) && $params[2]){
$controller->$params[1]($params[2]);
}
else{
$controller->$params[1]();
}
}
else{
$controller->index();
}
}
}
That should give you a basic MVC Framework.
Use your controller (index.php) to centralized code that would be used on every page (request validators, error handles, exception handlers, session stuff).
Create a Router class to get the correct models. Allow the models to get the correct views. I have included some UML diagrams from my other answer (https://stackoverflow.com/questions/42172228/is-this-how-an-mvc-router-class-typically-works) to help out. Remember, try to program to an abstract interface, not to a concrete implementation.
I was wondering how one could make codeigniter style url segments on a project.
Aside from an htaccess rule to capture the segments, how can this be done in PHP?
After snooping around the codeigniter source code, i could not find any reference to an htaccess file that captures the usage.
Any idea on how this can be done?
Assuming you are passing ALL requests to index.php via Apache mod_rewrite or Nginx:
// Get the current URL path (only) and convert encoded characters back to unicode
$path = rawurldecode(trim(parse_url(getenv('REQUEST_URI'), PHP_URL_PATH), '/')));
// array merge with defaults
$segments = explode('/', $path) + array('home', 'index');
//First two segments are controller/method
$controller = array_shift($segments);
$method = array_shift($segments);
// #todo serious security checking here!
// Finally, load and call
$controller = new $controller;
$controller->$method($segments);
Your controller would look like this
class Home
{
public function index($params)
{
print_r($params);
}
}
What you do is set up one single url param in htaccess, and then use a string splitting method to retrieve a model, controller, and view from that string which will then call a model class, a controller class, and then render the data into a view. the transformation would be something like the following:
mysite.com/index.php?url=/tasks/all => mysite.com/tasks/all
which calls the task model, which then calls the function called "all()" inside of the tasks controller.
As soon as this site goes back online, do look at the htaccess portion of the tutorial -- he did a good job of showing how its done http://www.henriquebarroso.com/how-to-create-a-simple-mvc-framework-in-php/
You still need something to "forward" all virtual requests to a physical file.
The idea is that any URI that doesn't match a physical file or folder on disk is rewritten (usually through mod_rewrite) to your index.php file (it's usually your index file so a direct call to the index works too), and it appends the URI to the path or as a query string parameter:
RewriteCond %{REQUEST_FILENAME} !-f # Not a real file
RewriteCond %{REQUEST_FILENAME} !-d # Not a real folder
RewriteRule ^(.*)$ index.php/$1 [L] # Rewrite to index.php, with a leading slash, followed by the URI
Alternatively, you can use a standard error document handler (still in .htaccess or apache config, but no need for mod_rewrite!):
<IfModule !mod_rewrite.c>
ErrorDocument 404 /index.php
</IfModule>
Now that control is passed to uniformly to an index.php file, you need a router mechanism to match the route to the correct controller. Ideally, you'd have a list of static and dynamic routes. Static routes are direct matches to the URI, and dynamic would be regular expression matches. Check your static routes first, as it's as simple as a single hash lookup, while you'll have to loop through all the dynamic routes.
For performance, it's nice to put your more common dynamic routes at the beginning of the list, and the obscure ones at the end.
Using the .htaccess file to employ mod_rewrite to rewrite the base of the url is essential to this. Otherwise, the browser will treat each segment as a supposed folder or file (depending on whether or not you have a trailing /)
once you have that, however, you can simply use the method described in this post:
how do i parse url php
I am setting up a simple routing system for my new custom MVC framework that I am making.
Currently my router class views the URL as such:
www.example.com/controller/controller_action/some/other/params
So, essentially...I've been reserving the first two segments of the URI for controller routing. However, what if I just want to run the following?
www.example.com/controller/some/other/params
...which would attempt to just run the default controller action and send the extra parameters to it?
Here is the simple router I'm using:
\* --- htaccess --- *\
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]
\* --- index.php --- *\
if (array_key_exists('rt',$_GET)) {
$path = $_GET['rt'];
$uri = explode('/',$this->path);
if(empty($uri[0])) {
$load->ctrl('home');
}
elseif(empty($uri[1])) {
$load->ctrl($uri[0]);
}
else {
$load->ctrl($uri[0],$uri[1]);
}
}
else {
$load->ctrl('index');
}
\* --- loader class --- *\
public function ctrl($ctrl,$action=null) {
$ctrl_name = 'Ctrl_'.ucfirst(strtolower($ctrl));
$ctrl_path = ABS_PATH . 'ctrl/' . strtolower($ctrl) . '.php';
if(file_exists($ctrl_path)) { require_once $ctrl_path;}
$ctrl = new $ctrl_name();
is_null($action) ? $action = "__default" : $action = strtolower($action);
$ctrl->$action();
}
How can I do this?
You could handle this within your controller. Typically, MVC frameworks will call a default method when the requested method isn't available. Simply overwrite this fallback-method to call your desired method and pass the parameter list in as parameters.
For instance, KohanaPHP has the __call($method, $params) method that is called when the requested method doesn't exist. You could handle the logic within this, or its functional equivalent in your MVC framework..
This would let you keep the logic internal to the controller itself rather than having it blasted out between various files.