1

Recently i've been learning how to implement an MVC structure in my applications. I'm planning to use a PHP framework in the near future, but for now it's just pure PHP. I've learned that controllers should be 'skinny' and models should be 'fat', but can't seem to understand how this can go hand in hand with clean URLs.

At the company where I'm doing my internship I'm developing a web-based application to perform CRUD actions on 3 different tables in a database. Each of the tables has its own specific fields, which means that for each of those tables there are 4 views needed (1 for each CRUD action).

I want to make use of clean URL's, so right now i have 1 (only one) controller (index.php) which splits up the URL in segments and analyzes them, calling the specific methods and views for each segment, or (if needed) redirecting the user to a correct URL.

Right now the controller contains close to 1000 lines of code. I always read that controllers should be 'skinny', and see people talking about multiple controllers in one application. I can't seem to understand how to implement this in combination with clean URLs...

Any help would be much appreciated. At the company I'm doing my internship at there's just a web designer, so I can't ask him any programming-related questions...

Below is a basic representation of my controller (index.php)

// Check if user is logged in
if (isset($_SESSION['username']) AND isset($_SESSION['loggedIn'])) {

    // Get URL from $_SERVER
    $url = $_SERVER['REQUEST_URI'];

    // Split URL and assign values to $url
    $url = trim($url, "/");
    $url = explode('/', $url);

    // Remove first url segment (index.php) for convenience
    array_shift($url);

    // Check if 1st URL segment (category) is set
    if (isset($url[1])) {
        $category = $url[1];

        // Check if category is 'apples'
        if ($category == 'apples') {

            // Check if 2nd URL segment (action) is set
            if (isset($url[2])) {
                $action = $url[2];

                // Check if action is 'add'
                if ($action == 'add') {
                    // Calls to Model
                    // Include Presentation (form to add new record to 'apples' category)
                }

                // Check if action is 'view'
                elseif ($action == 'view') {
                    // Calls to Model
                    // Include Presentation (list of all records in the 'apples' table)
                }

                // Check if action is 'edit'
                elseif ($action == 'edit') {

                    // Check if 3d URL segment (id) is set
                    if (isset($url[3])) {
                        $id = $url[3];

                        // Calls to Model
                        // Include Presentation (form to edit record of the 'apples' category);
                    }

                    // If 3d URL segment (id) is not set then redirect
                    else {
                        header('Location: index.php/$category/view');
                    }
                }

                // Check if action is 'delete'
                elseif ($action == 'delete') {

                    // Check if 3d URL segment (id) is set
                    if (isset($url[3])) {
                        $id = $url[3];

                        // Calls to Model
                        // Include Presentation (form to edit record of the 'apples' category);
                    }

                    // If 3d URL segment (id) is not set then redirect
                    else {
                        header('Location: index.php/$category/view');
                    }
                }

                // If 2nd URL segment (action) is invalid then assume user wants to view and redirect
                else {
                    header("Location: index.php/$category/view");   
                }
            } 

            // If 2nd URL segment (category) is not set then assume user wants to view and redirect
            else {
                header("Location: index.php/$category/view");
            }

        }

        // Check if category is 'pineapples'
        elseif ($category == 'pineapples') {
            // same here as in 'apples' code block
        }

        // Check if category is 'pears'
        elseif ($category == 'pears') {
            // same here as in 'apples' code block
        }

        // If 1st URL segment (category) is invalid then redirect to index.php
        else {
            header('Location: index.php')
        }
    }

    // If 1st URL segment (category) is not set then show category overview
    else {
        include 'presentation/category_overview.php';
    }
}

// If user is not logged in then check if login form got submitted
elseif($_POST['formSubmit'] == 'submit') {
    // Calls to Model (form and user credentials validation)
    // Include Presentation (category overview)
}

// If user is not logged in and did not submit login form then include view (login form)
else {
    include 'presentation/login.php';
}
user1440560
  • 163
  • 1
  • 7
  • 1
    Well, at first glance it seems like you've done a lot of commenting. This is good. But I'd caution you from obvious comments like "Check if category is 'apples'" before a line of `if ($category == 'apples') {`. Also, if you look at your SLOC (source lines of code - the lines that aren't comments) I think you'd see it was much less than 1000. – Bailey Parker Jul 18 '12 at 00:08

3 Answers3

1

I think there has happened some misunderstanding in regard to what controllers actually does.

Splitting the URL is not the controllers responsibility. It is usually done be separate routing mechanism. Sometimes known as front controller. The is usually implemented using regular expression (sometimes, with assistance of APC).

In web MVC-inspired patterns you basically have two options:

  1. classical controller, which is responsible for changing the state of model layer and view;
  2. page controllers, which initializes model layer structures, creates view, binds model layer elements to said view and then renders it

P.S. As for the rest of MVC, you might find useful this and this comment.

Community
  • 1
  • 1
tereško
  • 58,060
  • 25
  • 98
  • 150
0

That's not how MVC is implemented!

  • First of all you must have a class for each controller, model or view.
  • Each class must be in a separate file.
  • Files must be grouped in separate directories according to their class type.

consider you have an order table then you have:

/index.php
/controller/order.php
/model/order.php
/view/order/grid.php
/view/order/form.php

in index you must just route the program. you must instantiate the controller, call the demanded function. the controller must get the required object from model and put them in a view and return the view to index. index will call render on view and it will output the result.

Ali
  • 21,572
  • 15
  • 83
  • 95
0

In addition to implementing a front controller pattern, a true MVC application should exhibit all the qualities of good object-oriented design: modularity, loose coupling, cohesion, separation of concerns, DRY, etc.

So obviously all of your models, views and controllers are going to have a lot of shared functionality that should be encapsulated in a base model/view/controller class. And these base classes (as well as those for behaviours, helpers, components, etc.) plus necessary utility libraries (routing engine, DAL, etc.) will form the basis of your MVC framework.

Your framework libraries that are reused for every application should be distinguished from the application-specific code built on top of the framework for each individual application. And it's your application-specific models and controllers that are being referred to in "skinny controller, fat model".

Your base controller has to contain code to coordinate application flow, instantiate models, trigger model validation, instantiate views, trigger callbacks, pass data between models and views, perform session handling, implement scaffolding, etc. So naturally it's going to be somewhat big.

But your application controllers gain all these functions just by inheriting from the base controller class, allowing them to contain only the minimal code needed to define controller settings and actions.

Lèse majesté
  • 7,923
  • 2
  • 33
  • 44