1
\$\begingroup\$

I'm new to the whole thing of MVC patterns. Although I have heard of it before, I have never used it, so I'm confused with it all. I know that this code is not right probably but I need some guidance.

This is my folder structure:

├── app
│   ├── controllers
│   │   └── user.php
│   ├── core
│   │   ├── app.php
│   │   └── controller.php
│   ├── init.php
│   ├── models
│   │   └── users.php
│   └── views
│       └── json-output.php
├── public
│   └── index.php

I have established the routing in it. When a certain route is called, if it finds the mashing controller it's able to take requests (get, push, pull, delete...).

The model isn't finished yet. I want to add a DML calls which will communicate with the database. It's there just to mimic if I can call it that way the simple communication.

The view takes the data from the model and displays it.

I have divided the project into the src and public directories.The public will be my main directory, and the calls will be made there.

Here is my project:

user.php

  controllers/
               user.php

<?php


class User extends Controller{

    public function main($params){
        switch($_SERVER['REQUEST_METHOD']){
            case 'GET':
                $this->get_user();
             break;
            case 'POST':
                 $this->add_user();
             break;
            case 'PUT':
                 $this->update_user();
             break;
            case 'DELETE':
                 $this->delete_user();
             break;
        }
    }

    public function get_user(){//TODO
        $user = $this->model('Users');
        $user->name = 'Arslan'; 

        $this->view('json-output',['output'=>$user->name]);
    }

    public function add_user(){//TODO
        $this->view('json-output',['output'=>'add_user']);
    }

    public function update_user(){//TODO
        $this->view('json-output',['output'=>'update_user']);
    }

    public function delete_user(){//TODO
        $this->view('json-output',['output'=>'delete_user']);
    }

}

app.php

<?php

class App{

    protected $controller = 'user';

    protected $method = 'main';

    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 ? $this->urlToParam(array_values($url)[0]) : [];

        call_user_func_array([$this->controller,$this->method],array($this->params));

    }

    public function parseUrl(){

        if(isset($_GET['url'])){
            return $url = explode('/',filter_var(rtrim($_GET['url'],'/'),FILTER_SANITIZE_URL));
        }

    }

    public function urlToParam($name){
        $fParams = array();
        $params = explode(',',$name);
        foreach($params as $param){
            $params_2 = rtrim(ltrim($param,"{"),"}");
            array_push($fParams,$params_2);
        }
        return $fParams;
    }

}

controller.php

<?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';

    }

}

user.php

<?php

class Users{

    public $name;

}

json-output.php

<?php

$output = array(
    "response" => $data['output']
);

echo json_encode($output,JSON_PRETTY_PRINT);

init.php

<?php

require_once 'core/app.php';
require_once 'core/controller.php';

index.php

<?php
require_once '../app/init.php';

$app = new App;

And here is the GitHub repository.

\$\endgroup\$
2
\$\begingroup\$

Most modern framework extend beyond the simple MVC pattern to also include object-oriented constructs representing the request and response and oftentimes a separate router class. I think that is something your framework could also benefit from, as right now, you have a mix of request management and routing functions in your App class that really doesn't fully achieve either (for example if you have a proper request object, your controller wouldn't need to bee inspecting $_SERVER['REQUEST_METHOD'] - this info should already be determined before a controller is even invoked.

So perhaps this would give you a program flow like this:

  • include bootstrap file.
  • instantiate and build request object.
  • pass request object to router, which determines controller to be used.
  • router injects request object into controller and passes program control to controller.
  • controller loads/interacts with model.
  • controller build s model and/or other meaningful data into a response object which it passes to view for rendering.

You should strongly consider using a PSR-4 compliant autoloader. This can move you out of the world of having to write a lot of code around locating and loading class dependencies.


You may consider moving towards a dependency injection approach to your framework. Right now, your framework exhibits poor encapsulation and division of responsibility, which is something that proper instantiation and passing of dependencies could help to address. For example, right now, pretty much your whole application logic boils to to the App constructor method. You get to the point of calling this method before:

  • you know if you have a properly formatted request
  • you know if the request is made against a valid route.

By this point, if you have a problem with the request or route, you would need a lot of code in this constructor to handle these cases, something you do a poor job of in this method. Walk through your code to see what happens if I sent a URL like someBadController/someBadMethod. You would actually try to instantiate new user and call main() on it!

If you went with a program flow like I presented above, you could:

  • Determine if request is valid. If not fail out (perhaps to standard error controller) quickly. If valid, instantiate request object and inject into router.
  • Determine if the passed request object maps to a valid controller. If not fail out (again perhaps to standard error controller with 400-series error). If route is found, instantiate the appropriate controller and pass it the request object.
  • Controller could inspect the injected request object to determine method that is being called, get user input, etc.

Should Controller base class be abstract? You are not ever expecting to instantiate it right?


Why is there model() method on Controller()? I would think any inheriting controller would be built with an explicit understanding of what model it interacts with. So rather than do things in inheriting class like:

$this->model('Users');

Why would you not just have a class constant that stores the model reference? What value is the method adding?


I think it strange to have a controller and model class name that is so similar. That could be really confusing. Consider UserController and User (singular since you are only representing a single user here right?)


I would think your JSON view should be setting appropriate application\json header. I also think it should not have any responsibility for changing the data structure (like you are doing by adding response key. This should be done by the controller/model and passed to the view so you are not splitting your data structure business logic up into different parts of the app.

If you take the approach mentioned above around building a response object into your framework and using dependency injection, you might instead make your views into a proper class that can take response dependency and perform all header setting and rendering actions specific to the passed response content.

For example in controller:

// could in one of your action methods
$response = new Response($model);
$view = $this->view($response);
$view->render();

// code to return a view object
public function view($response) {
    // perhaps you have some logic here to determine kind of 
    // view object to build
    return new JsonView($response);
}

In this case, perhaps the response object need to implement JsonSerializable interface to provide instructions on how to serialize data to JSON.

By thew way, the more of your framework object you make into classes, the less require, file_exists(), etc. type of logic you need in your code if you use a proper autoloader.


What values does nesting everything under response key add anyway? You are just adding an unnecessary level of nesting to the response data structure. The caller already knows they are getting a response :)


You should probably have a proper model base class that User and other models in your system can inherit from. I know you said this part wasn't complete, but you are showing nothing here to indicate that you are considering this.

You will find that a well-implemented base class will save a lot of code in the inheriting classes.

You would also move away from class-specific naming for your CRUD methods. These methods should just be add/create, get, delete, update and should be overridden as necessary in inheriting classes. You may also find that you have need for static/factory type methods on you model classes.

abstract class Model
{
    const TABLE;
    const ID;
    protected $fields = [];
    private $pdo;

    // some methods that could be called in static context
    public static getById(int $id, PDO $pdo)
   {
        $fields = '`' . implode('`, `', $this->fields) . '`';
        $sql = "
            SELECT {$fields}
            FROM `{static::TABLE}`
            WHERE `{static::ID}` = :id
        ";
        // prepare and execute query
        // assume we read that data into $data
        return new static($data, $pdo);
    }

    public static deleteById(int $id, PDO $pdo)
    {
        $sql = "
            DELETE FROM {static::TABLE}
            WHERE {static::ID} = ?
        ";
        // prepare and execute query
    }

    // perhaps some method to create new object from array of data
    // that you want each inheriting class to implement
    abstract static function create(array $data, PDO $pdo);

    // perhaps some other methods you want classes to implement in operate
    // against concrete objects
    abstract function update();

    // etc.
}

With an inheriting User class perhaps looking like:

class User inherits Model
{
    const TABLE = 'users';
    const ID = 'id',
    protected $fields = [
        'name',
        'email',
        'password',
        // etc.
    ];
    // you could make these private with getters/setters if more appropriate
    public $name;
    public $email;
    public $password;
    // etc.

    public function __construct(array $data, PDO $pdo)
    {
        // shown without validation for demo purposes
        foreach($array as $key => $value) {
            $this->{$key} = $value;
        }
        $this->pdo = $pdo;
    }

    public static function create(array $data, PDO $pdo)
    {
        // insert record and upon success
        // return new instance
        return new User($data, $pdo);
    }

    // this implementation could possibly live in base class, but showing
    // here for demo purposes
    public function update()
    {
        $fields = '';
        foreach($this->fields as $field) {
            $fields .= "`{$field}` = :{$field}";
        }
        $sql = "
            UPDATE `{self::TABLE}`
            SET {$fields}
            WHERE {self::ID} = :id
        ";
        // prepare query
        // execute prepared statement with bound values from object properties
        // return success/failure boolean   
    }

    // etc.
}

This is really just here for informational purposes. I would suggest you look at common frameworks out there to drawn further inspiration around structuring your model classes.


I am concerned that you seem to be looking at a JSON-based REST API, but you have made no provisions at all for reading JSON as input. Your API seems to assume a form-encoded content type being sent to where $_GET, $_POST would be populated as it is in a standard form-based web application. If a REST client is sending your GET/POST with application/json content type, PHP will not auto-populate these superglobal variables. You will need to read input manually from standard input using something like:

$json = file_get_contents('php://input');

I guess this is similar to my earlier thoughts around request/response objects. I think your framework is currently quite naive around aspects of reading and setting HTTP headers, something that can be critical when trying to build a RESTful API framework. What you will likely find is that there might need to be quite a bit of logic around this, such that building these separate classes becomes mandatory in order to encapsulate this logic and not have it scattered throughout the other classes in your framework. Again you may want to look at your favorite framework(s) for inspiration in this area.


public function main($params){
    switch($_SERVER['REQUEST_METHOD']){
        case 'GET':
            $this->get_user();
         break;
        case 'POST':
             $this->add_user();
         break;
        case 'PUT':
             $this->update_user();
         break;
        case 'DELETE':
             $this->delete_user();
         break;
    }
}

I think this approach of mapping your request to an action in the controller may not scale well over time unless you only have to simplest of database models you are trying to make available via the API.

You will inevitably find that the paradigm of one URI to one set of actions in a model will tend to break down for even the simplest applications. Let's use an example use case of an application where you have a post (like a blog) post and O to n comments that can be made against a post.

When you go to serve up the data from your API, you may find you need convenience methods to allow the caller to get the full post as well as all comments with one API call, such that client doesn't need to get the post data and then make a series of follow-up API calls to get the data for each comment related to the post.

So you might decide to expose an endpoint like:

/posts/{id}/comments

How is that handled with your routing mechanism?

Oftentimes you may need to look at a combination of request method and URI (not just request method) to determine an appropriate controller and/or action on controller to call.

This ties back to my suggestion on having a request object (which would store request method) that can be passed to a dedicated router class. That router should be able to inspect the combination of URI and request method to determine what controller should be invoked.

Some routing strategies may have you just looking at first URI segment to route to a primary controller, which may then invoke subcontrollers to handle more complex routing/method requests, where some routing strategies may have router actually hold all controller/subcontroller mapping logic.

Which approach to take may depend on how you want to use the framework and your personal preferences, but just keep in mind that you will inevitably encounter this kind of use case and likely need to build out more robust routing logic.

\$\endgroup\$
  • \$\begingroup\$ Do you mind if i ask you a few more questions?And if i may tag you in the a new question?I'l try to include the things you have mentioned.Really explained in detail!Thanks a lot! \$\endgroup\$ – DaAmidza May 30 '17 at 19:27
  • \$\begingroup\$ @Arslan.H You are always welcome to open new questions. Feel free to tag me in comments \$\endgroup\$ – Mike Brant May 30 '17 at 22:16
  • \$\begingroup\$ Here is the updated version with the things that you have suggested.(github.com/rarslan/PHP-MVC-RESTful-API)I wasn't able to include DI..... \$\endgroup\$ – DaAmidza Jun 3 '17 at 11:01
  • \$\begingroup\$ github.com/rarslan/PHP-MVC-RESTful-API \$\endgroup\$ – DaAmidza Jun 3 '17 at 11:46
  • \$\begingroup\$ @Arslan.H feel free to create an iterative code review with your new code. \$\endgroup\$ – Mike Brant Jun 4 '17 at 3:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.