I am using Module Builder in SugarCRM to construct some custom modules for a project. A module consists of a central table to hold data, fields that map onto that table, layouts of those fields on various forms and display pages, and relationships between the module and other modules.
All these things can be manipulated through the GUI and deployed to the site at the click of a button.
That is all well and good, except that this is all you can do through Module Builder. Usually you will want to add additional functionality such as a custom template or fields on a relationship table. On the whole, you cannot do this, because as soon as you deploy the module, all those customisations get deleted and the basic module is rebuilt again from scratch.
If design by the waterfall approach really worked, then that would be fine. You add all the fields that you need, create the layouts, everyone is happy with that, then you move away from Module Builder since you don’t need to use it again. Unfortunately reality is not like that – changes to fields and relationships still need to be done well into other customisations of the module. Module Builder is just not helpful here, beyond getting some really basic first-draft fields and layouts in place.
One thing that *can* be customised and not get overwritten when you deploy the module, is the logic hooks.
One example of a logic hook that I find myself using often is the manipulation of an entity just before it is saved to the database. For example, when creating a module to serve as a many-to-many joining table, the user is not interested in giving that join a name. The name is mandatory, but not something the user wants or should need to enter.
So a logic hook that sets the name of an entity before it is saved to the database can be a great help. In this example I have an module containing Jobs and another containing Skills. The joining module that sits between the two is JobSkills. So a user creates a Job on the CRM, then selects a number if Skills connected to that job. They do this by creating JobSkills for the job, selecting the Skill for each one they create, and entering some further details in the JobSkill such as how important it is, and the number of years experience that is needed.
We have: Jobs >—< Skills (many-to-many) implemented as:
- Jobs —< JobSkills (one-to-many)
- Skills —< JobSkills (one-to-many)
(Each JobSkill links one Job to one Skill)
The reason for using JobSkills is to allow information to be entered into that relationship, so each Skill linked to a Job has some context. It is something that core SugarCRM is sorely missing, and I am surprised it has not been added to the core a long time ago.
So, when creating a JobSkill record, the user is not presented with the name to enter, so we need a logic hook to create that name. Where to put that logic hook?
Normally, for the JobSkills module, its logic hooks would go in this file:
modules/custom/JobSkills/logic_hooks.php
All the core plugins put their log hook declarations into the custom area too – and this is wrong IMO – the core module hooks need to go into the core modules, leaving the custom area for custom hooks. But SugarCRM is like this – design by committee, with many partly thought-through implementation decisions and nobody to pick up the pieces and put them right. We’ll put that argument aside for another day.
We cannot put our logic hook here, however, because deploying the module from Module Builder will delete it; it just simply zaps it. There is another structure we can put it into: the Extension structure. This file structure allows you to define your own local customisations to any module and those customisations get pulled into the relevant modules each time a “Quick Repair and Build” is run.
This is not a panacea – Module Builder will also delete most of the customisations you put in there. Why, is beyond me – building a module from scratch should not involve putting anything into the Extensions area. We’ll just put it down to another of those annoying quirks of SugarCRM. It just happens however, that the logic hooks put here are not touched.
To declare the logic hooks for my module, I put the definition file in this directory:
custom/Extension/modules/JobSkills/Ext/LogicHooks/
Any filename will do. I create SetName.php:
<?php // JDJ 2013-02-09 $hook_version = 1; $hook_array = Array(); $hook_array['before_save'][] = Array(1, 'setName', 'custom/Extension/modules/JobSkills/Ext/LogicHooksCode/autoSetName.php', 'JobSkills_SetName', 'setName');
This loads the file in the specified directory, when needed, which contains class JobSkills_SetName and runs method setName. Note that the class has been put into a different directory. That is because every file in LogicHooks will get combined together and copied into the custom extended logic hook of the module and run on every page load. The logig hood class file, however, only needs loading when it is needed, and so is kept out of this folder. Normally you would put it up a level, but Module Builder will remove it as a “customisation”, so we create our own directory for it.
We probably could have specified the directory as:
__DIR__ . '/LogicHooksCode/'
instead of that long path, which would be easier to manage.
The contents of autoSetName.php is:
<?php class CAGNT_JobSkills_SetName { public function setName(&$bean, $event, $arguments) { // Here set the name to the job name + the skill name, taking into account that either of them may not be set. $position = !empty($bean->jobskills_jobroles_name) ? $bean->jobskills_jobroles_name : 'Unknown job'; $skill = !empty($bean->jobskills_skills_name) ? $bean->jobskills_skills_name : 'Unknown skill'; $bean->name = trim(trim(trim($position . ' / ' . $skill), '/')); } }
Finding which bean properties contains the data you need, is a bit of trial-and-error, since there is no documentation that will tell you where to look. I just did a var_dump($bean) and looked for the job and skill names.
Those are only the two files needed – one to declare the logic hook for the module, and one to provide the functionality for the hook. There is little logic in the hook, since the intention is to always set the name of the entity, whether creating or updating record.
One last step is needed: run “Quick Repair and Rebuild” to pull the logic hook definition file into the module. This:
custom/Extension/modules/JobSkills/Ext/LogicHooks/setName.php
will get put into:
custom/modules/JobSkills/Ext/LogicHooks/logichooks.ext.php
You can create additional logic hooks in other files, and they all get concatenated into logichooks.ext.php by the rebuild.
Hi,
Great post.
I made a logic hook, following your indications. But, it didn’t work.
I set the same locations (custom/Extension/modules/[module_name]/Ext/LogicHooks/, custom/Extension/modules/[module_name]/Ext/LogicHooksCode/).
My situation:
SugarCRM : 6.5 Pro
1 custom module of type person, named cgs_person3.
In detailview of the module, i have a custom dropdown field with answ: blank , yes, no.
I made an after_save logic hook, which states this:
* if answ=yes, then it sends an acceptance email to the current email address, from the person module,
* if answ=no, then it sends an reject email to the current email address, from the person module,
My problem is that it erase the folders i create, and nothing happens.
Can you tell me what is wrong ?
Regards,
Sorina Chirila.
I was using 6.5 CE. This could be a difference between CE and Pro, with the Pro version deleting more than CE each time you deploy a module from Module Builder.
In retrospect, I think I spent far too long in Module Builder. I think you need to get the fields as close as you can to what you need, then export and install the module and work on it from there. You cannot export easily from there, so I do suspect I am missing some build tools.
I have been wondering if there is another area, perhaps under modulebuilder/builds/{PackageName}/modules/{ModuleName}/… where you can put custom logic hooks that will be automatically pulled into the Module Builder deployment? That might be a better approach.
So try creating a PHP file in:
modulebuilder/builds/{YourCgsPackageName}/modules/cgs_person3/Ext/LogicHooks/FooBar.php
and then see if it gets copied and merged into the module when you deploy it. If it works, I’ll be doing that from now on 🙂
I agree, any files put in the directory you first mention in the extensions area get zapped when redeploying the module, any updates on what would actually work please?
The “Ext” directories is generally where you put your extension code.
Why did you delete my message? the basis of your whole article is wrong, the whole directory gets deleted when you deploy the module. So whats the answer
Nothing deleted – just took longer to approve than I had hoped.