Dominando Camada View: Templates PHP e Separação de Apresentação em Projetos Reais Já leu

Camada View: Templates PHP e Separação de Apresentação A camada View é responsável por toda a apresentação de dados ao usuário. Em uma arquitetura bem definida, ela não contém lógica de negócio, apenas renderização HTML. Templates PHP são arquivos que misturam marcação com variáveis PHP, permitindo dinamismos na interface. A separação entre apresentação e lógica é um princípio fundamental do padrão MVC (Model-View-Controller) que, quando bem aplicado, torna o código mais manutenível, testável e reutilizável. Neste artigo, você aprenderá a estruturar templates PHP profissionais, aplicar boas práticas de separação de responsabilidades e implementar padrões reais usados em frameworks modernos. Estrutura Básica de Templates PHP O Princípio da Separação Um template PHP deve receber dados já processados do Controller e apenas renderizá-los. Nunca deve conter consultas ao banco de dados, regras de negócio ou validações complexas. Veja um exemplo incorreto versus correto: O segundo exemplo recebe já processado pelo Controller, contendo apenas usuários válidos. Usa para prevenir XSS e focam exclusivamente

Camada View: Templates PHP e Separação de Apresentação

A camada View é responsável por toda a apresentação de dados ao usuário. Em uma arquitetura bem definida, ela não contém lógica de negócio, apenas renderização HTML. Templates PHP são arquivos que misturam marcação com variáveis PHP, permitindo dinamismos na interface. A separação entre apresentação e lógica é um princípio fundamental do padrão MVC (Model-View-Controller) que, quando bem aplicado, torna o código mais manutenível, testável e reutilizável.

Neste artigo, você aprenderá a estruturar templates PHP profissionais, aplicar boas práticas de separação de responsabilidades e implementar padrões reais usados em frameworks modernos.

Estrutura Básica de Templates PHP

O Princípio da Separação

Um template PHP deve receber dados já processados do Controller e apenas renderizá-los. Nunca deve conter consultas ao banco de dados, regras de negócio ou validações complexas. Veja um exemplo incorreto versus correto:

// ❌ INCORRETO - Lógica na View
<?php
$usuario = mysqli_query($conexao, "SELECT * FROM usuarios WHERE id = 1");
$dados = mysqli_fetch_assoc($usuario);
if ($dados['idade'] > 18) {
    echo "Maior de idade";
}
?>

// ✅ CORRETO - Apenas apresentação
<?php foreach ($usuarios as $usuario): ?>
    <div class="usuario">
        <h2><?= htmlspecialchars($usuario['nome']) ?></h2>
        <p>Email: <?= htmlspecialchars($usuario['email']) ?></p>
    </div>
<?php endforeach; ?>

O segundo exemplo recebe $usuarios já processado pelo Controller, contendo apenas usuários válidos. Usa htmlspecialchars() para prevenir XSS e focam exclusivamente na renderização. Esta é a forma profissional.

Estrutura de Diretórios

projeto/
├── app/
│   ├── Controllers/
│   │   └── UsuarioController.php
│   └── Views/
│       ├── usuarios/
│       │   ├── index.php
│       │   ├── show.php
│       │   └── form.php
│       └── layouts/
│           └── base.php
├── public/
└── config/
    └── routes.php

Este padrão permite localizar templates facilmente e escalar a aplicação. Cada recurso possui seu próprio diretório de views.

Padrão Layout e Componentes Reutilizáveis

Layouts Base

Templates devem herdar de um layout base que contém HTML estrutural comum, evitando duplicação:

<!-- app/Views/layouts/base.php -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= isset($titulo) ? htmlspecialchars($titulo) : 'Meu App' ?></title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <header>
        <nav><!-- Menu aqui --></nav>
    </header>

    <main class="container">
        <?= $conteudo ?>
    </main>

    <footer>
        <p>&copy; 2024 Meu App</p>
    </footer>
</body>
</html>
<!-- app/Views/usuarios/index.php -->
<div class="usuarios-list">
    <h1>Usuários</h1>
    <a href="/usuarios/criar" class="btn">Novo Usuário</a>

    <?php if (empty($usuarios)): ?>
        <p class="aviso">Nenhum usuário encontrado.</p>
    <?php else: ?>
        <table>
            <thead>
                <tr>
                    <th>Nome</th>
                    <th>Email</th>
                    <th>Ações</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($usuarios as $usuario): ?>
                    <tr>
                        <td><?= htmlspecialchars($usuario['nome']) ?></td>
                        <td><?= htmlspecialchars($usuario['email']) ?></td>
                        <td>
                            <a href="/usuarios/<?= (int)$usuario['id'] ?>/editar">Editar</a>
                            <a href="/usuarios/<?= (int)$usuario['id'] ?>/deletar">Deletar</a>
                        </td>
                    </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    <?php endif; ?>
</div>

Componentes Reutilizáveis

Componentes são pequenas views incluídas em outras templates:

<!-- app/Views/components/alerta.php -->
<?php
$tipo = $tipo ?? 'info'; // info, sucesso, erro
$mensagem = $mensagem ?? '';
?>
<div class="alerta alerta-<?= htmlspecialchars($tipo) ?>">
    <?= htmlspecialchars($mensagem) ?>
</div>

<!-- Uso em outra template -->
<?php include __DIR__ . '/../components/alerta.php'; 
// Variáveis $tipo e $mensagem já definidas no escopo ?>

Integração com Controller

Renderização Profissional

O Controller deve ser responsável por chamar o template e passar dados:

<?php
// app/Controllers/UsuarioController.php

class UsuarioController {

    public function index() {
        // Buscar dados (normalmente via Model)
        $usuarios = [
            ['id' => 1, 'nome' => 'João', 'email' => 'joao@email.com'],
            ['id' => 2, 'nome' => 'Maria', 'email' => 'maria@email.com']
        ];

        // Renderizar template
        $this->render('usuarios/index', [
            'usuarios' => $usuarios,
            'titulo' => 'Lista de Usuários'
        ]);
    }

    protected function render($view, $dados = []) {
        extract($dados); // Torna variáveis acessíveis

        ob_start();
        include __DIR__ . "/../Views/{$view}.php";
        $conteudo = ob_get_clean();

        include __DIR__ . '/../Views/layouts/base.php';
    }
}

A função render() encapsula a lógica de renderização, usando ob_start() para capturar a output e injetar no layout base. Isso garante consistência em toda a aplicação.

Escapar Dados Sempre

Sempre escape dados de user-input para prevenir XSS:

<?php
// ✅ SEGURO
<p><?= htmlspecialchars($usuario['bio'], ENT_QUOTES, 'UTF-8') ?></p>

// ❌ VULNERÁVEL
<p><?= $usuario['bio'] ?></p>

// Para atributos HTML
<img src="<?= htmlspecialchars($imagem, ENT_QUOTES, 'UTF-8') ?>" alt="">

Boas Práticas Avançadas

Uso de Variáveis de Controle

Defina flags booleanas no Controller para controlar fluxos na View:

// No Controller
$this->render('produto/detalhes', [
    'produto' => $produto,
    'pode_editar' => $usuarioAutenticado && $usuarioAutenticado['id'] === $produto['usuario_id'],
    'em_estoque' => $produto['quantidade'] > 0
]);

// Na View
<?php if ($pode_editar): ?>
    <a href="/produtos/<?= $produto['id'] ?>/editar" class="btn">Editar</a>
<?php endif; ?>

<?php if (!$em_estoque): ?>
    <span class="indisponivel">Fora de estoque</span>
<?php endif; ?>

Evitar Lógica Complexa

Se precisar formatar dados, faça no Controller usando helper functions:

<?php
// Helper
function formatarMoeda($valor) {
    return 'R$ ' . number_format($valor, 2, ',', '.');
}

// No Controller
$dados['preco_formatado'] = formatarMoeda($produto['preco']);

// Na View (simples)
<p><?= htmlspecialchars($preco_formatado) ?></p>
?>

Conclusão

A separação entre View e lógica de negócio é fundamental para código profissional. Mantenha templates simples, delegue processamento ao Controller, sempre escape dados de entrada e reutilize componentes através de layouts. Esses princípios aumentam segurança, legibilidade e facilitam testes. Com prática, você naturalmente identificará quando código tem "cheiro" de estar no lugar errado — confie nesse instinto e refatore.

Referências


Artigos relacionados