Jump to content


Photo

PHP MVC structure and dynamic URL re-writing

php mvc url mysql

  • Please log in to reply
5 replies to this topic

#1 Tahhan

Tahhan
  • New Members
  • Pip
  • Newbie
  • 4 posts

Posted 29 September 2017 - 11:34 PM

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.



#2 requinix

requinix
  • Administrators
  • Impoverished Administrator
  • 9,874 posts
  • LocationWA

Posted 30 September 2017 - 08:14 AM

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.
"Basically, I think the general rule of thumb is: if someone really wants the blood that's inside of your body, and they're like a vampire, or a dracula, or some sort of man-squito, then that's probably okay. A dracula and a man-squito are made for removing things like blood and swords from inside your body. That's basically fine. If something wants to get at your blood and they're, say, some kind of murdersaurus, or maybe a really big frog, that's where the problems start to arise. A really big frog is not made for removing blood, and your blood knows this, which is why it is so vehement about wanting to stay in your body instead of coming out. Unfortunately this will not deter a really big frog because a really big frog is full of things like prizes, and value, and quite a lot of hatred, and it would really rather like to replace any and all of those things with your blood, and basically by any means possible." --slumbermancer

#3 Tahhan

Tahhan
  • New Members
  • Pip
  • Newbie
  • 4 posts

Posted 30 September 2017 - 07:50 PM

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.



#4 requinix

requinix
  • Administrators
  • Impoverished Administrator
  • 9,874 posts
  • LocationWA

Posted 01 October 2017 - 11:56 AM

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.
"Basically, I think the general rule of thumb is: if someone really wants the blood that's inside of your body, and they're like a vampire, or a dracula, or some sort of man-squito, then that's probably okay. A dracula and a man-squito are made for removing things like blood and swords from inside your body. That's basically fine. If something wants to get at your blood and they're, say, some kind of murdersaurus, or maybe a really big frog, that's where the problems start to arise. A really big frog is not made for removing blood, and your blood knows this, which is why it is so vehement about wanting to stay in your body instead of coming out. Unfortunately this will not deter a really big frog because a really big frog is full of things like prizes, and value, and quite a lot of hatred, and it would really rather like to replace any and all of those things with your blood, and basically by any means possible." --slumbermancer

#5 Gandalf64

Gandalf64
  • Members
  • PipPip
  • Member
  • 19 posts

Posted 01 October 2017 - 05:03 PM

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.  



#6 Tahhan

Tahhan
  • New Members
  • Pip
  • Newbie
  • 4 posts

Posted 02 October 2017 - 07:27 PM

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.






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users