Instead of using ?id=30 - php

and thanks for taking a look to this weird and awkward post.
I have been working on a Cars website for a while (version: 1) wich is pretty basic, now im learning a little bit more about POO so i guess i will be updating it...
I have seen before on a Scripts website that they do this:
Example:
websiteurl.com/scripts/view/300 or websiteurl.com/scripts/view/1761/media/
or websiteurl.com/scripts/view/1761/comments
Im currently doing this:
mywebsite.com/viewCars.php?id=300 but i would like instead of doing that , doing this:
mywebsite.com/cars/view/300
Thanks, and i hope someone understand my question, forgive me for my bad spelling but english isn't my first language.

Assuming you are using apache, soemthiing like this should give you the format you mentioned
in your .htaccess file
----------------------
RewriteEngine On
RewriteBase /
RewriteRule ^cars/view/([0-9]+)$ viewCars.php?id=$1 [NC,L]

have you heard about htaccess. Check the documentaion. You can sole your problem by using htaccess and some regular expresion. Here is an example solution for your question.
https://mediatemple.net/community/products/dv/204643270/using-htaccess-rewrite-rules
For your solution, you can write following code in your htaccess.
RewriteEngine On
RewriteBase /
RewriteRule ^cars/view/([0-9]+)$ viewCars.php?id=$1 [NC,QSA,L]

If you are using a MVC architecture, you could do something like this:
function __construct() {
$url = isset($_GET['url']) ? $_GET['url'] : null;
$url = rtrim($url, '/');
$url = explode('/', $url);
if (empty($url[0])) {
require 'controllers/index.php';
$controller = new Index();
$controller->index();
return false;
}
$file = 'controllers/' . $url[0] . '.php';
if (file_exists($file)) {
require $file;
} else {
$this->error();
}
$controller = new $url[0];
$controller->loadModel($url[0]);
// calling methods
if (isset($url[2])) {
if (method_exists($controller, $url[1])) {
$controller->{$url[1]}($url[2]);
} else {
$this->error();
}
} else {
if (isset($url[1])) {
if (method_exists($controller, $url[1])) {
$controller->{$url[1]}();
} else {
$this->error();
}
} else {
$controller->index();
}
}
}

Related

routing in MVC doesn't work in server, but work properly in localhost

i made a simple mvc with routing system. When i deployed it in 000webhost to be tested, all links don't work. They only shown in the URL. No error message.
i tried to change php version on the server to be the same as my php on localhost, it still didn't work
I guess maybe there's something wrong in my htaccess
here is the routing code:
<?php
class App
{
// controller, method, dan parameter
protected $controller = 'Home',
$method = 'index',
$params = [];
public function __construct()
{
$url = $this->parseURL();
// get controller dari url
if (file_exists('app/controllers/' . $url[0] . '.php')) {
$this->controller = $url[0];
// unset untuk menentukan param
unset($url[0]);
}
// call controller
require_once 'app/controllers/' . $this->controller . '.php';
// instansiasi class controller
$this->controller = new $this->controller;
// get method from url
// check if method exist in url
if (isset($url[1])) {
// cek ketersediaan method pada controller
if (method_exists($this->controller, $url[1])) {
$this->method = $url[1];
// unset untuk menentukan param
unset($url[1]);
}
}
// get param from url
// check array
if (!empty($url)) {
$this->params = array_values($url);
}
// run controller and method and param if exist
call_user_func_array([$this->controller, $this->method], $this->params);
}
public function parseURL()
{
if (isset($_GET['url'])) {
$url = rtrim($_GET['url'], '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
return $url;
}
}
}
and here's the htaccess
Options -Multiviews
DirectoryIndex index.php
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [L]
RewriteRule !^(public/|index\.php) [NC,F]

PHP hierarchical MVC design from scratch?

Background
I have been working through various tutorials over the past couple of months and am currently trying understand PHP frameworks.
One of the ways I am doing this is by trying to design my own very simple MVC framework from scratch.
I am trying to re-factor an application (which I have already built using spaghetti procedural PHP). This application has a front end for teachers and a back-end for the administrators.
I would like to separate concerns and have URL's like this
http://example.com/{module}/{controller}/{method}/{param-1}/{param-2}
Now the MVC framework I have cobbled together up to this point does not handle routing for 'modules' (I apologise if this is not the correct terminology), only the controller/method/params.
So I have separated the public_html from the app logic and inside of the /app/ folder I have specified two folders, my default "learn module" and the "admin module" so that the directory tree looks like this:
Apparently this design pattern is a "H"MVC?
My Solution
I am basically making use if the is_dir(); function to check if there is a "module" directory (such as "admin") and then unsetting the first URL array element $url[0] and reindexing the array to 0... then I am changing the controller path according to the URL... the code should be clearer...
<?php
class App
{
protected $_module = 'learn'; // default module --> learn
protected $_controller = 'home'; // default controller --> home
protected $_method = 'index'; // default method --> index
protected $_params = []; // default parameters --> empty array
public function __construct() {
$url = $this->parseUrl(); // returns the url array
// Checks if $url[0] is a module else it is a controller
if (!empty($url) && is_dir('../app/' . $url[0])) {
$this->_module = $url[0]; // if it is a model then assign it
unset($url[0]);
if (!empty($url[1]) && file_exists('../app/' . $this->_module . '/controllers/' . $url[1] . '.php')) {
$this->_controller = $url[1]; // if $url[1] is also set, it must be a controller
unset($url[1]);
$url = array_values($url); // reset the array to zero, we are left with {method}{param}{etc..}
}
// if $url[0] is not a module then it might be a controller...
} else if (!empty($url[0]) && file_exists('../app/' . $this->_module . '/controllers/' . $url[0] . '.php')) {
$this->controller = $url[0]; // if it is a controller then assign it
unset($url[0]);
$url = array_values($url); // reset the array to zero
} // else if url is empty default {module}{controller}{method} is loaded
// default is ../app/learn/home/index.php
require_once '../app/' . $this->_module . '/controllers/' . $this->_controller . '.php';
$this->_controller = new $this->_controller;
// if there are methods left in the array
if (isset($url[0])) {
// and the methods are legit
if (method_exists($this->_controller, $url[0])) {
// sets the method that we will be using
$this->_method = $url[0];
unset($url[0]);
} // else nothing is set
}
// if there is anything else left in $url then it is a parameter
$this->_params = $url ? array_values($url) : [];
// calling everything
call_user_func_array([$this->_controller, $this->_method], $this->_params);
}
public function parseUrl() {
// checks if there is a url to work with
if (isset($_GET['url'])) {
// explodes the url by the '/' and returns an array of url 'elements'
return $url = EXPLODE('/', filter_var(rtrim($_GET['url'], '/'), FILTER_SANITIZE_URL));
}
}
}
this so far appears to be working for me, but.....
Question
I am not sure if this is the preferred solution to this issue. Is calling the is_dir() check for every page request going slow down my app?
How would you engineer a solution or have I completely misunderstood the issue?
Many thanks in advance for your time and consideration!!
In my experience, I often use .htaccess file to redirect any request to an only index.php file, both these files place in the public_html folder.
The content of .htaccess file as following (your apache server should enabled mod_rewrite):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
Then, in the index.php file you can parse request url, define configs, common variables, paths, etc... and include neccessary others php files.
An example:
require( dirname( __FILE__ ) . '/your_routing.php' );
require( dirname( __FILE__ ) . '/your_configs.php' );
require( dirname( __FILE__ ) . '/admin/yourfile.php' );
...

Php mvc not loading images using <img>

I have made my own php MVC and it works perfectly for everything except when it comes to loading certain images. For example it will load like icons, and some other images but when it comes to using the tag it wont load the image but when i look in the google chrome developer inspector tool it shows that my tag images aren't loading. Its giving back a 301 number and loading the HTML. So in terms its like my MVC is thinking the URL for the image is a page!I want my MVC to only make requests to my index.php file which is in the root directory for my site. Also when it comes to it generating the HTML code i mean that it going to my error html page when i put the direct image url
in the browser.
Absolute URL for the image:
http://localhost/website/user_data/cfedabd2e283c19c06f3b8b5bd45df4bb66c2b3a/photo.jpg
Here is my code below:
.htaccess--
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?u=$1 [PT,L]
</IfModule>
bootstrap.php(Router)--
class Bootstrap {
function __construct() {
$url = $_GET['u'];
$url = rtrim($url, '/');
$url = explode('/', $url);
if (empty($url[0])) {
require 'controllers/index.php';
$controller = new Index();
$controller->index();
return false;
}
$file = 'controllers/' . $url[0] . '.php';
if (file_exists($file)) {
require $file;
} else {
$this->error();
}
$controller = new $url[0];
$controller->loadModel($url[0]);
// calling methods
if (isset($url[2])) {
if (method_exists($controller, $url[1])) {
$controller->{$url[1]}($url[2]);
} else {
$this->error();
}
} else {
if (isset($url[1])) {
if (method_exists($controller, $url[1])) {
$controller->{$url[1]}();
} else {
$this->error();
}
} else {
$controller->index();
}
}
}
function error() {
header('location: ' . APP_URL . 'index');
}
index.php(Where i want all the requests to go)--
<?php
//error_reporting(E_ALL);
session_start();
/* Require all config files */
require 'config/config.php';
/* Initiate system */
$bootstrap = new Bootstrap;
?>
Example What the user sees--
<h3>Error page not found</h3>
The image with
$photo = "http://localhost/website/user_data/cfedabd2e283c19c06f3b8b5bd45df4bb66c2b3a/photo.jpg;
<img src="<?php echo $photo; ?>" width="200" style="max-height: 200;"/>

How to prevent 403, 400 errors URL forwarding?

Site works good when user enter any url which doesn't exist and forward request to error controller.
But
If you write a script in url site throw 403 error
If you write some asp codes site throw 400 error
How can i prevent this and forward them to custom 403 and 404 pages? I tried forward with htaccess but i couldn't succeed it.
Another problem:
If you write ANY ascii code in adress bar, bootstrap forward to welcome page (controller ->index.php) .How is possible?
Directory structures, htaccess and bootstrap codes are below. Thank you for any help.
Directory structure:
/config
/libs
-bootstrap.php
/controllers
/models
/views
/public_html
-index.php
htaccess
htaccess
htaccess in main directory
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ public_html/ [L]
RewriteRule (.*) public_html/$1 [L]
</IfModule>
htaccess in public_html directory
Options -Indexes
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [PT,L]
</IfModule>
index.php in public_html
<?php
define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(dirname(__FILE__)));
require_once (ROOT . DS . 'libs' . DS . 'bootstrap.php');
$app = new Bootstrap();
bootstrap.php in libs
<?php
class Bootstrap {
function __construct() {
$url = isset($_GET['url']) ? $_GET['url'] : null;
$url = rtrim($url, '/');
$url = explode('/', $url);
print_r($url);
if (empty($url[0])) {
require '../controllers/index.php';
$controller = new Index();
$controller->index();
return false;
}
$file = '../controllers/' . $url[0] . '.php';
if (file_exists($file)) {
require $file;
} else {
$this->error();
return false;
}
$controller = new $url[0];
// calling methods
if (isset($url[2])) {
if (method_exists($controller, $url[1])) {
$controller->{$url[1]}($url[2]);
} else {
$this->error();
}
} else {
if (isset($url[1])) {
if (method_exists($controller, $url[1])) {
$controller->{$url[1]}();
} else {
$this->error();
}
} else {
$controller->index();
}
}
}
function error() {
require '../controllers/error.php';
$controller = new Error();
$controller->index();
return false;
}
}

Friendly URL's with an IndexController

My current router / FrontController is setup to dissect URL's in the format:
http://localhost/controller/method/arg1/arg2/etc...
However, I'm not sure how to get certain requests to default to the IndexController so that I can type:
http://localhost/contact
or
http://localhost/about/portfolio
Instead of:
http://localhost/index/contact
or
http://localhost/index/about/portfolio
How is this accomplished?
<?php
namespace framework;
class FrontController {
const DEFAULT_CONTROLLER = 'framework\controllers\IndexController';
const DEFAULT_METHOD = 'index';
public $controller = self::DEFAULT_CONTROLLER;
public $method = self::DEFAULT_METHOD;
public $params = array();
public $model;
public $view;
function __construct() {
$this->model = new ModelFactory();
$this->view = new View();
}
// route request to the appropriate controller
public function route() {
// get request path
$basePath = trim(substr(PUBLIC_PATH, strlen($_SERVER['DOCUMENT_ROOT'])), '/') . '/';
$path = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), '/');
if($basePath != '/' && strpos($path, $basePath) === 0) {
$path = substr($path, strlen($basePath));
}
// determine what action to take
#list($controller, $method, $params) = explode('/', $path, 3);
if(isset($controller, $method)) {
$obj = __NAMESPACE__ . '\\controllers\\' . ucfirst(strtolower($controller)) . 'Controller';
$interface = __NAMESPACE__ . '\\controllers\\' . 'InterfaceController';
// make sure a properly implemented controller and corresponding method exists
if(class_exists($obj) && method_exists($obj, $method) && in_array($interface, class_implements($obj))) {
$this->controller = $obj;
$this->method = $method;
if(isset($params)) {
$this->params = explode('/', $params);
}
}
}
// make sure we have the appropriate number of arguments
$args = new \ReflectionMethod($this->controller, $this->method);
$totalArgs = count($this->params);
if($totalArgs >= $args->getNumberOfRequiredParameters() && $totalArgs <= $args->getNumberOfParameters()) {
call_user_func_array(array(new $this->controller, $this->method), $this->params);
} else {
$this->view->load('404');
}
}
}
You can use your URLs by one of two methods:
Establish the controllers the way your routing defines them
example.com/contact => Have a "contact" controller with default or index action
example.com/about/portfolio => Have an "about" controller with a "portfolio" action
Because your currently available routing says your URL is treated like "/controller/method", there is no other way.
Establish dynamic routing to allow multiple URLs to be handled by a single controller
Obviously this needs a bit of configuration because one cannot know which URLs are valid and which one should be redirected to the generic controller, and which ones should not. This is somehow a replacement for any of the rewriting or redirecting solutions, but as it is handled on the PHP level, change might be easier to handle (some webserver configurations do not offer .htaccess because of performance reasons, and it generally is more effort to create these).
Your configuration input is:
The URL you want to be handled and
The controller you want the URL passed to, and it's action.
You'll end up having an array structure like this:
$specialRoutes = array(
"/contact" => "IndexController::indexAction",
"/about/portfolio" => "IndexController::indexAction"
);
What's missing is that this action should get the current URL passed as a parameter, or that the path parts become designated parameters within your URL schema.
All in all this approach is a lot harder to code. To get an idea, try to look at the routing of common MVC frameworks, like Symfony and Zend Framework. They offer highly configurable routing, and because of this, the routing takes place in multiple classes. The main router only reads the configuration and then passes the routing of any URL to the configured routers if a match is detected.
Based on your code snippet I'd do it like this (pseudo php code):
$handler = get_controller($controller);
if(!$handler && ($alias = lookup_alias($path))) {
list($handler, $method) = $alias;
}
if(!$handler) error_404();
function lookup_alias($path) {
foreach(ALL_CONTROLLERS as $controller) {
if(($alias = $controller->get_alias($path))) {
return $alias;
}
}
return null;
}
So basically in case there is no controller to handle a certain location you check if any controller is configured to handle the given path as an alias and if yes return that controller and the method it maps to.
You can create a rewrite in your webserver for these exceptions. For example:
RewriteRule ^contact$ /index/contact
RewriteRule ^about/portfolio$ /about/portfolio
This will allow you to have simplified URLs that map to your regular structure.
You could have a dynamic rule if you are able to precisely define what should be rewritten to /index. For example:
RewriteRule ^([a-z]+)$ /index/$1
Try this dynamic htaccess rewrite rule:
RewriteRule ^(.+)/?$ /index/$1 [QSA]
The QSA flag in the above rule allows you to also add a query string to the end if you want, like this:
http://localhost/contact?arg1=1&arg2=2
EDIT: This rule would also handle cases such as /about/portfolio:
RewriteRule ^(.+)/?(.+)?$ /index/$1 [QSA]

Categories