Jump to content

PHP hierarchical MVC design from scratch?


hazel1919

Recommended Posts

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

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:

 

X8HlI.png

 

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

 

Link to comment
Share on other sites

Binding the modules to physical folders isn't really a good idea, because it makes the architecture very inflexible. What if I want a large number of similar modules that share a lot of functionalities? It may not make sense to duplicate the entire folder structure for each one of them. What if I want virtual URLs that don't map to any physical classes at all?

 

It think what you're looking for is a router which processes the URL and maps it to an arbitrary action. This allows you to separate the URL structure from the actual backend structure. It also solves the problem at hand, because instead of making PHP search the folder structure on every request, you register your modules and controllers once.

Edited by Jacques1
  • Like 1
Link to comment
Share on other sites

Thanks for your kind replies... models needing to be used across the entire application is a good point. Perhaps an improvement to the folder structure is separating the models into one usable folder like this...

 

 

l7rbdZo.png

 

I will build the framework out and see if it works, I will know if I am repeating any code that I need to make adjustments. This is for my education so it cant hurt!

 

@gizmola, thanks for the link, that is some fantastic documentation and I have been going through the tutorial today... unfortunately there are just too many black boxes which I just don't understand (either why they exist or how to use them), otherwise I would love to jump straight into symfony or laravel and start taking advantage of the genius of the community. That is why I have to learn the basics of MVC/routing/design patterns first.

 

I just cant learn the "how to" without the "why" in the first place.

 

Best regards,

 

Jaques.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

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.