# PHP-MVC-Framework2 **Repository Path**: wfdaj/php-mvc-framework2 ## Basic Information - **Project Name**: PHP-MVC-Framework2 - **Description**: No description available - **Primary Language**: PHP - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-05 - **Last Updated**: 2026-05-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Modelo MVC Genérico em PHP Um micro-framework MVC moderno, leve e modular, construído com PHP puro. Projetado para servir como um _template base_ ágil para iniciar novos projetos, oferecendo recursos de segurança e arquitetura vistos em frameworks robustos (como Laravel), porém mantendo a simplicidade. ## Recursos Integrados - **Arquitetura Modular:** Separação limpa de responsabilidades (ex: `app/Site`, `app/Auth`). - **Roteamento Dinâmico:** Suporte a métodos HTTP, rotas por arquivo e injeção de parâmetros de URL. - **Middlewares:** Proteja suas rotas e execute lógicas antes do Controller. - **Classe Request:** Abstração completa de globais (`$_POST`, `$_GET`, etc.). - **Proteção CSRF Nativa:** Formulários seguros por padrão via Middleware Global. - **Sessões e Flash Messages:** Gerenciamento elegante de feedback para o usuário. - **Herança de Views:** Sistema inteligente de layouts globais ou específicos de módulo. --- ## 📁 Estrutura de Diretórios ```text ├── app/ # Seus módulos (Ex: Site, Auth, Painel) │ ├── Auth/ │ │ ├── Controllers/ │ │ ├── Middlewares/ │ │ ├── Models/ │ │ └── Views/ │ ├── Shared/ # Arquivos compartilhados (Layouts globais, Middlewares globais) │ └── Site/ ├── core/ # Núcleo do Framework (NÃO EDITAR) │ ├── Database/ # Acesso a Dados (Database.php, Model.php) │ ├── Http/ # Request, Router, Session, Middleware │ ├── Utils/ # Ferramentas auxiliares (Env, Funcoes, etc) │ ├── View/ # Motor de Renderização │ ├── Application.php # Inicializador Principal │ └── Controller.php # Classe Base dos Controllers ├── public/ # Ponto de entrada (Document Root) │ └── index.php └── routes/ # Arquivos de Rotas (*.php carregados automaticamente) ``` --- ## 🚀 Como Usar ### 1. Criando Rotas As rotas são definidas dentro de arquivos `.php` na pasta `/routes`. Todos os arquivos nessa pasta são carregados automaticamente. ```php // routes/site.php get('/', HomeController::class, 'index'); // Rota POST com parâmetros na URL (ex: /produto/15) $router->post('/produto/{id}', HomeController::class, 'atualizar'); // Rota Protegida por Middleware $router->get('/painel', HomeController::class, 'painel')->middleware(AuthMiddleware::class); ``` ### 2. Criando Controllers Todo controller deve herdar de `Core\Controller`. O objeto `Request` é injetado automaticamente nas funções para capturar dados. ```php namespace App\Site\Controllers; use Core\Controller; use Core\Http\Request; class HomeController extends Controller { public function index(Request $request) { // Resgatando dados (Substitui $_GET e $_POST) $termo = $request->get('busca'); $email = $request->post('email'); // Retornando uma View $this->render('Site', 'home/index', ['title' => 'Página Inicial']); } } ``` **Métodos úteis do Controller:** - `$this->render('Modulo', 'caminho/view', $dados)`: Renderiza tela html. - `$this->json($array, $status)`: Retorna uma resposta JSON (ideal para APIs REST). Ex: `$this->json(['ok' => true], 200)`. - `$this->redirect('/url')`: Redireciona o usuário. - `$this->flash('chave', 'mensagem')`: Define uma notificação Flash temporária. ### 3. Criando Views e Layouts As views ficam em `app/Modulo/Views`. Por padrão, a função `$this->render()` sempre tenta injetar a view dentro do layout global localizado em `app/Shared/Views/layouts/default.php`. Para imprimir variáveis, use o PHP puro. E para proteger os seus formulários POST, sempre use a função injetada `$csrf_field()`: ```php

Login

``` ### 4. Flash Messages (Notificações) Para exibir mensagens de sucesso ou erro (ex: após um login falhar), faça no controller: ```php $this->flash('error', 'Credenciais inválidas!'); $this->redirect('/login'); ``` E as exiba no seu layout (`default.php`): ```php
``` ### 5. Middlewares e Agrupamento de Rotas Middlewares funcionam como "guarda-costas" das rotas. O **CsrfMiddleware** já vem ativado globalmente pelo roteador para todas as rotas de alteração (`POST`, `PUT`, `DELETE`). **Middleware em rota individual:** ```php $router->get('/dashboard', DashboardController::class, 'index') ->middleware(\App\Auth\Middlewares\AuthMiddleware::class); ``` **Middleware no grupo (aplicado automaticamente a todas as rotas do grupo):** ```php $router->group(['prefix' => '/admin', 'middleware' => AuthMiddleware::class], function ($router) { $router->get('/painel', AdminController::class, 'index'); // tem AuthMiddleware $router->get('/usuarios', AdminController::class, 'users'); // tem AuthMiddleware }); ``` **Combinando: middleware do grupo + middleware extra por rota:** ```php $router->group(['prefix' => '/admin', 'middleware' => AuthMiddleware::class], function ($router) { // Essa rota tem AuthMiddleware (herdado do grupo) $router->get('/painel', AdminController::class, 'index'); // Essa rota tem AuthMiddleware (grupo) + AdminOnlyMiddleware (extra individual) $router->delete('/usuario/{id}', AdminController::class, 'destroy') ->middleware(AdminOnlyMiddleware::class); }); ``` > 💡 A ordem de execução é: **CSRF Global → Middlewares do Grupo → Middleware Individual → Controller**. ### 6. Validação de Dados (Request Validator) O objeto `Request` possui um validador embutido. Basta passar as regras. Se falhar, o sistema armazena os erros na sessão e volta para a página anterior automaticamente. **Regras Suportadas Atualmente:** - `required`: O campo não pode vir vazio. - `email`: O campo deve ser um endereço de e-mail válido. - `min:X`: O campo deve ter no mínimo X caracteres (ex: `min:6`). - `max:X`: O campo deve ter no máximo X caracteres. - `numeric`: O campo deve ser numérico (aceita decimais). - `integer`: O campo deve ser um número inteiro. - `alpha_num`: O campo deve conter apenas letras e números. **Exemplo de uso no Controller:** ```php public function salvar(Request $request) { $dados = $request->validate([ 'nome' => 'required', 'email' => 'required|email', 'senha' => 'required|min:6' ]); // O código abaixo só executa se a validação passar $this->model->create($dados); } ``` ### 7. Tratamento de Erros e Exceções O framework possui um `Handler` global (registrado em `public/index.php`). Se uma exceção ou erro fatal ocorrer: - Em ambiente local (`APP_ENV=local`), o erro completo (Stack Trace) será exibido na tela. - Em produção (`APP_ENV=production`), uma página 500 genérica será exibida, e o erro detalhado será salvo no arquivo `storage/logs/app.log`. --- ## 🚀 Iniciando um Novo Projeto A forma mais rápida de iniciar um novo projeto a partir deste template é via linha de comando: 1. Clone o repositório sem o histórico de commits (mais leve): ```bash git clone --depth=1 https://github.com/FabioSouzaF/PHP-MVC-Framework NovoProjeto cd NovoProjeto ``` 2. Inicialize o banco de dados e as configurações: ```bash php console init ``` _O comando `init` irá copiar automaticamente o `.env.example` para `.env`, criar o banco de dados especificado e rodar as Migrations iniciais._ --- ## 🔧 Configurando Banco de Dados (Migrations) O framework agora possui um sistema CLI de **Migrations** próprio para estruturar o banco de dados sem precisar ficar importando arquivos `.sql`. As credenciais do banco devem ser inseridas no arquivo `.env` na raiz do projeto: ```env DB_HOST=localhost DB_NAME=modelo_mvc DB_USER=root DB_PASS= APP_ENV=local # production ``` ### Criando o Banco (Executando Migrations) Após configurar o `.env` e criar o database (o database vazio deve existir no SGBD), rode o seguinte comando no terminal na raiz do projeto: ```bash php console migrate ``` Ele vai ler a pasta `database/migrations/` e criar as tabelas faltantes. ### Criando novas Migrations Para criar uma nova tabela, use: ```bash php console make:migration nome_da_sua_tabela ``` Isso gerará um arquivo na pasta `database/migrations/`. Abra-o e digite o SQL de criação dentro do método `up()`. ### Gerando Código pela CLI O `console` também automatiza a criação de **Testes Unitários** e **DTOs**, evitando o trabalho manual de criar pastas e boilerplate. **Criar um Teste Unitário:** ```bash # Gera: tests/Unit/Core/ValidatorTest.php php console make:test Core/ValidatorTest # Gera: tests/Unit/App/DTOs/PedidoDTOTest.php php console make:test App/DTOs/PedidoDTOTest ``` **Criar um DTO:** ```bash # Gera: app/Auth/DTOs/UserDTO.php php console make:dto Auth/UserDTO ``` **Criar um Model:** ```bash # Model tradicional (SQL puro) php console make:model Pedidos/PedidoModel # Model usando ORM ActiveRecord php console make:model Pedidos/PedidoModel --orm ``` Ambos os comandos criam o arquivo com toda a estrutura de namespace, imports e métodos prontos. Basta preencher os campos específicos. **Ver todos os comandos disponíveis:** ```bash php console ``` ### Consultando Dados (Models) A classe `Core\Database\Model` utiliza a conexão feita em `Core\Database\Database` e é ideal para quem prefere escrever **SQL puro**. ```php namespace App\Auth\Models; use Core\Database\Model; use PDO; class User extends Model { public function findByEmail(string $email) { $stmt = $this->db->prepare("SELECT * FROM users WHERE email = :email"); $stmt->execute(['email' => $email]); return $stmt->fetch(PDO::FETCH_ASSOC); } } ``` ### Consultando Dados via ORM (ActiveRecord) O framework também fornece um ORM leve opcional. Com ele, você não precisa escrever queries de CRUD ou buscas simples. ```php namespace App\Auth\Models; use Core\Database\ORM\ActiveRecord; class UserORM extends ActiveRecord { protected string $table = 'users'; } ``` **Usando o ORM no Controller:** ```php // Inserir $user = UserORM::create(['name' => 'João', 'email' => 'joao@teste.com']); // Buscar por ID $user = UserORM::find(1); // Atualizar $user->name = 'João Editado'; $user->save(); // Buscar com condições e ordenação $users = UserORM::query() ->where('active', 1) ->orderBy('created_at', 'DESC') ->limit(10) ->get(); // Retorna array de objetos UserORM // Deletar $user->delete(); ``` > **Integração com DTOs:** O ORM possui um método útil `$userModel->getAsDTO()` e `$user->toDTO()` para converter os resultados diretamente em DTOs tipados. #### Paginação Nativa Em vez de buscar todos os registros com `fetchAll`, você pode usar o helper de paginação passando a query crua. Ele lê automaticamente o parâmetro `?page=` da URL. ```php $resultado = $this->paginate("SELECT * FROM users ORDER BY created_at DESC", [], 15); // Retorna: // ['data' => [...], 'current_page' => 1, 'last_page' => 10, 'per_page' => 15, 'total' => 150] ``` #### DTOs — Retorno Tipado do Banco Em vez de arrays anônimos `array`, você pode usar **Data Transfer Objects (DTOs)** para receber os dados tipados e com autocomplete no editor. **1. Crie o DTO da entidade** em `app/SeuModulo/DTOs/`: ```php namespace App\Auth\DTOs; final class UserDTO { public function __construct( public readonly int $id, public readonly string $name, public readonly string $email, public readonly string $created_at, ) {} public static function fromArray(array $data): self { return new self( id: (int) $data['id'], name: (string) ($data['name'] ?? ''), email: (string) ($data['email'] ?? ''), created_at: (string) ($data['created_at'] ?? ''), ); } } ``` **2. Use `fetchAs()` no Model** passando a classe do DTO: ```php // Retorna UserDTO[] em vez de array[] $users = $this->fetchAs("SELECT id, name, email, created_at FROM users", UserDTO::class); // Com parâmetros: $users = $this->fetchAs("SELECT * FROM users WHERE active = ?", UserDTO::class, [1]); // Paginado com DTOs: $resultado = $this->paginateAs("SELECT * FROM users", UserDTO::class, [], 15); // $resultado['data'] será um array de UserDTO ``` **Resultado no Controller:** ```php foreach ($users as $user) { echo $user->name; // ✅ Autocomplete no editor, erro imediato se errar o nome } ``` > 💡 DTOs são **100% opcionais**. Você pode continuar usando `fetchAll()` e arrays normais se preferir. --- ## 🧪 Testes Unitários (PHPUnit 13) O framework possui uma suíte de testes configurada com **PHPUnit 13**. Os testes ficam em `tests/Unit/` e podem ser rodados a qualquer momento. ### Instalação (primeira vez) ```bash composer install ``` ### Rodando os testes ```bash # Todos os testes ./vendor/bin/phpunit # Apenas uma classe específica ./vendor/bin/phpunit tests/Unit/Core/ValidatorTest.php ``` ### Testes incluídos | Arquivo | O que testa | | ------------------------------------- | ---------------------------------------------------------------------- | | `tests/Unit/Core/ValidatorTest.php` | Todas as regras do `Request::validate()`, mass-assignment, combinações | | `tests/Unit/Core/RouterTest.php` | Registro de rotas, prefixos de grupo, herança de middlewares | | `tests/Unit/App/DTOs/UserDTOTest.php` | Conversão `fromArray()`, casting de tipos, propriedades `readonly` | ### Criando novos testes Extenda a classe `Tests\TestCase` que já inclui helpers prontos: ```php namespace Tests\Unit\App; use Tests\TestCase; class MeuNovoTest extends TestCase { public function test_meu_cenario(): void { // Helper pronto: cria um Request fake sem precisar de HTTP real $request = $this->makeRequest(post: ['campo' => 'valor']); $this->assertSame('valor', $request->post('campo')); } } ``` --- ## 🏃 Iniciando o Projeto localmente Para iniciar um servidor rápido pelo PHP sem depender de Apache ou Nginx, rode no terminal: ```bash php -S localhost:8000 -t public ``` Acesse `http://localhost:8000` no navegador. --- ## 🔭 Roadmap de Funcionalidades Futuras Curioso sobre o que está por vir? Confira o documento de planejamento com as próximas funcionalidades previstas para o framework, incluindo um **ORM opcional**, mais regras de validação, envio de e-mail e sistema de cache. 👉 [Ver FUTURE_FEATURES.md](./FUTURE_FEATURES.md)