Pular para conteúdo

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:

  1. Instanciação: O Controller é criado pelo Container de Injeção de Dependência.
  2. Injeção (set_container): O Kernel injeta o Container para que o controller possa acessar serviços (lazy loading).
  3. Captura de Request: A global $_GET/$_POST é convertida em um objeto Symfony\Component\HttpFoundation\Request seguro.
  4. 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:

<?php

return $this->json_response([
    'status' => 'success',
    'data' => $meuDto->to_array()
]);

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

<?php

return $this->redirect_to_route('middag.course.index', ['category' => 5]);

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.