Jump to content
Sign in to follow this  
Tahhan

PHP MVC structure and dynamic URL re-writing

Recommended Posts

I have followed a youtube playlist to build a simple MVC framework based on PHP, here is a link to the playlist I have followed.

and here is an image of my application structure image

RQjMz.png

I have connected the application to a MySql database, everything is working fine, but now I am trying to do friendly URLs.

Currently, my URLs look like the following:

mywebsite.com/public/home/property

This is fine as this link is for a static page, but I want the same for dynamic URLs, for example:

Current URL: mywebsite.com/public/home/property?id=10

to URL: mywebsite.com/public/home/property/id/10

I have tried a lot of methods in htaccss but the data is not pulled from the database once the URL is re-written and I get only an empty page.

current .htaccess code (the one that is located in the public folder):

Options -MultiViews
RewriteEngine On

RewriteBase /public

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]

I have tried a lot of methods and rules but non have worked.

I am not sure if the details I provided are clear enough, I am little confused as I combined a lot of tutorials together to create this app.

App.php code:

	
<?php
class App{
protected $controller = 'home';
protected $method = 'index';
protected $params = [];
public function __construct(){
$url = $this->parseUrl();
if(file_exists('../app/controllers/' . $url[0] . '.php')){
$this->controller = $url[0];
unset($url[0]);
}
require_once '../app/controllers/' .$this->controller. '.php';
$this->controller = new $this->controller;

if(isset($url[1])){
if(method_exists($this->controller, $url[1])){
$this->method = $url[1];
unset($url[1]);
}
}
$this->params = $url ? array_values($url) : [];
call_user_func_array([$this->controller, $this->method], $this->params);
}

public function parseUrl(){
if(isset($_GET['url'])){
return $url = explode('/', filter_var(rtrim($_GET['url'], '/'), FILTER_SANITIZE_URL));
echo $url;
}
}
}
?>

controller.php code:

<?php
  class Controller{
    public function model($model){
      require_once '../app/models/' . $model . '.php';
      return new $model();
    }
    public function view($view, $data = []){
      require_once '../app/views/' . $view . '.php';
    }
  }
 ?>

controllers code:

<?php
  class home extends Controller{
    public function index(){
      $this->view('home/index');
    }
    public function listings(){
      $this->view('home/listings');
    }
    public function property(){
      $this->view('home/property');
    }
  }
 ?>

code for the page that should show data from mysql database:

<?php include "includes/head.php"; ?>
<?php $property = Property::find_id($_GET['id']); ?>
<section id="sub-main"></section>
<section id="sub-list">
  <div class="container">
    <div class="row">
      <div class="col-xs-12 col-sm-6">
        <div id="listingCarousel" class="carousel slide" data-ride="carousel">
          <div class="carousel-inner" role="listbox">
            <div class="item active">
              <img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=585%C3%97380&w=585&h=380" alt="">
            </div>
            <div class="item">
              <img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=585%C3%97380&w=585&h=380" alt="">
            </div>
            <div class="item">
              <img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=585%C3%97380&w=585&h=380" alt="">
            </div>
          </div>
        </div>
      </div>
      <div class="col-xs-12 col-sm-6">
        <h1><?php echo $property->property_name; ?></h1>
        <ul class="list-inline">
          <li><i class="fa fa-map-signs" aria-hidden="true"></i> <b></b> <?php echo $property->city; ?></li>
        </ul>
      </div>
    </div>
    <hr>

    <div id="map"></div>
  </div>
</section>
<?php include "includes/subscribe.php"; ?>
<?php include "includes/footer.php"; ?>

The project is online, I can give full access if someone interested in helping me solve this issue.

Share this post


Link to post
Share on other sites

That was a long post so I'm just going to cover it from top to bottom.

 

> Current URL: mywebsite.com/public/home/property?id=10

Are you sure you want "public" in there? By the looks of it you should have public as the web root - you can still reference files outside it but this way the website won't try to offer application files (eg, /app/init.php) when it shouldn't.

 

Remove /public from the places that use it, like the entire RewriteBase line in your .htaccess. Naturally that means you can get rid of /app/.htaccess too.

 

 

> RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]

Fine so far. I assume index.php (or /app/init.php?) ends up doing

new App();
Having a new object automatically run code isn't the best design, but it isn't something to worry about right now.

 

 

> if(file_exists('../app/controllers/' . $url[0] . '.php')){

1. Doing relative file paths is very dangerous. In your situation index.php or init.php should create something (I suggest a constant) that gives an absolute path to the root of the entire application: the directory that has app/ and public/. As in

define("APP_ROOT", dirname(__DIR__)); // if run from /public/index.php or /app/init.php
Then

if(file_exists(APP_ROOT . '/app/controllers/' . $url[0] . '.php')){
2. Are you sure $url contains what you think it does? Dump it out.

 

3. Before you translate $url to a file path you need to make sure it's safe to do so: $url could be maliciously created (since it comes from the URL) in a way to trick your code into running files it shouldn't. Since the first part maps to a class name, you can verify it only contains valid class name characters*.

 

 

> include "includes/head.php";

Same comment from before about relative paths. You could define another constant for the path to the includes/ directory, or

include $_SERVER["DOCUMENT_ROOT"] /* which is now public/ */ . "/includes/head.php";

* Class names in PHP can have more than just letters, but in your application that doesn't look like it will be the case for the controllers. So restricting it to letters should be fine.

Share this post


Link to post
Share on other sites

Thanks for the reply, I really appreciate it from you, pardon me please as I am not an advanced PHP user, especially when it comes to OOP and MVC, I have tried to do what you wrote me. but still having the same issue.

 

I just want to know how to pass the variable which is id in the URL, without showing the "?"  and "=" sign, I would really appreciate it if you help with this, as I have been trying for more than 3 days and couldn't achieve anything.

Share this post


Link to post
Share on other sites

You're already doing the hard part of it: the RewriteRule stuff. The rest is you making creating the right URLs in your links - that doesn't happen automatically.

  • Like 1

Share this post


Link to post
Share on other sites

That for me was the hardest part in writing clean URLS and I don't know why?  :confused:

 

I would have this in my .htaccess file

RewriteRule ^(index|about|blog|calendar|contact|edit|login|order)$ $1.php [NC,L]


RewriteRule ^edit/(\d+)$ edit.php?id=$1 [NC,L]

but forget I had to do this

echo '<a class="edit" href="edit/' . $this->row->id . '">Edit</a>';

I would spend days trying to get it work and finally a light bulb turned on, but until I did figure it out it was like I was  :suicide: myself.  

Share this post


Link to post
Share on other sites

I had this issue in the past, I even hired an advanced PHP developer to solve it, and then it turned out that I was forgetting to implement it in the a tag.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.