Camada de Controle (Controllers)¶
A Camada de Controle é a porta de entrada da aplicação. Sua responsabilidade é receber uma Requisição HTTP, validar a intenção do usuário (Autenticação/Autorização), delegar a ação para um Serviço e devolver uma Resposta formatada.
No MIDDAG, os controllers agem como uma barreira de proteção entre o ambiente global caótico do Moodle e o núcleo orientado a objetos limpo do plugin.
O Papel do Base Controller¶
Todos os controllers do sistema devem estender local_middag\core\platform\controller\base_controller (para páginas HTML) ou base_api_controller (para JSON/API).
Essa classe abstrata fornece superpoderes através de Injeção de Dependência e Traits de Comportamento.
classDiagram
class BaseController {
<>
#ContainerInterface container
#Request request
+handle()
+render(content)
+json_response(data)
}
class InteractsWithAuth {
<>
+require_login()
+check_capabilities()
}
class InteractsWithPage {
<>
+set_page_title()
+setup_moodle_page()
}
class MyFeatureController {
+index()
+save()
}
BaseController <|-- MyFeatureController
BaseController ..|> InteractsWithAuth : uses
BaseController ..|> InteractsWithPage : uses
Ciclo de Vida da Requisição¶
Quando o Kernel despacha uma rota para um Controller, o seguinte fluxo ocorre automaticamente:
- Instanciação: O Controller é criado pelo Container de Injeção de Dependência.
- Injeção (
set_container): O Kernel injeta o Container para que o controller possa acessar serviços (lazy loading). - Captura de Request: A global
$_GET/$_POSTé convertida em um objetoSymfony\Component\HttpFoundation\Requestseguro. - Pré-Execução (
handle): Antes de executar o método da rota (ex:index), o controller executa verificações de segurança padrão (login, contexto, capacidades).
Segurança e Contexto¶
Diferente do desenvolvimento Moodle padrão onde você chama require_login() solto no topo do arquivo PHP, no MIDDAG isso é declarativo via Traits.
Autenticação (interacts_with_auth)¶
O controller verifica automaticamente se o usuário está logado e se possui permissão para acessar o contexto atual.
<?php
// No seu controller:
protected function check_capabilities(): void
{
// Lança exceção automaticamente se o usuário não tiver a capability
$this->require_capability('local/middag:manage_courses');
}
Contexto de Página (interacts_with_page)¶
Configura a variável global $PAGE do Moodle (título, breadcrumbs, layout) de forma orientada a objetos.
<?php
public function index(): Response
{
$this->set_page_title('Meus Cursos');
$this->set_page_layout('standard');
// ... lógica
}
Retornando Respostas¶
O MIDDAG normaliza a saída. Um método de controller sempre deve retornar um objeto Symfony\Component\HttpFoundation\Response.
1. Renderizando HTML (Moodle Page)¶
Para renderizar uma página completa com cabeçalho e rodapé do Moodle:
<?php
return $this->render_from_template('local_middag/minha_view', [
'nome' => 'Aluno',
'cursos' => $lista
]);
O método render captura o Output Buffer do Moodle ($OUTPUT->header(), etc) e encapsula em um objeto Response.
2. Retornando JSON (API)¶
Para endpoints AJAX ou APIs internas:
O http_kernel garante que cabeçalhos corretos (application/json) sejam enviados e que nenhum aviso/erro do PHP corrompa o JSON (Output Cleaning).
3. Redirecionamento¶
Roteamento com Atributos (PHP 8)¶
Não é necessário editar arquivos centrais de rota. As rotas são definidas diretamente acima dos métodos do controller usando Atributos Nativos do PHP 8.
<?php
use Symfony\Component\Routing\Annotation\Route;
class curso_controller extends base_controller
{
#[Route('/curso/{id}', name: 'middag.curso.view', methods: ['GET'])]
public function view(int $id): Response
{
// O Kernel automaticamente converte o parâmetro {id} da URL
// para o argumento $id do método.
$item = $this->repo->find($id);
return $this->render(...);
}
#[Route('/curso/save', name: 'middag.curso.save', methods: ['POST'])]
public function save(): Response
{
// Processamento de formulário...
}
}
Injeção de Serviços nos Controllers¶
Como o Controller é instanciado pelo Container, você pode pedir qualquer serviço no construtor:
<?php
public function __construct(
private readonly item_search_service $searchService,
private readonly course_repository $courseRepo
) {}
Nota: O base_controller não sobrescreve o construtor, deixando-o livre para suas dependências.