Perfex CRM, built on the CodeIgniter PHP framework, supports a modular architecture that allows developers to build custom modules to extend its core functionality. As the official Perfex CRM documentation notes, modules can include controllers, models, views, language files, libraries, hooks, and more.
In this guide, we’ll walk through how to structure, build, and deploy a custom module in Perfex CRM, covering all the major parts: initialization, controllers, models, views, hooks, language files, options, security, and even payment gateway integration.
1. Perfex CRM Module Prerequisites & Setup
Before you start:
- Perfex CRM Version: Make sure you’re using a version that supports modules (module documentation is valid from v2.3.2+).
- Enable Development Mode: To catch errors or warnings, turn on development mode in your Perfex CRM instance.
- Familiarity with CodeIgniter: Since Perfex is based on CodeIgniter, knowing its MVC structure, how controllers and models work, and how to access the
$CIinstance is very important.
2. Module Directory Structure
Every module lives under the modules/ directory in your Perfex CRM installation. Perfex CRM+1 Here is a typical structure:
modules/
my_module/
my_module.php // init file
controllers/
Admin_my_module.php // admin controller
Clients_my_module.php // client area controller (optional)
models/
My_module_model.php
views/
admin/
index.php
public/
index.php
libraries/
My_module_lib.php
language/
english/
my_module_lang.php
assets/
css/
js/
- The init file (
my_module.php) is mandatory. Its name must match the module folder name. - Controllers: separate admin (
AdminController) and client (ClientsController) controllers. - Models: for database interaction.
- Views: separate views for admin and client.
- Libraries: for shared logic or helper classes.
- Language files: store translatable text.
- Assets: CSS / JS if your module needs UI.
3. Perfex CRM Module Init File (Metadata + Hooks)
The init file is the starting point. It provides the module’s metadata, and where you register activation, deactivation, and uninstall hooks.
Example modules/my_module/my_module.php :
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/*
Module Name: My Module
Description: This is a sample custom module for Perfex CRM.
Version: 1.0.0
Requires at least: 2.3.*
Author: Your Name
*/
register_activation_hook('my_module', 'my_module_activate');
register_deactivation_hook('my_module', 'my_module_deactivate');
register_uninstall_hook('my_module', 'my_module_uninstall');
function my_module_activate() {
// Code to run when module is activated
// e.g., create database tables
}
function my_module_deactivate() {
// Code to run when module is deactivated
}
function my_module_uninstall() {
// Code to run when module is uninstalled (cleanup)
}
// Hooks / filters example
hooks()->add_action('admin_init', 'my_module_admin_init');
function my_module_admin_init() {
// add menu items or other admin logic
}
Key points:
- Use unique function prefixes (e.g.,
my_module_) to avoid collisions. - You can register Cron tasks, payment gateways, or language files using helper functions provided by Perfex.
4. Controllers
Controllers handle HTTP logic. For modules, you typically create controllers under controllers/.
- AdminController: for staff / admin side.
- ClientsController: for client portal side.
Example modules/my_module/controllers/Admin_my_module.php:
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Admin_my_module extends AdminController
{
public function __construct()
{
parent::__construct();
}
public function index()
{
$data['title'] = 'My Module Admin Page';
$this->load->view('my_module/admin/index', $data);
}
public function save_settings()
{
$CI = &get_instance();
// Use models or libraries to save module-specific data
$this->load->model('my_module/my_module_model');
$this->my_module_model->save_settings($CI->input->post());
set_alert('success', 'Settings saved.');
redirect(admin_url('my_module'));
}
}
f you want a client-side controller, you might do:
<?php
defined('BASEPATH') or exit('No direct script access allowed');
use app\services\ValidatesContact;
class Clients_my_module extends ClientsController
{
use ValidatesContact;
public function dashboard()
{
$data['my_info'] = 'Hello, client!';
$this->load->view('my_module/public/index', $data);
}
}
Note: If you extend ClientsController, you can use the ValidatesContact trait to ensure only logged-in contacts access controller methods.
5. Models
Models live in modules/my_module/models/, and you use them to interact with the database.
Example My_module_model.php:
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class My_module_model extends CI_Model
{
public function __construct()
{
parent::__construct();
}
public function save_settings($data)
{
// For example, store settings in perfex options table
foreach ($data as $key => $value) {
add_option('my_module_' . $key, $value);
}
}
public function get_settings()
{
$settings = [];
$settings['foo'] = get_option('my_module_foo');
$settings['bar'] = get_option('my_module_bar');
return $settings;
}
}
Use Perfex helper functions like add_option(), get_option(), update_option() for storing module settings.
Prefix your option names (e.g., my_module_foo) to avoid conflicting with Perfex core or other modules.
6. Views
Your views folder should contain separate subfolders for admin and public (client side).
For example:
modules/my_module/views/
admin/
index.php
public/
index.php
Admin view (index): modules/my_module/views/admin/index.php:
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<div class="panel_s">
<div class="panel-body">
<h4><?php echo $title; ?></h4>
<form action="<?php echo admin_url('my_module/save_settings'); ?>" method="post">
<div class="form-group">
<label for="foo">Foo</label>
<input type="text" name="foo" id="foo" value="<?php echo isset($settings['foo']) ? $settings['foo'] : ''; ?>" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
Public/Client view: modules/my_module/views/public/index.php:
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<div class="my-module-client-dashboard">
<h3>Welcome to My Module</h3>
<p><?php echo $my_info; ?></p>
</div>
7. Language Files
To support translations, create language files under modules/my_module/language/[language]/.
For example: modules/my_module/language/english/my_module_lang.php
<?php
# English
$lang['my_module_title'] = 'My Module';
$lang['my_module_settings_saved'] = 'Settings have been saved successfully.';
Use your language variables in views and controllers to support internationalization.
Working With Hooks (Actions & Filters)
Perfex CRM provides hooks (similar to WordPress-style) that you can use to execute code at various points.
Some common hook functions:
hooks()->add_action('hook_name', 'my_function', $priority = 10);
hooks()->add_filter('filter_name', 'my_filter_function', $priority = 10, $accepted_args = 1);
hooks()->do_action('hook_name', $params = '');
hooks()->apply_filters('filter_name', $value, $params = []);
For example, to add a menu item when the admin panel loads:
hooks()->add_action('admin_init', function() {
$CI = &get_instance();
$CI->app_menu->add_sidebar_menu_item('my_module', [
'name' => 'My Module',
'collapse' => false,
'position' => 20,
'href' => admin_url('my_module'),
'icon' => 'fa fa-puzzle-piece',
]);
});
9. Module Options / Settings
As mentioned, use add_option(), get_option(), and update_option() to manage settings in the module. These store values in tbloptions table in Perfex database.
- add_option($name, $value, $autoload) — add a new option.
- get_option($name, $default = null) — retrieve an option.
- update_option($name, $value) — update or create option if it doesn’t exist (from v2.3.3+).
10. Security Best Practices
When developing modules, you must ensure security:
- Validate and sanitize all input data (e.g., from forms).
- Use CodeIgniter’s built-in CSRF protection. Note: Perfex enables CSRF by default; ensure your forms work with it. Perfex CRM
- Use
defined('BASEPATH') or exit('No direct script access allowed');at the top of your PHP files to prevent direct access. - Use parameterized queries when accessing the database (via CI’s Query Builder) to avoid SQL Injection.
11. Module Upgrading
When you release a new version of your module, you can handle upgrades via the init file version header. Perfex CRM compares the version in the init file with what’s already installed.
In the register_activation_hook, you can include logic to run database migrations or other setup for the new version.
Example:
function my_module_activate() {
$CI = &get_instance();
$CI->db->query("ALTER TABLE `" . db_prefix() . "my_module_table` ADD COLUMN `new_field` VARCHAR(255) NULL;");
}
12. Payment Gateway Module (Optional)
If you want to build a custom payment gateway module, Perfex provides support for this.
Steps:
- Create a folder
/modules/my_gateway/libraries/and a class named e.g.My_gateway_gateway.php. The filename must end with_gateway.php. - In your init file, register the payment gateway:
register_payment_gateway('my_gateway', 'my_gateway');
- In your gateway library class, define methods like
process_payment($data)to handle the payment logic. - Optionally, create a controller under
controllers/for handling redirects or webhook callbacks. - If your gateway needs a webhook callback endpoint, you may need to exclude that URL from CSRF protection (since webhooks might POST data).
Example: Full “Simple Module” Walk‑Through
Putting it all together, here is a short walkthrough to build a very simple “Hello World” module.
- Create directory:
modules/hello_world/ - Init File:
hello_world.php:
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/*
Module Name: Hello World
Description: A simple demo module for Perfex CRM.
Version: 1.0.0
Requires at least: 2.3.*
Author: Your Name
*/
register_activation_hook('hello_world', 'hello_world_activate');
register_deactivation_hook('hello_world', 'hello_world_deactivate');
function hello_world_activate() {
// Module installed / activated
}
function hello_world_deactivate() {
// Module deactivated
}
hooks()->add_action('admin_init', 'hello_world_add_menu');
function hello_world_add_menu() {
$CI = &get_instance();
$CI->app_menu->add_sidebar_menu_item('hello_world', [
'name' => 'Hello World',
'href' => admin_url('hello_world'),
'position' => 50,
'icon' => 'fa fa-smile'
]);
}
Controller: modules/hello_world/controllers/Admin_hello_world.php:
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Admin_hello_world extends AdminController
{
public function index()
{
$data['title'] = _l('hello_world_title'); // use language
$this->load->view('hello_world/admin/index', $data);
}
}
Model: optional, but you could create Hello_world_model if needed.
View: modules/hello_world/views/admin/index.php:
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<div class="panel_s">
<div class="panel-body">
<h4><?php echo $title; ?></h4>
<p>Hello, this is my custom module!</p>
</div>
</div>
Language: modules/hello_world/language/english/hello_world_lang.php:
<?php
$lang['hello_world_title'] = 'Hello World Module';
After you upload this module (zipped) into Setup → Modules in your Perfex admin UI, install and activate it, you should see a new “Hello World” menu item in the sidebar, and clicking it will display the simple page.
14. Testing & Debugging
Use development mode (enabled earlier) to catch PHP errors & deprecated function warnings.
Log via CodeIgniter’s logging (e.g., log_message('debug', 'my_module debug info');) for debugging.
Use browser dev tools to debug JS or view assets if your module includes CSS/JS.
Check database after activation if you’re creating tables or options, to ensure they exist.
15. Best Practices for Module Development
| Best Practice | Description |
|---|---|
| Prefix functions and classes | Prevent naming collisions with core or other modules. |
| Security | Use CSRF protection, sanitize inputs, prevent direct access. |
| Use hooks | Leverage hooks()->add_action and hooks()->add_filter for clean integration. |
| Versioning | Use module version in the init file and handle upgrades. |
| Separation of concerns | Keep controllers, models, views, and libraries well organized. |
| Internationalization | Use language files for UI strings. |
| Maintain compatibility | Test module against new versions of Perfex, because major updates may break modules. |
16. Advanced Use Cases
Payment Gateway Module: As covered, create a custom gateway by providing a library and controller.
Cron / Scheduled Tasks: You can register cron tasks in the init file to run background jobs.
Custom Libraries: Add PHP classes in libraries/ for business logic.
Language Packs: Add support for multiple languages by creating additional language subfolders.
Module Dependencies: If your module depends on other modules or libraries, check for their existence in activation hook.
17. Packaging & Distribution
Once your module is ready, zip the module folder.
The zip should preserve the structure under modules/my_module/....
Install via Perfex CRM Admin → Setup → Modules → Upload Module.
After uploading, click Install and then Activate. support.corbitaltech.dev
If you’re distributing (e.g., via CodeCanyon), clearly document version, requirements, and update process.
18. Troubleshooting Common Issues
| Problem | Possible Cause | Solution |
|---|---|---|
| Module not showing in list | Init file name mismatch or header missing | Ensure modules/<name>/<name>.php exists, headers are correct. |
| CSRF token errors on form | CSRF protection is enabled | Use CI form helper, include CSRF token, or adjust CSRF settings. |
| Controller 404 | Wrong class name or path | Check controller file name, class extends correct base (AdminController or ClientsController). |
| Settings not saving | Option keys wrong | Use add_option / update_option with correctly prefixed names. |
| Security vulnerability | Unsanitized input | Validate, sanitize, and escape inputs before database usage. |
Conclusion
Developing a custom module in Perfex CRM gives you the flexibility to extend the CRM exactly as your business needs — whether you’re adding a simple admin page, integrating a payment gateway, or building a full client-facing feature. By following the modular structure, using CodeIgniter conventions, and leveraging Perfex’s hooks and helper functions, you can build robust, maintainable, and upgrade-friendly modules.
Here’s a quick recap:
- Set up your module directory under
modules/. - Create an init file with metadata and hooks.
- Build controllers, models, views, and optionally libraries & language files.
- Use
add_option / get_optionfor settings. - Secure your module, handle CSRF, and version your module.
- Package your module as a zip for installation.
- Test thoroughly, especially after Perfex updates.
FAQ’s About Perfex CRM Module Development:
1. What is a Perfex CRM module?
A module is an add-on that extends Perfex CRM with custom features.
2. Can I build my own Perfex CRM module?
Yes, Perfex allows full custom module development.
3. Which programming language does Perfex CRM use?
It is built with PHP (CodeIgniter framework).
4. Where do I place a new module in Perfex CRM?
Upload the module into the /modules/ directory.
5. Does Perfex CRM support custom APIs?
Yes, you can create and extend API endpoints.
6. Do I need coding experience to develop Perfex modules?
Yes, basic PHP, MVC, and CodeIgniter knowledge is required.
7. How do I create a module installation file?
Use the main module init file e.g., mymodule.php.
8. Can I add custom menus in Perfex CRM?
Yes, using hooks like admin_init and admin_head.
9. Does Perfex CRM support module activation/deactivation?
Yes, each module includes activation and deactivation functions.
10. Can I build admin and customer views in the module?
Yes, via the views/admin/ and views/public/ folders.
11. Can I add custom database tables in Perfex modules?
Yes, using migration files or manual schema creation.
12. How do I load module models in Perfex CRM?
Use $this->load->model('module_name/module_model');.
13. Can I include CSS and JS in my module?
Yes, through assets and view loading functions.
14. Does Perfex CRM support language files in modules?
Yes, via language/english/module_lang.php.
15. How do I debug a Perfex CRM module?
Enable CodeIgniter debugging or use log_message().
16. Can I integrate third-party APIs in a Perfex module?
Yes, through custom libraries and controllers.
17. Does Perfex CRM allow module updates?
Yes, modules can include versioning and update scripts.
18. Where can I find Perfex module documentation?
At the official docs: help.perfexcrm.com.
19. Can modules affect system performance?
Yes, poorly written code can slow the CRM down.
20. Can I sell my custom Perfex CRM module?
Yes, many developers sell modules on marketplaces like CodeCanyon.


![Why Are People Leaving SuiteCRM? [2025 Insights & Alternatives] Why Are People Leaving SuiteCRM? [Insights & Alternatives]](https://devdiligent.com/blog/wp-content/uploads/2025/10/Untitled-design-10.png)