Módulos

Cómo Crear tu Primer Módulo en Perfex CRM

J. Negro 20 Dic 2025 20 min lectura
Volver al Blog

Introducción

Crear módulos en Perfex CRM es la forma más potente de extender las funcionalidades del sistema sin modificar el código base. En este tutorial completo, aprenderás a crear un módulo profesional desde cero, siguiendo las mejores prácticas de la industria.

💡 ¿Por qué crear módulos?

Los módulos te permiten añadir funcionalidades que se mantienen separadas del core, facilitando las actualizaciones de Perfex CRM y permitiendo reutilizar tu código en otros proyectos.

Estructura Obligatoria del Módulo

Todo módulo en Perfex CRM debe seguir una estructura específica. Aquí tienes la estructura mínima requerida:

📁 modules/ 📁 mi_modulo/ 📄 mi_modulo.php ← Archivo principal (OBLIGATORIO) 📁 controllers/ 📄 Mi_modulo.php ← Controlador principal 📁 models/ 📄 Mi_modulo_model.php ← Modelo de datos 📁 views/ 📄 dashboard.php ← Vistas del módulo 📁 language/ 📁 spanish/ 📄 mi_modulo_lang.php 📁 english/ 📄 mi_modulo_lang.php 📁 assets/ 📁 css/ 📁 js/ 📁 migrations/ ← Migraciones de BD

1. El Archivo Principal del Módulo

El archivo principal (mi_modulo.php) es el corazón de tu módulo. Define la información básica y registra los hooks necesarios:

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

/*
 * Module Name: Mi Módulo
 * Description: Descripción de lo que hace el módulo
 * Version: 1.0.0
 * Author: Tu Nombre
 * Author URI: https://tudominio.com
 * Requires at least: 3.0
 */

define('MI_MODULO_VERSION', '1.0.0');

// Registrar activación del módulo
register_activation_hook(MI_MODULO_MODULE_NAME, 'mi_modulo_activation_hook');

function mi_modulo_activation_hook()
{
    $CI = &get_instance();
    $CI->load->library('App_module_installer');

    // Instalar base de datos
    require_once(__DIR__ . '/install.php');
}

// Registrar menú de administración
hooks()->add_action('admin_init', 'mi_modulo_init');
hooks()->add_action('app_admin_head', 'mi_modulo_add_head');

function mi_modulo_init()
{
    // Registrar permisos
    $capabilities = [];
    $capabilities['capabilities'] = [
        'view'   => _l('permission_view'),
        'create' => _l('permission_create'),
        'edit'   => _l('permission_edit'),
        'delete' => _l('permission_delete'),
    ];
    register_staff_capabilities('mi_modulo', $capabilities, _l('mi_modulo_title'));
}

function mi_modulo_add_head()
{
    echo '<link rel="stylesheet" href="' . module_dir_url('mi_modulo', 'assets/css/styles.css') . '">';
}

// Registrar ítem de menú
hooks()->add_action('admin_init', 'mi_modulo_menu');

function mi_modulo_menu()
{
    $CI = &get_instance();

    if (has_permission('mi_modulo', '', 'view')) {
        $CI->app_menu->add_sidebar_menu_item('mi_modulo', [
            'name'     => _l('mi_modulo_title'),
            'href'     => admin_url('mi_modulo'),
            'icon'     => 'fa fa-puzzle-piece',
            'position' => 50,
        ]);
    }
}

2. Crear el Controlador

El controlador maneja las peticiones HTTP y conecta la lógica con las vistas. Debe extender AdminController:

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

class Mi_modulo extends AdminController
{
    public function __construct()
    {
        parent::__construct();

        // Verificar permisos
        if (!has_permission('mi_modulo', '', 'view')) {
            access_denied('mi_modulo');
        }

        // Cargar modelo
        $this->load->model('mi_modulo_model');
    }

    public function index()
    {
        // Preparar datos para la vista
        $data['title'] = _l('mi_modulo_title');
        $data['items'] = $this->mi_modulo_model->get_all();

        // Renderizar vista
        $this->load->view('dashboard', $data);
    }

    public function create()
    {
        if (!has_permission('mi_modulo', '', 'create')) {
            access_denied('mi_modulo');
        }

        if ($this->input->post()) {
            $data = $this->input->post();
            $id = $this->mi_modulo_model->add($data);

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

        $this->load->view('create');
    }
}

⚠️ Importante: Singleton Pattern

Fuera de controladores, siempre usa $CI = &get_instance(); para acceder a las funciones de CodeIgniter. Nunca uses $this en helpers o librerías.

3. Crear el Modelo

El modelo gestiona todas las operaciones con la base de datos. Recuerda usar siempre db_prefix():

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

class Mi_modulo_model extends App_Model
{
    private $table;

    public function __construct()
    {
        parent::__construct();
        $this->table = db_prefix() . 'mi_modulo_items';
    }

    public function get_all()
    {
        return $this->db->get($this->table)->result_array();
    }

    public function get($id)
    {
        $this->db->where('id', $id);
        return $this->db->get($this->table)->row();
    }

    public function add($data)
    {
        $data['datecreated'] = date('Y-m-d H:i:s');
        $this->db->insert($this->table, $data);
        return $this->db->insert_id();
    }

    public function update($id, $data)
    {
        $this->db->where('id', $id);
        return $this->db->update($this->table, $data);
    }

    public function delete($id)
    {
        $this->db->where('id', $id);
        return $this->db->delete($this->table);
    }
}

4. Archivo de Instalación

El archivo install.php crea las tablas necesarias cuando se activa el módulo:

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

$CI = &get_instance();

// Crear tabla principal
if (!$CI->db->table_exists(db_prefix() . 'mi_modulo_items')) {
    $CI->db->query("
        CREATE TABLE `" . db_prefix() . "mi_modulo_items` (
            `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
            `nombre` VARCHAR(255) NOT NULL,
            `descripcion` TEXT,
            `activo` TINYINT(1) DEFAULT 1,
            `datecreated` DATETIME NOT NULL,
            PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    ");
}

5. Traducciones (i18n)

Siempre incluye traducciones para español e inglés:

// language/spanish/mi_modulo_lang.php
<?php
$lang['mi_modulo_title'] = 'Mi Módulo';
$lang['mi_modulo_item'] = 'Elemento';
$lang['mi_modulo_add_new'] = 'Añadir Nuevo';
$lang['mi_modulo_name'] = 'Nombre';
$lang['mi_modulo_description'] = 'Descripción';

// language/english/mi_modulo_lang.php
<?php
$lang['mi_modulo_title'] = 'My Module';
$lang['mi_modulo_item'] = 'Item';
$lang['mi_modulo_add_new'] = 'Add New';
$lang['mi_modulo_name'] = 'Name';
$lang['mi_modulo_description'] = 'Description';

✅ Checklist Pre-Publicación

Antes de publicar tu módulo, verifica: ✓ Permisos de archivos (644) y carpetas (755) ✓ Todas las cadenas traducidas ✓ README.md incluido ✓ CHANGELOG.md actualizado ✓ Sin errores PHP (php -l archivo.php)

Conclusión

Ahora tienes los conocimientos básicos para crear módulos en Perfex CRM. Recuerda siempre seguir las convenciones del framework, usar los helpers nativos y mantener tu código limpio y documentado.

En próximos artículos exploraremos temas más avanzados como DataTables server-side, APIs REST y el sistema de migraciones.

JN

J. Negro

Desarrollador especializado en Perfex CRM con más de 5 años de experiencia creando módulos empresariales. Fundador de SalesCloud.