PHP hierarchical MVC design from scratch?

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
hazel1919
Forum Newbie
Posts: 9
Joined: Wed Jan 27, 2016 10:07 am

PHP hierarchical MVC design from scratch?

Post by hazel1919 »

Hi everyone,

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

Code: Select all

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:

Image

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...

Code: Select all

<?php

class App
{

    protected $_module = 'learn'; // default module --> learn
    protected $_controller = 'home'; // default controller --> home
    protected $_method = 'index'; // default method --> index
    protected $_params = []; // default paramatars --> 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!!

Hazel,
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: PHP hierarchical MVC design from scratch?

Post by Celauran »

hazel1919 wrote:I would like to separate concerns and have URL's like this

Code: Select all

http://example.com/{module}/{controller}/{method}/{param-1}/{param-2}
This seems brittle. What if I want to have a URI like /signup? What if I later want to change that to /register?
hazel1919
Forum Newbie
Posts: 9
Joined: Wed Jan 27, 2016 10:07 am

Re: PHP hierarchical MVC design from scratch?

Post by hazel1919 »

Hey Celauran, I appreciate the response.

To add /signup I would have to add a controller called app/learn/controllers/Signup.php... to change the URI I suppose I would have to update the controller name, and the model name...

I am aware that this is not the best solution so I am looking for ideas... would I need to design a separate routing abstraction layer? Could someone perhaps point me in the direction of a simple example?
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: PHP hierarchical MVC design from scratch?

Post by Celauran »

Maybe take a look at PHP-Router, FastRoute, or even Symfony's routing component. You could use the routing scheme you described earlier as a fallback, though I believe you can see its shortcomings.
hazel1919
Forum Newbie
Posts: 9
Joined: Wed Jan 27, 2016 10:07 am

Re: PHP hierarchical MVC design from scratch?

Post by hazel1919 »

Thanks for the reply Celauran, I know this probably is not the best solution but I might try something like this folder structure:

Image

In any case this project is for my own education so that I can eventually jump into Laravel...

Best,

Jaques.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: PHP hierarchical MVC design from scratch?

Post by Christopher »

hazel1919 wrote: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?
It sounds like you are asking for optimization to reduce file system checks. The goal would be to only do a file_exists() check on /app/{$module}/controllers/{$controller}.php. That depends on the rules for your module/controller/action. If you always require the module to be specified then you don't really have any problem. If you module is optional in PATHINFO then you only need to do the module check where there are two items in PATHINFO. If there are three items then they are /module/controller/action/. If there is only one then it is /controller/.

You could also provide the Front Controller with an array of module names so that it would not need do the file system check, but that may cause difficult to find errors later.

PS - the aliasing that Celauran mentioned can be done later in the Router.
(#10850)
Post Reply