Projeto Prático de OOP

Você já estudou cinco lições sobre programação orientada a objetos. Agora, vamos unir todo esse conhecimento em dois projetos: um sistema completo de usuários que abrange herança, enums, traits e interfaces, e uma estrutura simples que lhe dará uma primeira noção da arquitetura MVC.

1. Projeto Um: Sistema de Gerenciamento de Funções do Usuário

Combine classes, herança, enums, traits e interfaces para criar uma estrutura completa de gerenciamento de funções de usuário.

▶ Exemplo: Sistema de usuários totalmente baseado em OOP

PHP
<?php
// === Enums ===
enum UserRole: string {
    case ADMIN  = 'admin';
    case EDITOR = 'editor';
    case MEMBER = 'member';
    case GUEST  = 'guest';
}

enum UserStatus: string {
    case ACTIVE   = 'active';
    case INACTIVE = 'inactive';
    case BANNED   = 'banned';
}

// === Interfaces ===
interface Loggable {
    public function getLogIdentifier(): string;
}

interface Manageable {
    public function canAccess(string $resource): bool;
    public function getPermissions(): array;
}

// === Traits ===
trait HasTimestamps {
    public readonly string $createdAt;
    public readonly string $updatedAt;

    public function touch(): void {
        $now = date("Y-m-d H:i:s");
        if (!isset($this->createdAt)) {
            $this->createdAt = $now;
        }
        $this->updatedAt = $now;
    }
}

trait ArraySerializable {
    public function toArray(): array {
        return get_object_vars($this);
    }
}

// === Base Class ===
abstract class User implements Loggable, Manageable {
    use HasTimestamps, ArraySerializable;

    public function __construct(
        public readonly string $id,
        public string $name,
        public string $email,
        public UserRole $role = UserRole::MEMBER,
        public UserStatus $status = UserStatus::ACTIVE,
    ) {
        $this->touch();
    }

    public function getLogIdentifier(): string {
        return "User#{$this->id} ({$this->name})";
    }

    abstract public function canAccess(string $resource): bool;
    abstract public function getPermissions(): array;
    abstract public function getRoleLabel(): string;
}

// === Concrete Role Classes ===
class Admin extends User {
    public function __construct(
        string $id, string $name, string $email
    ) {
        parent::__construct($id, $name, $email, UserRole::ADMIN);
    }

    public function canAccess(string $resource): bool {
        return true;  // Admin can access everything
    }

    public function getPermissions(): array {
        return ['create', 'read', 'update', 'delete', 'manage_users', 'system_config'];
    }

    public function getRoleLabel(): string {
        return '🔧 Admin';
    }
}

class Editor extends User {
    public function __construct(
        string $id, string $name, string $email
    ) {
        parent::__construct($id, $name, $email, UserRole::EDITOR);
    }

    public function canAccess(string $resource): bool {
        return in_array($resource, ['articles', 'media', 'comments']);
    }

    public function getPermissions(): array {
        return ['create', 'read', 'update', 'manage_comments'];
    }

    public function getRoleLabel(): string {
        return '✏️ Editor';
    }
}

class Member extends User {
    public function __construct(
        string $id, string $name, string $email
    ) {
        parent::__construct($id, $name, $email, UserRole::MEMBER);
    }

    public function canAccess(string $resource): bool {
        return in_array($resource, ['articles', 'comments', 'profile']);
    }

    public function getPermissions(): array {
        return ['read', 'comment'];
    }

    public function getRoleLabel(): string {
        return '👤 Member';
    }
}

// === Access Manager ===
class AccessManager {
    public function checkAccess(User $user, string $resource): string {
        if ($user->status === UserStatus::BANNED) {
            return "{$user->getRoleLabel()} {$user->name}: Banned, access denied for {$resource}";
        }
        
        $allowed = $user->canAccess($resource)
            ? "✅ Access Granted" : "❌ Access Denied";
        
        return "{$user->getRoleLabel()} {$user->name}: {$allowed} {$resource}";
    }
    
    public function listAllAccess(array $users, array $resources): void {
        foreach ($users as $user) {
            echo "<h4>{$user->getLogIdentifier()}</h4>";
            echo "Permissions: " . implode(', ', $user->getPermissions()) . "<br>";
            foreach ($resources as $res) {
                echo $this->checkAccess($user, $res) . "<br>";
            }
        }
    }
}

// === Test the System ===
$users = [
    new Admin("u1", "John", "admin@example.com"),
    new Editor("u2", "Jane", "editor@example.com"),
    new Member("u3", "Bob", "member@example.com"),
];

$resources = ['articles', 'media', 'comments', 'system_config', 'manage_users'];

$manager = new AccessManager();
$manager->listAllAccess($users, $resources);

// Test enum + serialization
echo "<br>Admin data: " . json_encode($users[0]->toArray(), JSON_UNESCAPED_UNICODE);
?>
▶ Experimente

2. Projeto 2: Framework MVC simples

Compreenda o conceito MVC de Router, Controller e View — essa é a ideia central por trás de frameworks como o Laravel.

▶ Exemplo: Mini MVC

PHP
<?php
// === Router ===
class Router {
    private array $routes = [];

    public function get(string $path, callable $handler): void {
        $this->routes['GET'][$path] = $handler;
    }

    public function post(string $path, callable $handler): void {
        $this->routes['POST'][$path] = $handler;
    }

    public function dispatch(string $method, string $uri): void {
        $path = parse_url($uri, PHP_URL_PATH);
        $handler = $this->routes[$method][$path] ?? null;

        if ($handler) {
            echo $handler($_GET);
        } else {
            http_response_code(404);
            echo "<h2>404 — Page Not Found</h2>";
        }
    }
}

// === View Base ===
class View {
    public static function render(string $template, array $data = []): string {
        extract($data);
        ob_start();
        include __DIR__ . "/{$template}.php";
        return ob_get_clean();
    }
}

// === Controller Base ===
abstract class Controller {
    protected function json(array $data, int $code = 200): void {
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
    }
}

// === Concrete Controller ===
class HomeController extends Controller {
    public function index(): void {
        echo View::render('home', [
            'title'   => 'Home',
            'message' => 'Welcome to my blog!',
        ]);
    }

    public function about(): void {
        echo View::render('about', [
            'title'   => 'About Us',
            'team'    => ['John', 'Jane', 'Bob'],
            'version' => '1.0.0',
        ]);
    }

    public function api(): void {
        $this->json([
            'status' => 'ok',
            'data'   => ['version' => '1.0.0', 'uptime' => time()],
        ]);
    }
}

// === Set Up Routes ===
$router = new Router();
$home = new HomeController();

$router->get('/', fn() => $home->index());
$router->get('/about', fn() => $home->about());
$router->get('/api/status', fn() => $home->api());

// === Launch the App ===
$router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
?>
▶ Experimente

O arquivo de visualização anexo home.php:

PHP
<!DOCTYPE html>
<html lang="en">
<head><title><?= $title ?></title></head>
<body>
    <h1><?= $message ?></h1>
    <p>This page is built using MVC principles.</p>
</body>
</html>

3. A mudança de mentalidade: da programação procedural à OOP

Pensamento procedural Pensamento OOP
“Preciso ler os dados do usuário” → escreva uma função getUser() “O que é um usuário? Quais são suas propriedades e comportamentos?” → projete uma classe User
Dados e funções espalhados por vários arquivos Dados + comportamento encapsulados em uma classe
Código duplicado → copiar e colar ou extrair uma função Código duplicado → usar herança ou Traits
Passar strings 'admin' para permissões Passar enumeração UserRole::ADMIN, com segurança de tipos
💡 Dica: A POO é um meio, não um fim. A programação procedural é perfeitamente adequada para pequenos scripts. Mude para a POO quando perceber estes sinais: (1) os mesmos parâmetros são passados repetidamente entre funções; (2) as variáveis globais estão se acumulando; (3) as condições se baseiam em “cadeias de caracteres de tipo” (if ($role === 'admin')).

❓ Perguntas Frequentes

P: O que um Controlador realmente faz no MVC? R: O Controlador é o “diretor de tráfego” — ele recebe a solicitação, chama o Modelo para obter dados e escolhe a Visualização para renderização. Ele não lida com a lógica de negócios propriamente dita (essa é a função do Modelo) e não gera código HTML (essa é a função da Visualização).

P: Devo criar meu próprio framework ou usar um já existente? R: Crie um mini-framework durante a fase de aprendizado para entender os princípios (assim como nesta lição). Use o Laravel/Symfony para produção. Depois de criar seu próprio Router/Controller/View uma vez, usar o Laravel faz você pensar: “Ah, então é isso que acontece nos bastidores.”

❓ Perguntas Frequentes

P: Como faço para testar a mini-framework MVC que criei? R: Inicie o Apache ou o servidor PHP integrado (php -S localhost:8000) e acesse http://localhost:8000 no seu navegador. O despachador direciona para o controlador apropriado com base na URL. Use var_dump() ou error_log() para depurar passo a passo.

📖 Resumo

📝 Exercícios

  1. Com base no sistema do usuário, adicione uma função Guest (que só permite acessar artigos, sem nenhuma permissão de edição) e inclua-a no conjunto de testes.
  2. Adicione um método addRoute('GET/POST', $path, $handler) ao roteador para que uma única chamada possa registrar ambos os métodos.
  3. Adicione um UserController à estrutura MVC que implemente uma lista de usuários (lida a partir de um array) e uma página de detalhes do usuário (consulta por ID).
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%