├── .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 |
2 | 3 |

Editar Novo Produto

4 | 5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 | Cancelar 13 | 14 |
-------------------------------------------------------------------------------- /resources/views/products.index.html.php: -------------------------------------------------------------------------------- 1 | 2 | Novo 3 | 4 | 5 | 6 | $product) { ?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
id ?>name ?>editarapagar
18 | -------------------------------------------------------------------------------- /resources/views/products.new.html.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Criar Novo Produto

4 | 5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 | Cancelar 13 | 14 |
-------------------------------------------------------------------------------- /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 |