This article shows an example of the implementation of the MVC model using PHP. The proposed implementation is extremely simple both for understanding and for execution. Useful features are easy extensibility and support for a hierarchy of patterns. All this allows us to put it in the basis of a fairly complex web project.
A little bit about MVC
The MVC development pattern has been discussed many times and it is unlikely to describe it in detail. For familiarization with the subject you can read:
Therefore, we only briefly mention the key components of this system:
')
- Controller (controller) - The most important component of the system. It is the set of controllers and the commands they process (actions) that determine what kind of functionality the system will have. The controller is the first element in the implementation of the business logic of the application and should determine what happened in the system and how to respond to it. In particular, the controller determines which representation (View) is needed to display the state of the system and how (Model) this data should be obtained. However, the controller, as a good commander, should not delve into how his subordinates will perform the task. His job is to choose performers and give them orders. It is important to bear in mind that even within the framework of processing one command, the controller is not tied to one view and one model. On the contrary, he can choose silt on the fly. For illustration, consider the following example: user authorization (pseudocode):
<code>
userController-> actionAuth ($ login, $ pass)
{
$ model = new userModel ();
if ($ model-> authorize ($ login, $ pass) {
$ view = "authok";
}
else {
$ view = "authfailure";
}
processView ($ view);
}
</ code>
In this case, the controller has no idea how exactly the model performs the authorization. Working with data is purely model tasks. The model only informs the controller whether processing was successful or not. Depending on this, the controller decides which page to display to the user.
- View (View) - Responsible for presenting data to the user. Submission has no idea (sorry for the pun) about how to obtain the data that he needs. It knows only where this data can be taken from and how to present it to the user. As a rule, the main source of data for presentation is a model. However, as we will see later - the model is not always able to provide data in full. It is important that several views can interact with the same model. As shown in the example, both the “authok” view and the “authfailure” view will use data from the same $ model.
- Model (model) - A very important component, but somewhat reminiscent of a scattered scientist who is able with the same interest to solve the problems of immortality and the destruction of all life. In other words, the model should be able to collect the necessary data or process incoming data. This is her horse. However, the model usually has no idea about the context in which it was called, in response to which command, what is happening in the system as a whole. Moreover, the model cannot assume whether the system will need additional data to process the command or not. Obviously, to collect all possible data for "just in case" no model is able to.
Attempting to collect all the data in one view can lead to multiple code duplication. For example, user profile data of a certain site may be needed for a variety of tasks (from editing a user’s profile to specifying a link to the author of the published material. If each model requests such data on its own, complexity and duplication of code will increase exponentially.
If the submission is able to request data in chunks, defining each time who can provide this data, the programmer’s life will be easy and cloudless, since it will be possible to concentrate on solving a separate task, without having to keep the whole project in mind. However, this implies the need for organizing hierarchical processing of views. If we can from another place of the template request the data of another controller and get a ready-made fragment of the output code (for example, already formatted html), it will become easier. However, for this we will at some point need to suspend the processing of the current thread and transfer control to another handler. The problem is that PHP copes with this task, to put it mildly, not brilliantly. The data passes through the interpreter only once and constructions like
echo('<?php echo($msg'); ?>');
will not work. An internal appeal to the interpreter will not work.
In part, the problem of block building output helps solve template engines. There are a lot of them, different in capabilities and convenience of work. However, they have one common flaw - they are an add-on to PHP and introduce their own markup language that needs to be learned. At the same time, such functionality within the framework of PHP is obviously redundant, since this language in itself provides quite good possibilities for working with templates. I think everyone had to write constructions like:
<code>
<html>
...
<? php echo ($ msg); ?>
or more complex
<table>
<? php foreach ($ array as $ val) {?>
<tr> <td> $ val </ td> </ tr>
<?}?>
</ table>
</ code>
In such constructions, PHP features as a template engine are used. By the way, designers to such inserts are without fear.
Now let's get to the practical part, and see how within the page template you can implement the insertion of a whole block, which in turn can be composed of a whole set of sub-blocks. This can be implemented in pure PHP, without using an additional markup language.
Practice
We will consider a simplified version of the system, in which such moments as the work with CNC (human-understandable URL) and the settings of the .htaccess file are omitted to ensure a single point of entry into the system. These questions are widely covered in the network and there is no point in repeating. Also, we will not consider the issues of diligent folding of the system components by catalogs, as this is essentially a personal matter for everyone. Focus on solving the problem of nesting patterns.
The system itself in action can be seen
here.The main component of the system and the main entry point is the application.php file:
<code>
<? php
class webApplication
{
protected $ dataBuf;
protected static $ _classInstance;
protected $ defaultHandler;
protected $ sefRequestParams; // To support SEF technology
private function __construct ()
{
$ this-> dataBuf = "";
$ this-> defaultController = "mainpage";
$ currentURL = $ _SERVER ['REQUEST_URI'];
$ this-> sefRequestParams = explode ("/", $ currentURL);
// It could be a good idea.
}
private function __clone ()
{
}
public function getSEFParams () // sef params app
{
return $ this-> sefRequestParams;
}
public static function getApp ()
{
if (null === self :: $ _ classInstance)
self :: $ _ classInstance = new self ();
return self :: $ _ classInstance;
}
public function handle ($ controller, $ action)
{
if (! isset ($ controller) || $ controller == "")
$ controller = $ this-> defaultController;
$ val = $ controller. '. php';
$ res = require_once ($ val);
if ($ res! = 1)
{
echo ("requested controller not found!");
return 0;
}
$ controlClass = new $ controller ();
if ($ controlClass == NULL)
{
echo ("Controller initialization error!");
return 0;
}
ob_start ();
$ controlClass-> dispatchAction ($ action, & $ this);
$ this-> dataBuf = ob_get_contents ();
ob_end_clean ();
echo ($ this-> dataBuf);
return 1;
}
public function handleHttp ()
{
$ controller = $ _ REQUEST ['controller'];
$ action = "";
if (! isset ($ controller) || $ controller == "") // Assume we're using SEF technics
{
$ controller = $ this-> sefRequestParams [0];
$ action = $ this-> sefRequestParams [1];
}
else
{
$ action = $ _ REQUEST ['action'];
}
return $ this-> handle ($ controller, $ action);
}
}
$ app = webApplication :: getApp ();
$ app-> HandleHttp ();
?>
</ code>
The webApplication class is the base entry point to the system. As can be seen from the presented code, this class implements the
Singleton pattern. As part of the system, we always have an instance of this class, and there is always only one. This property makes it extremely convenient for storing all global system data. In this case, the import of system settings and their use are omitted, as they are easy to implement independently, depending on their needs.
The key function of the class is the handle ($ controller, $ action) method. This function accepts the name of the controller (the first parameter) and the name of the action (action) to be executed. In accordance with the practice of good programming, we assume that the name of the controller class coincides with the name of the file in which it is stored. Of course, if desired, the string $ val = $ controller. '. Php'; can be modified: $ val = CONTROLLER_PATH. $ controller. '. php'
What is important is that this function allows you to call the desired controller by its name. In order for controllers to interact correctly with the system, they must implement the iHandler interface, which is defined as follows:
<code>
ihandler.php:
<? php
interface iHandler
{
public function dispatchAction ($ action, & $ app);
public function actionDefault (& $ app);
}
?>
</ code>
This interface introduces two required methods: dispatchAction ($ action, & $ app) and public function actionDefault (& $ app);
Both methods take a reference to the webApplication class (the & $ app parameter) as one of the parameters. This is done in order to avoid the PHP habit of making a complete copy of the object. Yes, and having the desired class as a parameter is somewhat more convenient than writing global $ app; $ app-> ...
Let's return to our example. In our case, the welcome (welcome.php) controller will be invoked:
<code>
<? php
require_once ("ihandler.php");
class welcome implements iHandler
{
public function __construct ()
{
// Nothing to do here
}
public function dispatchAction ($ action, & $ app)
{
$ this-> actionDefault (& $ app);
}
public function actionDefault (& $ app)
{
include ("welcome.html");
}
}
?>
</ code>
This controller is written strictly in the framework of the example and we can assume that it does nothing at all. It only loads a certain HTML file. Where, then, do the data come from? It can be assumed that the downloaded file is a template and somehow requests them. Is it so? See the code:
<code>
<html>
<head>
<meta http-equiv = "Content-Type" content = "text / html; charset = utf-8">
<title> Template handler output test page </ title>
</ head>
<body>
<p> Test page for template handler </ p>
<h3> Starting the test </ h3>
<? php $ app-> handle ("hello", "say");?>
</ body>
</ html>
</ code>
And this is true! .. We are looking at the line <? Php $ app-> handle ("hello", "say");?>. Here he is a lion! We called our webApplication class and asked to call the controller we need. Moreover, the system will provide the preparation and insertion of the HTML we need automatically. No rows returned. And we used the same function handle, which was analyzed above. We ask to call the hello controller, which is obviously located in hello.php
<code>
<? php
require_once ("ihandler.php");
class hello implements iHandler
{
public function __construct ()
{
// Nothing to do here
}
public function dispatchAction ($ action, & $ app)
{
switch ($ action)
{
case 'say':
$ this-> actionSay (& $ app);
break;
default:
$ this-> actionDefault (& $ app);
break;
}
}
public function actionSay (& $ app)
{
require_once ("saymodel.php");
$ model = new sayModel ();
$ model-> prepareString ($ _ REQUEST ['name']);
include ("hello.tpl");
}
public function actionDefault (& $ app)
{
// Nothing to do by default
}
}
?>
</ code>
In this controller, we have implemented a method that is responsible for handling the “say” action - actionSay.
This method performs a typical sequence of actions for the MVC model: it creates a model, passes data to it for processing, loads the view.
First, look at the model (this is just an example, so it is extremely simple).
<code>
<? php
class sayModel
{
public $ msg;
public function __construct ()
{
$ this-> msg = "";
}
public function prepareString ($ name)
{
$ this-> msg = "Hello $ name!";
}
}
?>
</ code>
It is clear that the real model will be much more difficult. This model provides data in the form of an accessible (public) string variable $ msg. How does the hello.tplb view loaded by the controller use this data? Very simple:
hello.tpl
<code>
<p color = "# ff0000"> <? php echo ($ model-> msg); ?> </ p>
</ code>
As you can see, this view is just a fragment of HTML code, with the insertion of PHP that accesses the model data. This data will go to the stream and after processing by the PHP interpreter will get to the right place of the first view. It is clear that in general, the level of nesting and the number of controller calls is not limited.
Everything! We implemented a multi-level template system using the MVC model for each of them. The proposed system can serve as a good skeleton for the implementation of complex applications.