├── .gitignore
├── .htaccess
├── README.md
├── core
├── Orm
│ └── Model.php
├── Routing
│ └── Router.php
├── View
│ └── Views.php
└── autoloader.php
├── public
├── .htaccess
└── index.php
├── resources
└── views
│ ├── products.edit.html.php
│ ├── products.index.html.php
│ ├── products.new.html.php
│ └── web.index.html.php
└── src
├── Controller
├── ProductsController.php
└── WebController.php
└── Model
└── ProductModel.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | RewriteEngine On
3 | RewriteCond %{REQUEST_URI} !^/public/
4 | RewriteRule ^(.*)$ /public/$1 [L,QSA]
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Proposta de MVC 01
2 |
3 | Eu tenho dentro de mim a vondade meio subconsciente de criar um MVC bem simples de se usar.
4 |
5 | ### O que é MVC?
6 |
7 | MVC é um forma de estruturar seu Código. Principalmente quando se trata de uma Aplicação dentro de um servidor, porque essa estrutura é capaz de agilizar o desenvolvimento do seu sistema separando as REGRAS DE NEGÓCIO, FORMAS DE VISUALIZAÇÃO e REGRAS DE ROTAS.
8 |
9 | ## Como estruturei meu MVC
10 |
11 | Na index deixei o `autoloader` para puxar os arquivos de qualquer classe que eu chamasse.
12 |
13 | ```php
14 | spl_autoload_register(function ($className) { require_once "$className.php"; });
15 | ```
16 |
17 | ### 2. core\Router\Router
18 |
19 | Criei uma maneira simples de registrar a rota e o verbo e para qual controller ele mandaria.
20 |
21 | ```php
22 | $router->register('/', 'get', 'App\Controller\ProductsController', 'index');
23 | ```
24 |
25 | As rotas também podem ser criadas passando um parâmetro na URL.
26 |
27 | ```php
28 | $router->register('/produto/:id', 'get', 'App\Controller\ProductsController', 'show');
29 | ```
30 |
31 | O primeiro argumento é a Rota.
32 | O segundo argumento é o Verbo.
33 | O terceiro argumento é a Classe do Controller.
34 | O quarto argumento é o Método do Controller. Esse é o que será acionado quando chamarem aquela Rota.
35 |
36 | ### 3. Controller
37 |
38 | O Controller ficou super simples também, basta criar um arquivo com o mesmo nome da Classe e usar as funções.
39 |
40 | ```php
41 | class App\Controller\ProductsController {
42 | function index () {}
43 | }
44 | ```
45 |
46 | Quaso for passado um argumento na rota ele pode ser usado pegando diretamente
47 | dos argumentos da função.
48 |
49 | ```php
50 | class App\Controller\ProductsController {
51 | function show ($id) {}
52 | }
53 | ```
54 |
55 | 4. core\View\Views
56 |
57 | Quando você chamar um Método do Controller, mesmo que não faça interações com o banco de dados, ele pode exibir uma tela ou redirecionar para outra. Achei conveniente colocar essas duas funcionalidades dentro de uma Classe chamada core\View\Views.
58 |
59 | Para redirecionar, basta:
60 |
61 | ```php
62 | core\View\Views::redirect('/other/route');
63 | ```
64 |
65 | Para exibir uma tela, basta:
66 |
67 | ```php
68 | core\View\Views::render('filename', get_defined_vars());
69 | ```
70 |
71 | O `get_defined_vars` pega todas as variáveis declaradas dentro da função e transfere para dentro do arquivo php
72 |
73 | 5. Model
74 |
75 | O Model é uma classe que contém informações sobre como salvar os arquivos dentro do Banco de Dados, e também outras funcionalidades e regras de negócio.
76 |
77 | Diferente dos Controllers as Classes de Model precisam extender a Classe Model.
78 |
79 | ```php
80 | class UserModel extends Model {}
81 | ```
82 |
83 | É preciso também registrar sua tabela no banco de dados, seus atributos e quais atributos podem ser alterados pelo usuários do sistema. É necessário para funcionar.
84 |
85 | ```php
86 | class UserModel extends Model {
87 | static public $table = 'users';
88 |
89 | public $id;
90 | public $name;
91 |
92 | protected static $permited_params = ['name'];
93 | }
94 | ```
95 |
96 | Podem ser criadas outras funcões a partir dai, que serão usadas pelos controllers em tempo de execução. Por exemplo a função `firstname` no caso da classe UserModel.
97 |
98 | ```php
99 | function firstname() {
100 | return explode(" ", $this->name)[0];
101 | }
102 | ```
103 |
104 | Para utilizar o model em tempo de execução existem as seguintes funções:
105 |
106 | ```php
107 | # Array com Instâncias
108 | $users = UserModel::all();
109 | $users = UserModel::where('name = ?', 'Joe');
110 |
111 | # Apenas Instância
112 | $user = UserModel::new(['name' => 'Joe']);
113 | $user = UserModel::find(1);
114 | $user->update(['name' => 'John Doe']);
115 | UserModel::destroy(1);
116 | ```
117 |
118 | # Coias para futuras versões
119 |
120 | - templates e partials
121 | - verificar se apenas controller são chamados no registro de rotas
122 | - separar models, controllers e views em pastas (verificar se spl_autoloader tem mais argumentos que só o ClassName)
123 |
--------------------------------------------------------------------------------
/core/Orm/Model.php:
--------------------------------------------------------------------------------
1 | query($query));
14 | }
15 |
16 | static function where ($where, $params) {
17 | $table = static::$table;
18 |
19 | $query = "SELECT * FROM $table WHERE $where;";
20 |
21 | $stmt = self::instance()->prepare($query);
22 |
23 | $s = str_repeat('s', count($params));
24 |
25 | $stmt->bind_param($s, ...$params);
26 |
27 | $stmt->execute();
28 |
29 | return self::convertResult($stmt->get_result());
30 | }
31 |
32 | function update ($params) {
33 | $filterParams = self::filterParams($params);
34 |
35 | $query = self::buildUpdateQuery($filterParams);
36 |
37 | $stmt = self::instance()->prepare($query);
38 |
39 | $s = str_repeat('s', count(array_keys($filterParams)) + 1);
40 |
41 | $stmt->bind_param($s, ...array_merge(array_values($filterParams), [$this->id]));
42 |
43 | $stmt->execute();
44 | }
45 |
46 | static function new ($params) {
47 | $filterParams = self::filterParams($params);
48 |
49 | $query = self::buildInsertQuery($filterParams);
50 |
51 | $stmt = self::instance()->prepare($query);
52 |
53 | $s = str_repeat('s', count(array_keys($filterParams)));
54 |
55 | $stmt->bind_param($s, ...array_values($filterParams));
56 |
57 | $stmt->execute();
58 | }
59 |
60 | static function destroy ($id) {
61 | $table = static::$table;
62 | $query = "DELETE FROM $table WHERE id = $id;";
63 | self::instance()->query($query);
64 | }
65 |
66 | static function find ($id) {
67 | $table = static::$table;
68 | $query = "SELECT * FROM $table WHERE id = $id;";
69 | return self::convertSingleResult(self::instance()->query($query));
70 | }
71 |
72 | # other functions
73 |
74 | static function filterParams ($params) {
75 | $permited = array_flip(static::$permited_params);
76 | return array_intersect_key($params, $permited);
77 | }
78 |
79 | static function buildInsertQuery ($filterParams) {
80 | $table = static::$table;
81 | $keys = array_keys($filterParams);
82 |
83 | $columns = implode(", ", $keys);
84 | $interrogations = implode(", ", array_fill(0, count($keys), "?"));
85 |
86 | return "INSERT INTO $table ($columns) VALUES ($interrogations);";
87 | }
88 |
89 | static function buildUpdateQuery ($filterParams) {
90 | $table = static::$table;
91 | $keys = array_keys($filterParams);
92 |
93 | $columns = [];
94 |
95 | foreach ($keys as $key) {
96 | $columns[] = "$key = ?";
97 | }
98 |
99 | $columns = implode(', ', $columns);
100 |
101 | return "UPDATE $table SET $columns WHERE id = ?;";
102 | }
103 |
104 |
105 | # mysqli connect and friends
106 |
107 | static private $instance = null;
108 |
109 | static private function instance () {
110 |
111 | if (self::$instance == null) {
112 | self::$instance = new mysqli(HOST, USER, PASS, BASE);
113 | self::$instance->set_charset('utf8mb4');
114 |
115 | if (self::$instance->connect_error) die('Connect Error (' . self::$instance->connect_errno . ') ' . self::$instance->connect_error);
116 | }
117 |
118 | return self::$instance;
119 | }
120 |
121 | static function convertResult ($result) {
122 | $response = [];
123 |
124 | while ($row = $result->fetch_assoc()) {
125 |
126 | $instance = new static();
127 |
128 | foreach($row as $attribute => $value){
129 | $instance->$attribute = $value;
130 | }
131 |
132 | $response[] = $instance;
133 |
134 | }
135 |
136 | return $response;
137 | }
138 |
139 | static function convertSingleResult ($result) {
140 | $row = $result->fetch_assoc();
141 |
142 | $instance = new static();
143 |
144 | foreach($row as $attribute => $value){
145 | $instance->$attribute = $value;
146 | }
147 |
148 | return $instance;
149 | }
150 |
151 | }
--------------------------------------------------------------------------------
/core/Routing/Router.php:
--------------------------------------------------------------------------------
1 | lockRegistration) return false;
18 |
19 | if ($this->isCurrentRoute($route, $http_method))
20 | {
21 |
22 | $this->lockRegistration = true;
23 |
24 | $params = $this->getRouteParams($route, $_SERVER['REQUEST_URI']);
25 | $this->callControllerAndMethod($controller, $method, $params);
26 |
27 | }
28 |
29 | return true;
30 | }
31 |
32 | function isCurrentRoute($route, $method)
33 | {
34 |
35 | if ($_SERVER['REQUEST_METHOD'] != strtoupper($method)) return false;
36 |
37 | if (!$this->isRouteMatchURI($route, $_SERVER['REQUEST_URI'])) return false;
38 |
39 | return true;
40 |
41 | }
42 |
43 | function getRouteParams($route, $uri)
44 | {
45 |
46 | preg_match($this->getRegexFromRoute($route), $uri, $matches, PREG_UNMATCHED_AS_NULL);
47 |
48 | array_shift($matches);
49 |
50 | return $matches;
51 |
52 | }
53 |
54 | function callControllerAndMethod($controller, $method, $params)
55 | {
56 |
57 | $controller = new $controller();
58 | $controller->$method(...$params);
59 |
60 | }
61 |
62 | function isRouteMatchURI($route, $uri)
63 | {
64 | return preg_match($this->getRegexFromRoute($route), rtrim($uri, '/'), $matches, PREG_UNMATCHED_AS_NULL);
65 | }
66 |
67 | function getRegexFromRoute($route)
68 | {
69 | $response = "/^";
70 |
71 | foreach (explode('/', $route) as $slash)
72 | {
73 | if (!$slash) continue;
74 |
75 | $response .= "\/";
76 | $response .= substr($slash, 0, 1) == ":" ? "(.*)" : $slash;
77 | }
78 |
79 | return $response . "$/";
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/core/View/Views.php:
--------------------------------------------------------------------------------
1 | 'src/',
5 | 'Core\\' => 'core/'
6 | ];
7 |
8 | spl_autoload_register(function ($class) use ($autoloader) {
9 | foreach ($autoloader as $prefix => $base_dir)
10 | {
11 | $len = strlen($prefix);
12 | if (strncmp($prefix, $class, $len) !== 0) {
13 | continue;
14 | }
15 |
16 | // Pega o nome relativo da classe
17 | $relative_class = substr($class, $len);
18 |
19 | // Substitui o prefixo do namespace pelo diretório base, substitui os separadores
20 | // de namespace por separadores de diretório no nome da classe relativa e adiciona '.php'
21 | $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
22 | $file = __DIR__ . '/../' . $base_dir . str_replace('\\', '/', $relative_class) . '.php';
23 |
24 | if (file_exists($file))
25 | {
26 | require $file;
27 | return;
28 | }
29 | }
30 | });
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine On
2 | RewriteCond [REQUEST_URI] !-f
3 | RewriteCond [REQUEST_URI] !-d
4 | RewriteRule ^(.*)$ index.php?_route_=$1 [QSA,L]
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | register('/', 'get', 'App\Controller\ProductsController', 'index');
17 | $router->register('/produtos', 'get', 'App\Controller\ProductsController', 'index');
18 | $router->register('/novo-produto', 'get', 'App\Controller\ProductsController', 'new');
19 | $router->register('/criar-produto', 'post', 'App\Controller\ProductsController', 'create');
20 | $router->register('/apagar-produto/:id', 'get', 'App\Controller\ProductsController', 'destroy');
21 | $router->register('/editar-produto/:id', 'get', 'App\Controller\ProductsController', 'edit');
22 | $router->register('/editar-produto/:id', 'post', 'App\Controller\ProductsController', 'update');
--------------------------------------------------------------------------------
/resources/views/products.edit.html.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/products.index.html.php:
--------------------------------------------------------------------------------
1 |
2 | Novo
3 |
4 |
5 |
6 | $product) { ?>
7 |
8 |
9 | id ?> |
10 | name ?> |
11 | editar |
12 | apagar |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/views/products.new.html.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/web.index.html.php:
--------------------------------------------------------------------------------
1 | WEB
--------------------------------------------------------------------------------
/src/Controller/ProductsController.php:
--------------------------------------------------------------------------------
1 | = ?', [7]);
16 |
17 | Views::render('products.index', get_defined_vars());
18 | }
19 |
20 | function new()
21 | {
22 | Views::render('products.new', get_defined_vars());
23 | }
24 |
25 | function create()
26 | {
27 | ProductModel::new($_POST);
28 | Views::redirect('/produtos');
29 | }
30 |
31 | function destroy($id)
32 | {
33 | ProductModel::destroy($id);
34 | Views::redirect('/produtos');
35 | }
36 |
37 | function edit($id)
38 | {
39 | $product = ProductModel::find($id);
40 | Views::render('products.edit', get_defined_vars());
41 | }
42 |
43 | function update($id)
44 | {
45 | $product = ProductModel::find($id);
46 | $product->update($_REQUEST);
47 | Views::redirect('/produtos');
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/src/Controller/WebController.php:
--------------------------------------------------------------------------------
1 |