📜 ⬆️ ⬇️

MVC again

Hello haboobschestvu

Once I came across an article with a habr link . After trying that system, I came to the conclusion that it had many flaws, and the translation of the original article was not fully present. Yes, the article is old-fashioned, but at the same time I wanted to write my own small framework for creating simple and medium complexity sites. So as to carry with you 2-3 megabytes of a serious framework for creating a business card site I consider blasphemy (though not always).

In general, I decided to correct the glaring flaws and share the result with haberdalers.

So what was refined?
')
1. Got rid of using the Registry class. Why do we need it if PHP5 has such wonderful methods as __set and __get?

2. Instead of PDO, I wrote a class called by me as Active Records, which allows you to communicate with the database through a layer. I will not give the source code, since the quality of the code is not so hot there and needs refactoring. But if need be - add.

3. The Template class was rewritten a bit, the ability to make nested templates was added. Now it looks like this:

<? php

class Template {

private $ vars = array ( ) ;

function __set ( $ varname , $ value ) {
$ this -> vars [ $ varname ] = $ value ;
return true ;
}

function show ( $ name , $ data = false , $ display = true ) {
$ path = site_path . 'views' . DIRSEP . $ name . '.php' ;

if ( ! file_exists ( $ path ) ) {
trigger_error ( 'Template `' . $ name . '` does not exist.' , E_USER_NOTICE ) ;
return false ;
}

if ( $ data ) {
// fill with data
if ( is_array ( $ data ) ) {
foreach ( $ data as $ key => $ value ) {
$ this -> __set ( $ key , $ value ) ;
}
}
}

extract ( $ this -> vars ) ;

if ( $ display ) {
include ( $ path ) ;
} else {
if ( is_file ( $ path ) ) {
ob_start ( ) ;
include $ path ;
$ contents = ob_get_contents ( ) ;
ob_end_clean ( ) ;
return $ contents ;
}
return false ;
}
}
}

?>


4. The router was changed, support for several parameters in the URL appeared. Although I suspect that the router is still with flaws, which I will eventually eliminate. But while it works :)

<? php

class Router {

public $ path ;
private $ args = array ( ) ;

function setPath ( $ path ) {
$ path = trim ( $ path , '/ \\' ) ;
$ path . = DIRSEP ;

if ( ! is_dir ( $ path ) ) {
throw new Exception ( 'Invalid controller path: `' . $ path . '`' ) ;
}
$ this -> path = $ path ;
}

function delegate ( ) {
// analize path
$ this -> getController ( $ file , $ controller , $ action , $ args ) ;
// is file available?
if ( ! is_readable ( $ file ) ) {
trigger_error ( 'File `' . $ file . '` not found' , E_USER_ERROR ) ;
}
include ( $ file ) ;

// create controller
$ class = 'Controller_' . $ controller ;
$ controller = new $ class ( ) ;

// action is available in object?
if ( ! is_callable ( array ( $ controller , $ action ) ) ) {
trigger_error ( 'No method `' . $ action . '` was found in class' , E_USER_ERROR ) ;
}
// call action
call_user_func_array ( array ( $ controller , $ action ) , $ args ) ;
}

private function getController ( & $ file , & $ controller , & $ action , & $ args ) {
$ route = ( empty ( $ _GET [ 'route' ] ) ) ? 'index' : $ _GET [ 'route' ] ;
// split called url
$ route = trim ( $ route , '/ \\' ) ;
$ parts = explode ( '/' , $ route ) ;

// find right controller
$ cmd_path = $ this -> path ;

// set default controller name (will be found if found another)
$ controller = 'index' ;

foreach ( $ parts as $ part ) {
$ fullpath = $ cmd_path . $ part ;
// is dir?
if ( is_dir ( $ fullpath ) ) {
$ cmd_path . = $ part . DIRSEP ;
array_shift ( $ parts ) ;
continue ;
}

// is file?
if ( is_file ( $ fullpath . '.php' ) ) {
$ controller = $ part ;
array_shift ( $ parts ) ;
break ;
}
}

// get action method in controller
$ action = ( isset ( $ parts [ 0 ] ) && ! is_numeric ( $ parts [ 0 ] ) ) ? array_shift ( $ parts ) : 'index' ;
$ file = $ cmd_path . $ controller . '.php' ;
$ args = $ parts ;
}
}
?>


5. Added the Loader class, in which the model will be loaded (implemented), libraries (implemented). In the future, I think, to tie in there and the loading of helpers.

<? php

class loader {

public $ instance ;
public $ vars = array ( ) ;

function __construct ( $ parent ) {
$ this -> instance = $ parent ;
}

function model ( $ model_name ) {
$ filename = strtolower ( $ model_name ) . '.php' ;
$ file = site_path . 'models' . DIRSEP . $ filename ;
if ( ! file_exists ( $ file ) ) {
return false ;
}
include ( $ file ) ;
$ this -> instance -> $ model_name = new $ model_name ( new ActiveRecords ( ) ) ;
}

function library ( $ name ) {
$ filename = strtolower ( $ name ) . '.php' ;
$ file = site_path . 'libraries' . DIRSEP . $ filename ;
if ( ! file_exists ( $ file ) ) {
return false ;
}
include ( $ file ) ;
$ this -> instance -> $ name = new $ name ( new ActiveRecords ( ) ) ;
}
}

?>


Here, as we see, the aforementioned ActiveRecords class (which is currently singletone) is passed to any model or library. Due to this, work with the database becomes available in models and libraries (which is to be expected from the MVC concept). But there are restrictions on the names of models and libraries - it is impossible, for example, to use a model with the name load.

The very same base class of the model, from which all the subsequent ones in the project are inherited, looks like this:

<? php

class Model {

var $ db ;

function __construct ( $ db ) {
$ this -> db = $ db ;
}
}

?>


6. In view of the above changes, the base controller class, from which all subsequent controllers are inherited, now looks like this:

<? php

class Controller_Base {

public $ template ;
public $ load ;

function __construct ( ) {
$ this -> template = new Template ( ) ;
$ this -> load = new loader ( $ this ) ;
}

function __set ( $ varname , $ value ) {
$ this -> $ varname = $ value ;
return true ;
}
}

?>


Thus, the Loader and Template classes are available in each controller. And we can write similar things in code:

$ this -> load -> model ( 'links' ) ;
$ this -> links -> add ( ' new ' ) ;


Ie all the methods of the model become available in this type:

$ this -> [ model name ] -> [ method name in model ]


7. Also, small changes were added to the startup script — session initialization (later I plan to add sessions from the database), along with the __autoload function, the redirect function that is often used is added:

function redirect ( $ url )
{
Header ( "Location:" . $ Url ) ;
}


as well as a connection to the database (I'm not sure here, can it make sense to bring the connection to the database to the Active Records class?)

8. And most importantly, our main index.php file, which handles all requests. Now it looks like this:

<? php

require 'includes / start.php' ;
$ router = new Router ( ) ;
$ router -> setPath ( site_path . 'controllers' ) ;
$ router -> delegate ( ) ;

?>


The source code of the framework can be downloaded here: link . I draw your attention to the fact that here the source code is not out of the box. Only some code is given, except for the base kernel, which is required as an example of use.

I will be glad to your comments, comments and instructions.

PS And yes - the most important thing. A working example of a small resource created using a resource is a link . The service allows you to store links by category online. Written for themselves, as the existing repository for several reasons are not satisfied. In addition, it was necessary to test the kernel on something.

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


All Articles