- All SKILL.md files now <500 lines (avg reduction 69%) - Detailed content extracted to references/ subdirectories - Frontmatter standardised: only name + description (Anthropic standard) - New skills: brand-guidelines, spec-coauthor, report-templates, skill-creator - Design skills: anti-slop guidelines, premium-proposals reference - Removed non-standard frontmatter fields (triggers, version, author, category) Plugins affected: infraestrutura, marketing, dev-tools, crm-ops, gestao, core-tools, negocio, perfex-dev, wordpress, design-media Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
10 KiB
name, description
| name | description |
|---|---|
| perfex-migrations | Migrações e upgrades de módulos Perfex CRM — gestão de versões, ficheiros de migração e actualizações de base de dados. Baseado apenas na documentação oficial. |
/perfex-migrations - Migrations Perfex CRM
Sistema de migrações e upgrades de módulos. Zero assumptions, zero hallucinations - apenas documentação oficial.
Documentação Base
Como Funciona
O Perfex CRM compara:
- Versão no init file (header
Version:) - Versão na base de dados (tabela
tblmodules)
Se forem diferentes → Sistema mostra "Database upgrade required".
Estrutura de Migrations
modules/meu_modulo/
├── meu_modulo.php # Init file com Version: X.Y.Z
└── migrations/
├── 001_version_100.php # v1.0.0 → v1.0.1
├── 002_version_101.php # v1.0.1 → v1.0.2
├── 100_version_100.php # v1.0.0 inicial (alternativa)
├── 110_version_110.php # v1.1.0
└── 200_version_200.php # v2.0.0
Formato do Ficheiro
Nomenclatura (CRÍTICO)
O Perfex usa Sequential Migration (não Timestamp).
NNN_version_NNN.php
│ │ │
│ │ └── Versão sem pontos (110 = 1.1.0)
│ └── Literal "version"
└── Número sequencial (3 dígitos, sem gaps)
Exemplos:
| Versão | Ficheiro |
|---|---|
| 1.0.0 | 001_version_100.php ou 100_version_100.php |
| 1.0.1 | 002_version_101.php ou 101_version_101.php |
| 1.1.0 | 003_version_110.php ou 110_version_110.php |
| 2.0.0 | 004_version_200.php ou 200_version_200.php |
Convenção recomendada: Usar versão como prefixo (100, 110, 200...).
Template Migration
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_110 extends App_module_migration
{
/**
* Executado durante upgrade
*/
public function up()
{
// Alterações de base de dados aqui
}
}
Notas Importantes
- Classe:
Migration_Version_NNN(NNN = versão sem pontos) - Extends:
App_module_migration - Método: Apenas
up()- não hádown()(downgrades não suportados) - Pode ser vazio: Se não houver alterações de BD, deixar
up()vazio
Exemplos de Migrations
Adicionar Coluna
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_110 extends App_module_migration
{
public function up()
{
$CI = &get_instance();
// Adicionar coluna se não existir
if (!$CI->db->field_exists('new_column', db_prefix() . 'meu_modulo')) {
$CI->db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo`
ADD COLUMN `new_column` VARCHAR(255) DEFAULT NULL AFTER `name`');
}
}
}
Criar Nova Tabela
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_120 extends App_module_migration
{
public function up()
{
$CI = &get_instance();
if (!$CI->db->table_exists(db_prefix() . 'meu_modulo_items')) {
$CI->db->query('
CREATE TABLE `' . db_prefix() . 'meu_modulo_items` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`parent_id` INT(11) UNSIGNED NOT NULL,
`name` VARCHAR(255) NOT NULL,
`quantity` INT(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . ';
');
}
}
}
Modificar Coluna
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_130 extends App_module_migration
{
public function up()
{
$CI = &get_instance();
// Alterar tipo de coluna
$CI->db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo`
MODIFY COLUMN `amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00');
}
}
Adicionar Índice
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_140 extends App_module_migration
{
public function up()
{
$CI = &get_instance();
// Verificar se índice existe (evitar erro)
$indexes = $CI->db->query('SHOW INDEX FROM `' . db_prefix() . 'meu_modulo`
WHERE Key_name = "idx_status"')->result();
if (count($indexes) == 0) {
$CI->db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo`
ADD INDEX `idx_status` (`status`)');
}
}
}
Migração de Dados
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_150 extends App_module_migration
{
public function up()
{
$CI = &get_instance();
// Migrar dados existentes
$CI->db->query('UPDATE `' . db_prefix() . 'meu_modulo`
SET `status` = "active"
WHERE `status` = "1"');
$CI->db->query('UPDATE `' . db_prefix() . 'meu_modulo`
SET `status` = "inactive"
WHERE `status` = "0"');
}
}
Adicionar Opção
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_160 extends App_module_migration
{
public function up()
{
// Adicionar nova opção (add_option não sobrescreve)
add_option('meu_modulo_new_feature', '1');
add_option('meu_modulo_default_status', 'pending');
}
}
Migration Vazia (Só actualiza versão)
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_170 extends App_module_migration
{
public function up()
{
// Sem alterações de BD nesta versão
// O Perfex apenas actualiza o número da versão
}
}
Sincronizar Activation Hook
CRÍTICO: O activation hook deve SEMPRE conter o schema mais recente.
// modules/meu_modulo/meu_modulo.php
function meu_modulo_activation_hook()
{
$CI = &get_instance();
// Schema completo e actualizado (v1.7.0)
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,
`new_column` VARCHAR(255) DEFAULT NULL, -- Adicionado v1.1.0
`description` TEXT,
`amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00, -- Modificado v1.3.0
`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 `idx_status` (`status`) -- Adicionado v1.4.0
) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . ';
');
}
// Tabela items (adicionada v1.2.0)
if (!$CI->db->table_exists(db_prefix() . 'meu_modulo_items')) {
$CI->db->query('
CREATE TABLE `' . db_prefix() . 'meu_modulo_items` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`parent_id` INT(11) UNSIGNED NOT NULL,
`name` VARCHAR(255) NOT NULL,
`quantity` INT(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=' . $CI->db->char_set . ';
');
}
// Opções (versão mais recente)
add_option('meu_modulo_version', '1.7.0');
add_option('meu_modulo_new_feature', '1'); // v1.6.0
add_option('meu_modulo_default_status', 'pending'); // v1.6.0
}
Workflow de Release
1. Desenvolver Alterações
// Exemplo: Adicionar campo "priority" na v1.8.0
2. Criar Migration
// migrations/180_version_180.php
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_180 extends App_module_migration
{
public function up()
{
$CI = &get_instance();
if (!$CI->db->field_exists('priority', db_prefix() . 'meu_modulo')) {
$CI->db->query('ALTER TABLE `' . db_prefix() . 'meu_modulo`
ADD COLUMN `priority` TINYINT(1) NOT NULL DEFAULT 0 AFTER `status`');
}
}
}
3. Actualizar Activation Hook
// Adicionar campo ao CREATE TABLE no activation hook
`priority` TINYINT(1) NOT NULL DEFAULT 0,
4. Actualizar Versão no Header
/*
Module Name: Meu Módulo
Version: 1.8.0 <!-- ACTUALIZAR -->
Requires at least: 2.3.*
*/
5. Testar
- Instalação limpa (activation hook)
- Upgrade de versão anterior (migration)
Verificações de Segurança em Migrations
public function up()
{
$CI = &get_instance();
// Verificar se tabela existe antes de ALTER
if (!$CI->db->table_exists(db_prefix() . 'meu_modulo')) {
return; // Tabela não existe, nada a fazer
}
// Verificar se coluna existe antes de ADD
if ($CI->db->field_exists('new_column', db_prefix() . 'meu_modulo')) {
return; // Coluna já existe
}
// Seguro para executar
$CI->db->query('ALTER TABLE ...');
}
Anti-Patterns (NUNCA FAZER)
| Anti-Pattern | Risco | Alternativa |
|---|---|---|
| Gaps na sequência (001, 003) | Migrations saltadas | Sequência contínua |
| Não actualizar activation hook | Instalações novas incompletas | Sincronizar sempre |
| ALTER sem verificar existência | Erro se já existe | field_exists() |
| DROP sem backup | Dados perdidos | Backup ou soft delete |
| Hardcode prefix "tbl" | Falha em instalações custom | db_prefix() |
Checklist Migrations
1. [ ] Ficheiro com nome correcto (NNN_version_NNN.php)
2. [ ] Classe com nome correcto (Migration_Version_NNN)
3. [ ] Extends App_module_migration
4. [ ] Verificações de existência antes de ALTER
5. [ ] db_prefix() em todas as queries
6. [ ] Activation hook actualizado com schema completo
7. [ ] Header Version actualizado
8. [ ] Testado: instalação limpa
9. [ ] Testado: upgrade de versão anterior
10. [ ] Sem gaps na sequência de migrations
Versão: 1.0.0 | Autor: Descomplicar® Fonte: help.perfexcrm.com/preparing-module-upgrade