---
name: perfex-controllers
description: Controllers para módulos Perfex CRM — AdminController, ClientsController, routing e trait ValidatesContact. Baseado apenas na documentação oficial.
---

# /perfex-controllers - Controllers Perfex CRM

Controllers para módulos Perfex CRM. **Zero assumptions, zero hallucinations** - apenas documentação oficial.

---

## Documentação Base

- [Module Controllers](https://help.perfexcrm.com/module-controllers/)
- [CodeIgniter Controllers](https://codeigniter.com/userguide3/general/controllers.html)

---

## Estrutura de Pastas

```
modules/meu_modulo/
└── controllers/
    ├── Meu_modulo.php          # Controller principal
    ├── Admin_controller.php     # Controller admin adicional
    └── Client_controller.php    # Controller cliente
```

---

## Classes Base Disponíveis

| Classe | Uso | Validação Automática |
|--------|-----|----------------------|
| `AdminController` | Área admin/staff | Login staff obrigatório |
| `ClientsController` | Área cliente | Tema cliente aplicado |
| `App_Controller` | Base genérica | Nenhuma |

---

## AdminController (Área Admin)

Para funcionalidades exclusivas de staff/administradores.

### Template

```php
<?php

defined('BASEPATH') or exit('No direct script access allowed');

class Meu_modulo extends AdminController
{
    public function __construct()
    {
        parent::__construct();
        // Carregar models, helpers, etc.
        $this->load->model('meu_modulo/meu_modulo_model');
    }

    /**
     * Página principal do módulo
     * URL: /admin/meu_modulo ou /admin/meu_modulo/index
     */
    public function index()
    {
        // Verificar permissão
        if (!staff_can('view', 'meu_modulo')) {
            access_denied('meu_modulo');
        }

        // Preparar dados
        $data['items'] = $this->meu_modulo_model->get_all();
        $data['title'] = _l('meu_modulo_title');

        // Carregar view
        $this->load->view('meu_modulo/index', $data);
    }

    /**
     * Formulário de criação
     * URL: /admin/meu_modulo/create
     */
    public function create()
    {
        if (!staff_can('create', 'meu_modulo')) {
            access_denied('meu_modulo');
        }

        if ($this->input->post()) {
            // Processar formulário
            $data = $this->input->post();
            $id = $this->meu_modulo_model->add($data);

            if ($id) {
                set_alert('success', _l('added_successfully', _l('meu_modulo_item')));
                redirect(admin_url('meu_modulo'));
            }
        }

        $this->load->view('meu_modulo/form');
    }

    /**
     * Editar item
     * URL: /admin/meu_modulo/edit/123
     */
    public function edit($id)
    {
        if (!staff_can('edit', 'meu_modulo')) {
            access_denied('meu_modulo');
        }

        $data['item'] = $this->meu_modulo_model->get($id);

        if (!$data['item']) {
            show_404();
        }

        if ($this->input->post()) {
            $success = $this->meu_modulo_model->update($id, $this->input->post());

            if ($success) {
                set_alert('success', _l('updated_successfully', _l('meu_modulo_item')));
                redirect(admin_url('meu_modulo'));
            }
        }

        $this->load->view('meu_modulo/form', $data);
    }

    /**
     * Apagar item
     * URL: /admin/meu_modulo/delete/123
     */
    public function delete($id)
    {
        if (!staff_can('delete', 'meu_modulo')) {
            access_denied('meu_modulo');
        }

        if ($this->meu_modulo_model->delete($id)) {
            set_alert('success', _l('deleted', _l('meu_modulo_item')));
        }

        redirect(admin_url('meu_modulo'));
    }
}
```

---

## ClientsController (Área Cliente)

Para funcionalidades na área de clientes.

### Template

```php
<?php

defined('BASEPATH') or exit('No direct script access allowed');

class Meu_modulo extends ClientsController
{
    public function __construct()
    {
        parent::__construct();
        $this->load->model('meu_modulo/meu_modulo_model');
    }

    /**
     * Página principal cliente
     * URL: /meu_modulo ou /meu_modulo/index
     */
    public function index()
    {
        // Verificar se cliente logado
        if (!is_client_logged_in()) {
            redirect(site_url('authentication/login'));
        }

        $contact_id = get_contact_user_id();
        $client_id = get_client_user_id();

        $data = [
            'items' => $this->meu_modulo_model->get_by_client($client_id),
        ];

        // Métodos específicos de ClientsController
        $this->data($data);
        $this->title(_l('meu_modulo_title'));
        $this->view('meu_modulo/client_index');
        $this->layout();
    }

    /**
     * Ver detalhe
     * URL: /meu_modulo/view/123
     */
    public function view($id)
    {
        if (!is_client_logged_in()) {
            redirect(site_url('authentication/login'));
        }

        $item = $this->meu_modulo_model->get($id);

        // Verificar se pertence ao cliente
        if (!$item || $item->client_id != get_client_user_id()) {
            show_404();
        }

        $this->data(['item' => $item]);
        $this->title($item->name);
        $this->view('meu_modulo/client_view');
        $this->layout();
    }
}
```

---

## ValidatesContact Trait (v2.3.3+)

Para exigir contacto autenticado com email verificado.

```php
<?php

defined('BASEPATH') or exit('No direct script access allowed');

use app\services\ValidatesContact;

class Meu_modulo extends ClientsController
{
    use ValidatesContact;

    public function __construct()
    {
        parent::__construct();

        // Todos os métodos requerem contacto validado
        $this->validateContactInController();
    }

    public function index()
    {
        // Só executa se contacto logado E email verificado
        // ...
    }
}
```

---

## URLs e Routing

### Admin Controllers

| URL | Controller | Método |
|-----|------------|--------|
| `/admin/meu_modulo` | Meu_modulo | index() |
| `/admin/meu_modulo/create` | Meu_modulo | create() |
| `/admin/meu_modulo/edit/123` | Meu_modulo | edit(123) |
| `/admin/meu_modulo/delete/123` | Meu_modulo | delete(123) |

### Client Controllers

| URL | Controller | Método |
|-----|------------|--------|
| `/meu_modulo` | Meu_modulo | index() |
| `/meu_modulo/view/123` | Meu_modulo | view(123) |

### Quando Nome Controller = Nome Módulo

Se o controller tem o mesmo nome do módulo, URLs simplificadas:

```
/admin/meu_modulo          → modules/meu_modulo/controllers/Meu_modulo.php::index()
/meu_modulo                → modules/meu_modulo/controllers/Meu_modulo.php::index()
```

### Controllers com Nomes Diferentes

```
/admin/meu_modulo/outro_controller/metodo
→ modules/meu_modulo/controllers/Outro_controller.php::metodo()
```

---

## Funções Helper Úteis

### Admin URLs

```php
// Gerar URL admin
admin_url('meu_modulo');           // /admin/meu_modulo
admin_url('meu_modulo/edit/123');  // /admin/meu_modulo/edit/123

// Redirecionar
redirect(admin_url('meu_modulo'));
```

### Site URLs

```php
// Gerar URL site/cliente
site_url('meu_modulo');            // /meu_modulo
site_url('meu_modulo/view/123');   // /meu_modulo/view/123
```

### Alertas

```php
// Definir alerta (mostra na próxima página)
set_alert('success', 'Operação concluída!');
set_alert('warning', 'Atenção!');
set_alert('danger', 'Erro!');
```

### Verificações de Login

```php
// Staff
is_staff_logged_in();     // bool
get_staff_user_id();      // int

// Cliente/Contacto
is_client_logged_in();    // bool
get_client_user_id();     // int (customer_id)
get_contact_user_id();    // int (contact_id)
```

### Acesso Negado

```php
// Redireciona com mensagem de acesso negado
access_denied('meu_modulo');
```

---

## AJAX Responses

### Retornar JSON

```php
public function ajax_get_item($id)
{
    if (!$this->input->is_ajax_request()) {
        show_404();
    }

    $item = $this->meu_modulo_model->get($id);

    echo json_encode([
        'success' => (bool) $item,
        'data' => $item,
    ]);
}
```

### Com Validação CSRF

```php
public function ajax_save()
{
    if (!$this->input->is_ajax_request()) {
        show_404();
    }

    // CSRF validado automaticamente se usar jQuery
    $data = $this->input->post();
    $id = $this->meu_modulo_model->add($data);

    echo json_encode([
        'success' => (bool) $id,
        'id' => $id,
        'message' => $id ? _l('added_successfully') : _l('error_occurred'),
    ]);
}
```

---

## Anti-Patterns (NUNCA FAZER)

| Anti-Pattern | Risco | Alternativa |
|--------------|-------|-------------|
| Controller sem verificar permissões | Acesso não autorizado | `staff_can()` sempre |
| Aceder dados de outros clientes | Data breach | Validar ownership |
| Nome ficheiro lowercase | Não carrega | Primeira letra MAIÚSCULA |
| echo em controllers admin | Output malformado | Usar views |
| Não validar AJAX requests | CSRF vulnerável | `is_ajax_request()` |

---

## Checklist Controller

```
1. [ ] defined('BASEPATH') no topo
2. [ ] Extends classe correcta (Admin/Clients/App_Controller)
3. [ ] Nome ficheiro com primeira letra maiúscula
4. [ ] Permissões verificadas em cada método
5. [ ] Ownership validado para dados de clientes
6. [ ] Inputs via $this->input->post/get
7. [ ] Alertas definidos antes de redirect
8. [ ] AJAX methods verificam is_ajax_request()
```

---

**Versão:** 1.0.0 | **Autor:** Descomplicar®
**Fonte:** help.perfexcrm.com/module-controllers

---

## Healing Log

Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.

```jsonl
{"date":"","issue":"","fix":"","source":"user|auto"}
```

*Adicionar nova linha após cada erro corrigido.*
