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>© 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.