📜 ⬆️ ⬇️

Simple MVC Applications

I would like to touch on the topic of proper PHP application architecture. The article will focus on the MVC design pattern. Write about MVC I was inspired by the fact that understanding this pattern is key to the development of a PHP programmer. So if you are new and want to start writing programs correctly, read on.

Theory


In short, the MVC (model view controller) is a way of writing a program, when the code responsible for outputting data is written in one place, and the code that forms this data is written in another place. The result is that if you need to correct the output, you immediately know where to look. Now all popular frameworks use this architecture.

It is also worth mentioning the fact that there are two camps: one writes logic in controllers, the second in models. In those frameworks that I know (yii, laravel), logic is written in controllers, and models are just ORM instances. By the way, in yii it is written in the manual that it is necessary to write logic in models, and then they write it in controllers in the examples, quite funny.

Determined with business logic, we write in controllers. Also in the controller's methods there is a call to the models, which are essentially ORM instances in order to get data from the database over which they will make changes. The end result is sent to the views. Types contain HTML markup and small PHP code inserts for crawling, formatting and displaying data.
')
You can also mention that there are two types of MVC Page Controller and Front Controller. The Page Controller is rarely used, its approach is to use multiple entry points (requests to the site are made to several files), and within each entry point there is a display code. We will write Front Controller with one entry point.

Practice


We need to start by setting up the server to redirect to our single entry point. If we have apache, then in the .htaccess file we write the following

RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule .* index.php [L] 

Further in the folder of our project we create a folder that can be called the App for example. It will contain the following content.

Our Service Locator. App.php file


 <?php class App { public static $router; public static $db; public static $kernel; public static function init() { spl_autoload_register(['static','loadClass']); static::bootstrap(); set_exception_handler(['App','handleException']); } public static function bootstrap() { static::$router = new App\Router(); static::$kernel = new App\Kernel(); static::$db = new App\Db(); } public static function loadClass ($className) { $className = str_replace('\\', DIRECTORY_SEPARATOR, $className); require_once ROOTPATH.DIRECTORY_SEPARATOR.$className.'.php'; } public function handleException (Throwable $e) { if($e instanceof \App\Exceptions\InvalidRouteException) { echo static::$kernel->launchAction('Error', 'error404', [$e]); }else{ echo static::$kernel->launchAction('Error', 'error500', [$e]); } } } 

Service locator is needed to store the components of our application in it. Since we have a simple mvc application , we do not use the registry pattern (as done in yii, for example). And we simply save the application components to static properties in order to access them easier. Another App registers class autoloader and exception handler.

Router. Router.php file


 <?php namespace App; class Router { public function resolve () { if(($pos = strpos($_SERVER['REQUEST_URI'], '?')) !== false){ $route = substr($_SERVER['REQUEST_URI'], 0, $pos); } $route = is_null($route) ? $_SERVER['REQUEST_URI'] : $route; $route = explode('/', $route); array_shift($route); $result[0] = array_shift($route); $result[1] = array_shift($route); $result[2] = $route; return $result; } } 

In a simple mvc application, the router contains only one method. It parses the address of $ _SERVER ['REQUEST_URI']. I have not yet said that all our links to the pages of the site should be www.ourwebsite.com/%controller%/%action% , where% controller% is the name of the controller file, and% action% is the name of the controller method that will be called .

Db.php file


 <?php namespace App; use App; class Db { public $pdo; public function __construct() { $settings = $this->getPDOSettings(); $this->pdo = new \PDO($settings['dsn'], $settings['user'], $settings['pass'], null); } protected function getPDOSettings() { $config = include ROOTPATH.DIRECTORY_SEPARATOR.'Config'.DIRECTORY_SEPARATOR.'Db.php'; $result['dsn'] = "{$config['type']}:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}"; $result['user'] = $config['user']; $result['pass'] = $config['pass']; return $result; } public function execute($query, array $params=null) { if(is_null($params)){ $stmt = $this->pdo->query($query); return $stmt->fetchAll(); } $stmt = $this->pdo->prepare($query); $stmt->execute($params); return $stmt->fetchAll(); } } 

This class uses the config file that is returned by the array.

Config / Db.php file


 <?php return [ 'type' => 'mysql', 'host' => 'localhost', 'dbname' => 'gotlib', 'charset' => 'utf8', 'user' => 'root', 'pass' => '' ]; 

Our core. Kernel.php file


 <?php namespace App; use App; class Kernel { public $defaultControllerName = 'Home'; public $defaultActionName = "index"; public function launch() { list($controllerName, $actionName, $params) = App::$router->resolve(); echo $this->launchAction($controllerName, $actionName, $params); } public function launchAction($controllerName, $actionName, $params) { $controllerName = empty($controllerName) ? $this->defaultControllerName : ucfirst($controllerName); if(!file_exists(ROOTPATH.DIRECTORY_SEPARATOR.'Controllers'.DIRECTORY_SEPARATOR.$controllerName.'.php')){ throw new \App\Exceptions\InvalidRouteException(); } require_once ROOTPATH.DIRECTORY_SEPARATOR.'Controllers'.DIRECTORY_SEPARATOR.$controllerName.'.php'; if(!class_exists("\\Controllers\\".ucfirst($controllerName))){ throw new \App\Exceptions\InvalidRouteException(); } $controllerName = "\\Controllers\\".ucfirst($controllerName); $controller = new $controllerName; $actionName = empty($actionName) ? $this->defaultActionName : $actionName; if (!method_exists($controller, $actionName)){ throw new \App\Exceptions\InvalidRouteException(); } return $controller->$actionName($params); } } 

The kernel accesses the router, and then starts the controller actions. Another kernel can throw an exception if there is no controller or method needed.

Controller.php file


We also need to create a base class for our controllers, then to inherit from it. Inheriting methods is necessary so that you can render (form a conclusion) views. Rendering methods support the use of layouts - templates that contain components common to all types, for example, footer and heder.

 <?php namespace App; use App; class Controller { public $layoutFile = 'Views/Layout.php'; public function renderLayout ($body) { ob_start(); require ROOTPATH.DIRECTORY_SEPARATOR.'Views'.DIRECTORY_SEPARATOR.'Layout'.DIRECTORY_SEPARATOR."Layout.php"; return ob_get_clean(); } public function render ($viewName, array $params = []) { $viewFile = ROOTPATH.DIRECTORY_SEPARATOR.'Views'.DIRECTORY_SEPARATOR.$viewName.'.php'; extract($params); ob_start(); require $viewFile; $body = ob_get_clean(); ob_end_clean(); if (defined(NO_LAYOUT)){ return $body; } return $this->renderLayout($body); } } 

File index.php


Do not forget to create an index file in the root:

 <?php define('ROOTPATH', __DIR__); require __DIR__.'/App/App.php'; App::init(); App::$kernel->launch(); 

Create controllers and views


Working with our application (you can even say a miniframe) is now reduced to creating views and controllers. An example controller is as follows (in the Controllers folder):

 <?php namespace Controllers; class Home extends \App\Controller { public function index () { return $this->render('Home'); } } 

Example view (in the Views folder):

 <img src="Img/my_photo.jpeg" alt="my_photo" id="my_photo"> <h1></h1> <p>     - -.</p>  :<br> 8-912-641-3462<br> goootlib@gmail.com 

In the Views / Layout folder we create Layout.php:

 <!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title> </title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="/Css/style_layout.css" rel="stylesheet" type="text/css"> <link href="https://fonts.googleapis.com/css?family=Roboto+Condensed" rel="stylesheet"> </head> <body> <header> <nav> <a id="about_button" href="/home"> </a> <a id="portfolio_button" href="/portfolio"></a> <a id="blog_button" href="/blog"></a> </nav> </header> <div class="main"> <?= $body ?> </div> </body> </html> 

Conclusion


If you decide to use the code of the application that I described, do not forget to create an Error controller with the error404 and error500 methods. The class for working with DB, described by me, is suitable for writing queries with hands, instead of it you can connect ORM and you will be real models.

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


All Articles