feat: adiciona 12 plugins Descomplicar ao marketplace
Plugins: automacao, crm-ops, design-media, dev-tools, gestao, infraestrutura, marketing, negocio, perfex-dev, project-manager, wordpress + hello-plugin (existente). Totais: 83 skills, 44 agents, 12 datasets.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
469
perfex-dev/skills/perfex-database/SKILL.md
Normal file
469
perfex-dev/skills/perfex-database/SKILL.md
Normal file
@@ -0,0 +1,469 @@
|
||||
---
|
||||
name: perfex-database
|
||||
description: Perfex CRM database operations. db_prefix(), options, queries, Active Record. Based on official documentation only. Use when user mentions "perfex database", "db_prefix", "query perfex", "options perfex".
|
||||
author: Descomplicar® Crescimento Digital
|
||||
version: 1.0.0
|
||||
quality_score: 70
|
||||
user_invocable: true
|
||||
desk_task: null
|
||||
---
|
||||
|
||||
# /perfex-database - Base de Dados Perfex CRM
|
||||
|
||||
Operações de base de dados em módulos. **Zero assumptions, zero hallucinations** - apenas documentação oficial.
|
||||
|
||||
---
|
||||
|
||||
## Documentação Base
|
||||
|
||||
- [Module Basics](https://help.perfexcrm.com/module-basics/)
|
||||
- [CodeIgniter Query Builder](https://codeigniter.com/userguide3/database/query_builder.html)
|
||||
|
||||
---
|
||||
|
||||
## Regra Fundamental: db_prefix()
|
||||
|
||||
**SEMPRE usar `db_prefix()` em todas as queries.**
|
||||
|
||||
O Perfex CRM suporta prefixos de tabela customizados. O default é `tbl`, mas pode ser alterado.
|
||||
|
||||
```php
|
||||
// CORRECTO
|
||||
$CI->db->get(db_prefix() . 'meu_modulo');
|
||||
|
||||
// ERRADO - quebra se prefix não for "tbl"
|
||||
$CI->db->get('tblmeu_modulo');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aceder à Base de Dados
|
||||
|
||||
### Em Controllers/Models
|
||||
|
||||
```php
|
||||
// Acesso directo
|
||||
$this->db->get(db_prefix() . 'clients');
|
||||
```
|
||||
|
||||
### Fora de Controllers/Models
|
||||
|
||||
```php
|
||||
$CI = &get_instance();
|
||||
$CI->db->get(db_prefix() . 'clients');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Criar Tabelas (Activation Hook)
|
||||
|
||||
```php
|
||||
function meu_modulo_activation_hook()
|
||||
{
|
||||
$CI = &get_instance();
|
||||
|
||||
// Verificar se tabela existe
|
||||
if (!$CI->db->table_exists(db_prefix() . 'meu_modulo')) {
|
||||
$CI->db->query('
|
||||
CREATE TABLE `' . db_prefix() . 'meu_modulo` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`client_id` INT(11) UNSIGNED NOT NULL,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`description` TEXT,
|
||||
`amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
||||
`status` VARCHAR(50) NOT NULL DEFAULT "pending",
|
||||
`created_at` DATETIME NOT NULL,
|
||||
`updated_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `client_id` (`client_id`),
|
||||
KEY `status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . ';
|
||||
');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Query Builder (Active Record)
|
||||
|
||||
### SELECT
|
||||
|
||||
```php
|
||||
// Todos os registos
|
||||
$result = $this->db->get(db_prefix() . 'meu_modulo')->result();
|
||||
|
||||
// Com condições
|
||||
$this->db->where('status', 'active');
|
||||
$this->db->where('client_id', $client_id);
|
||||
$result = $this->db->get(db_prefix() . 'meu_modulo')->result();
|
||||
|
||||
// Seleccionar campos específicos
|
||||
$this->db->select('id, name, amount');
|
||||
$this->db->where('status', 'active');
|
||||
$result = $this->db->get(db_prefix() . 'meu_modulo')->result();
|
||||
|
||||
// Um registo
|
||||
$this->db->where('id', $id);
|
||||
$row = $this->db->get(db_prefix() . 'meu_modulo')->row();
|
||||
|
||||
// Com LIKE
|
||||
$this->db->like('name', $search);
|
||||
$result = $this->db->get(db_prefix() . 'meu_modulo')->result();
|
||||
|
||||
// Com ORDER BY
|
||||
$this->db->order_by('created_at', 'DESC');
|
||||
$result = $this->db->get(db_prefix() . 'meu_modulo')->result();
|
||||
|
||||
// Com LIMIT
|
||||
$this->db->limit(10, 0); // 10 registos, offset 0
|
||||
$result = $this->db->get(db_prefix() . 'meu_modulo')->result();
|
||||
|
||||
// JOIN
|
||||
$this->db->select('m.*, c.company as client_name');
|
||||
$this->db->from(db_prefix() . 'meu_modulo as m');
|
||||
$this->db->join(db_prefix() . 'clients as c', 'c.userid = m.client_id', 'left');
|
||||
$result = $this->db->get()->result();
|
||||
```
|
||||
|
||||
### INSERT
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'client_id' => $client_id,
|
||||
'name' => $this->input->post('name'),
|
||||
'description' => $this->input->post('description'),
|
||||
'amount' => $this->input->post('amount'),
|
||||
'status' => 'pending',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$this->db->insert(db_prefix() . 'meu_modulo', $data);
|
||||
$insert_id = $this->db->insert_id();
|
||||
```
|
||||
|
||||
### UPDATE
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'name' => $this->input->post('name'),
|
||||
'description' => $this->input->post('description'),
|
||||
'amount' => $this->input->post('amount'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$this->db->where('id', $id);
|
||||
$this->db->update(db_prefix() . 'meu_modulo', $data);
|
||||
|
||||
// Verificar se actualizou
|
||||
$affected = $this->db->affected_rows();
|
||||
```
|
||||
|
||||
### DELETE
|
||||
|
||||
```php
|
||||
$this->db->where('id', $id);
|
||||
$this->db->delete(db_prefix() . 'meu_modulo');
|
||||
```
|
||||
|
||||
### COUNT
|
||||
|
||||
```php
|
||||
// Contar todos
|
||||
$count = $this->db->count_all(db_prefix() . 'meu_modulo');
|
||||
|
||||
// Contar com condições
|
||||
$this->db->where('status', 'active');
|
||||
$count = $this->db->count_all_results(db_prefix() . 'meu_modulo');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sistema de Opções
|
||||
|
||||
Para guardar configurações simples (key-value).
|
||||
|
||||
### add_option()
|
||||
|
||||
Cria opção nova. **NÃO sobrescreve se existir.**
|
||||
|
||||
```php
|
||||
add_option('meu_modulo_enabled', '1');
|
||||
add_option('meu_modulo_config', serialize(['key' => 'value']));
|
||||
|
||||
// Com autoload (0 = não, 1 = sim)
|
||||
add_option('meu_modulo_setting', 'valor', 0);
|
||||
```
|
||||
|
||||
### get_option()
|
||||
|
||||
Obtém valor da opção.
|
||||
|
||||
```php
|
||||
$enabled = get_option('meu_modulo_enabled');
|
||||
$config = unserialize(get_option('meu_modulo_config'));
|
||||
|
||||
// Retorna string vazia se não existir
|
||||
if (get_option('meu_modulo_setting') === '') {
|
||||
// Opção não existe
|
||||
}
|
||||
```
|
||||
|
||||
### update_option()
|
||||
|
||||
Actualiza ou cria opção (v2.3.3+).
|
||||
|
||||
```php
|
||||
update_option('meu_modulo_enabled', '0');
|
||||
update_option('meu_modulo_last_sync', date('Y-m-d H:i:s'));
|
||||
```
|
||||
|
||||
### delete_option()
|
||||
|
||||
Remove opção.
|
||||
|
||||
```php
|
||||
delete_option('meu_modulo_enabled');
|
||||
```
|
||||
|
||||
### Boas Práticas Opções
|
||||
|
||||
```php
|
||||
// SEMPRE prefixar com nome do módulo
|
||||
add_option('meu_modulo_version', '1.0.0');
|
||||
add_option('meu_modulo_api_key', '');
|
||||
add_option('meu_modulo_enabled', '1');
|
||||
|
||||
// NUNCA usar nomes genéricos
|
||||
add_option('enabled', '1'); // ERRADO
|
||||
add_option('api_key', '...'); // ERRADO
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Model Exemplo Completo
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
class Meu_modulo_model extends App_Model
|
||||
{
|
||||
private $table;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->table = db_prefix() . 'meu_modulo';
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter todos os registos
|
||||
*/
|
||||
public function get_all($where = [])
|
||||
{
|
||||
if (!empty($where)) {
|
||||
$this->db->where($where);
|
||||
}
|
||||
$this->db->order_by('created_at', 'DESC');
|
||||
return $this->db->get($this->table)->result();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter por ID
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
$this->db->where('id', $id);
|
||||
return $this->db->get($this->table)->row();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter por cliente
|
||||
*/
|
||||
public function get_by_client($client_id)
|
||||
{
|
||||
$this->db->where('client_id', $client_id);
|
||||
$this->db->order_by('created_at', 'DESC');
|
||||
return $this->db->get($this->table)->result();
|
||||
}
|
||||
|
||||
/**
|
||||
* Criar novo registo
|
||||
*/
|
||||
public function add($data)
|
||||
{
|
||||
$data['created_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
// Trigger hook antes
|
||||
$data = hooks()->apply_filters('before_meu_modulo_added', $data);
|
||||
|
||||
$this->db->insert($this->table, $data);
|
||||
$insert_id = $this->db->insert_id();
|
||||
|
||||
if ($insert_id) {
|
||||
// Trigger hook após
|
||||
hooks()->do_action('after_meu_modulo_added', $insert_id);
|
||||
log_activity('Meu Módulo criado [ID: ' . $insert_id . ']');
|
||||
}
|
||||
|
||||
return $insert_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualizar registo
|
||||
*/
|
||||
public function update($id, $data)
|
||||
{
|
||||
$data['updated_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
// Trigger hook antes
|
||||
$data = hooks()->apply_filters('before_meu_modulo_updated', $data, $id);
|
||||
|
||||
$this->db->where('id', $id);
|
||||
$this->db->update($this->table, $data);
|
||||
|
||||
if ($this->db->affected_rows() > 0) {
|
||||
// Trigger hook após
|
||||
hooks()->do_action('after_meu_modulo_updated', $id);
|
||||
log_activity('Meu Módulo actualizado [ID: ' . $id . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apagar registo
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
// Trigger hook antes
|
||||
hooks()->do_action('before_meu_modulo_deleted', $id);
|
||||
|
||||
$this->db->where('id', $id);
|
||||
$this->db->delete($this->table);
|
||||
|
||||
if ($this->db->affected_rows() > 0) {
|
||||
// Trigger hook após
|
||||
hooks()->do_action('after_meu_modulo_deleted', $id);
|
||||
log_activity('Meu Módulo apagado [ID: ' . $id . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contar por status
|
||||
*/
|
||||
public function count_by_status($status)
|
||||
{
|
||||
$this->db->where('status', $status);
|
||||
return $this->db->count_all_results($this->table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pesquisa
|
||||
*/
|
||||
public function search($term)
|
||||
{
|
||||
$this->db->group_start();
|
||||
$this->db->like('name', $term);
|
||||
$this->db->or_like('description', $term);
|
||||
$this->db->group_end();
|
||||
|
||||
return $this->db->get($this->table)->result();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transações
|
||||
|
||||
Para operações que devem ser atómicas:
|
||||
|
||||
```php
|
||||
$this->db->trans_begin();
|
||||
|
||||
try {
|
||||
$this->db->insert(db_prefix() . 'meu_modulo', $data1);
|
||||
$id = $this->db->insert_id();
|
||||
|
||||
$this->db->insert(db_prefix() . 'meu_modulo_items', [
|
||||
'parent_id' => $id,
|
||||
'item' => $item,
|
||||
]);
|
||||
|
||||
if ($this->db->trans_status() === false) {
|
||||
$this->db->trans_rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->trans_commit();
|
||||
return $id;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->db->trans_rollback();
|
||||
log_activity('Erro transação: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Funções Úteis
|
||||
|
||||
### log_activity()
|
||||
|
||||
Registar actividade no sistema.
|
||||
|
||||
```php
|
||||
log_activity('Descrição da actividade');
|
||||
log_activity('Item criado [ID: ' . $id . ', Cliente: ' . $client_id . ']');
|
||||
```
|
||||
|
||||
### Datas
|
||||
|
||||
```php
|
||||
// Data actual formatada
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// Formatar para display
|
||||
$formatted = _d($row->created_at); // Data
|
||||
$formatted = _dt($row->created_at); // Data e hora
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns (NUNCA FAZER)
|
||||
|
||||
| Anti-Pattern | Risco | Alternativa |
|
||||
|--------------|-------|-------------|
|
||||
| Query sem db_prefix() | Falha em instalações custom | `db_prefix()` sempre |
|
||||
| SQL directo com input user | SQL Injection | Query Builder |
|
||||
| Opções sem prefixo módulo | Conflitos | Prefixar sempre |
|
||||
| Não usar transações em multi-insert | Dados inconsistentes | `trans_begin/commit` |
|
||||
| Hardcode charset | Problemas encoding | `$CI->db->char_set` |
|
||||
|
||||
---
|
||||
|
||||
## Checklist Database
|
||||
|
||||
```
|
||||
1. [ ] db_prefix() em TODAS as queries
|
||||
2. [ ] $CI->db->char_set na criação de tabelas
|
||||
3. [ ] Opções com prefixo do módulo
|
||||
4. [ ] Query Builder para inputs de utilizador
|
||||
5. [ ] Transações para operações múltiplas
|
||||
6. [ ] log_activity() para auditoria
|
||||
7. [ ] Hooks em operações CRUD
|
||||
8. [ ] Índices em colunas de pesquisa
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Versão:** 1.0.0 | **Autor:** Descomplicar®
|
||||
**Fonte:** help.perfexcrm.com/module-basics
|
||||
Reference in New Issue
Block a user