PHP: Implementing MVC design pattern

Based in the symfony docs we will try to summarize the implementation of MVC with PHP.

In a previous post we explained the MVC design pattern, and now we’re going to see how to implement it in PHP.

To implement this pattern and try to understand it a little more we’ll see how to transform a basic PHP application in a MVC app. In this case, we’ll build a posts list in a blog. To show a post list from the database in the PHP spaghetti way the script would looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// Connecting, selecting database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
 
// Performing SQL query
$result = mysql_query('SELECT date, title FROM post', $link);
?>

<html>
    <head>
        <title>List of Posts</title>
    </head>
    <body>
        <h1>List of Posts</h1>
        <table>
        <tr><th>Date</th><th>Title</th></tr>
<?php
// Printing results in HTML
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
    echo "<tr>";
    printf("<td> %s </td>", $row['date']);
    printf("<td> %s </td>", $row['title']);
    echo "";
}
?>

        </table>
    </body>
</html>
<?php
// Closing connection
mysql_close($link);
?>

The above script is easy to write, fast to execute, but hard to maintain; some of the trouble we can find is the absence of errors checking (what happen if the db connection fails?), the HTML and the PHP code are all mixed and the script is tied to the MySQL database.

Splitting the presentation out

The echo and printf calls make the code hard to read. To edit the HTML is a pain in the above script, so lets split the code in two sides. First, the PHP code with all the business logic in a Controller (index.php):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// Connecting, selecting database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
 
// Performing SQL query
$result = mysql_query('SELECT date, title FROM post', $link);
 
// Filling up the array for the view
$posts = array();
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
    $posts[] = $row;
}
 
// Closing connection
mysql_close($link);
 
// Requiring the view
require('view.php');

And, the View script (view.php):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<html>
    <head>
        <title>List of Posts</title>
    </head>
    <body>
        <h1>List of Posts</h1>
        <table>
            <tr><th>Date</th><th>Title</th></tr>
<?php
foreach ($posts as $post):
?>
            <tr>
                <td>
                    <?php echo $post['date']; ?>
                </td>
                <td>
                    <?php echo $post['title']; ?>
                </td>
            </tr>
<?php
endforeach;
?>

        </table>
    </body>

To determine if the view is clean enough, a good rule is to have the minimum amount of PHP code in a way that the view could be understand it by a designer without PHP knowledge. The most common and proper PHP syntax in the view are echo, if/endif and foreach/endforeach. Also, there should not be PHP code printing HTML tags.

Hence, all the logic is moved into the controller (containing only PHP, with no HTML)
Por ende, toda la lógica es movida al controlador, y contiene solo código PHP, sin HTML. Como algo importante, deberías imaginar que el mismo controlador debería poder ser usado para diferentes presentaciones (vistas), tales como paginas HTML, archivos PDF, o en una estructura XML.

Isolating the data manipulation

Most of the controller code is dedicated to the data manipulation. But, what happen if we need to list the post for another controller, for instance, one which has a feed RSS output, or if we want to change the data model, like change the table name from post to weblog_post, or we have to change the database engine from MySQL to PostgreSQL. To be able to do all those things we have to remove the data manipulation from the controller and put that logic in the model (model.php):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function getAllPosts()
{
    // Connecting, selecting database
    $link = mysql_connect('localhost', 'myuser', 'mypassword');
    mysql_select_db('blog_db', $link);
 
    // Performing SQL query
    $result = mysql_query('SELECT date, title FROM post', $link);
 
    // Filling up the array
    $posts = array();
    while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
        $posts[] = $row;
    }
 
    // Closing connection
    mysql_close($link);
 
    return $posts;
}

And modifying the controller (index.php) to include the model:

1
2
3
4
5
6
7
8
9
<?php
// Requiring the model
require_once('model.php');
 
// Retrieving the list of posts
$posts = getAllPosts();
 
// Requiring the view
require('view.php');

In this way we have a really readable controller since if unique task is to get the data from the model and pass it to the view. In more complex apps, the controller also handles the request, the user session, the authentication and much more functionalities.

Source: Symfony documentation

3 Comments

Leave a Reply to leo Cancel reply