📜 ⬆️ ⬇️

Simple MVC model implementation with template hierarchy support

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:
')


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.

Source: https://habr.com/ru/post/232089/


All Articles