One to many relationship by code

Hi everyone,
I am creating an admin page for a custom module. In this page I present a list that shows the names of SuiteCRM modules. By selecting the modules I would like to create for each one a new one-to-many relationship with my custom module. I think it is possible to do this but I don’t find the right function.

Thanks

Here is a good start:

Hi,
I saw the video, actually I would need more to understand how to use the already in use features of SuiteCRM to create relationships between modules from code.
Searching a bit I think it should use the methods of the DeployedRelationships class found in /modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php.

For example in a test EntryPoint I created this code taking cue from another example shown at this link

I had to comment out the instruction on line 44
//SugarTestHelper::setUp('dictionary');
it generated an error because the SugarTestHelper class no longer exists.
Launching the code through the entryPoint the desired relation is created, but in the subpanel of the starting form nothing is presented.

Here is the code used

    <?php
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
    if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
    require_once 'modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php';

    class SugarTestRelationshipUtilities
    {
        private static $_relsAdded = array();
        protected static $_relRequiredKeys = array('relationship_type', 'lhs_module', 'rhs_module');
        /**
         * Create a relationship
         *
         * Params should be passed in as this:
         *
         * array(
         *       'relationship_type' => 'one-to-many',
         *       'lhs_module' => 'Accounts',
         *       'rhs_module' => 'Accounts',
         *   )
         *
         * @static
         * @param array $relationship_def
         * @return ActivitiesRelationship|bool|ManyToManyRelationship|ManyToOneRelationship|OneToManyRelationship|OneToOneRelationship
         */
        public static function createRelationship(array $relationship_def)
        {
            if (!self::checkRequiredFields($relationship_def)) {
                return false;
            }
            $relationships = new DeployedRelationships($relationship_def['lhs_module']);
            if (!isset($relationship_def['view_module'])) {
                $relationship_def['view_module'] = $relationship_def['lhs_module'];
            }
            $REQUEST_Backup = $_REQUEST;
            $_REQUEST = $relationship_def;
            $relationship = $relationships->addFromPost();
            $relationships->save();
            $relationships->build();
            LanguageManager::clearLanguageCache($relationship_def['lhs_module']);
            SugarRelationshipFactory::rebuildCache();
            // rebuild the dictionary to make sure that it has the new relationship in it
            //SugarTestHelper::setUp('dictionary');
            // reset the link fields since we added one
            VardefManager::$linkFields = array();
            $_REQUEST = $REQUEST_Backup;
            unset($REQUEST_Backup);
            self::$_relsAdded[] = $relationship->getDefinition();
            return $relationship;
        }
        /**
         * Remove all created relationships
         *
         * @static
         */
        public static function removeAllCreatedRelationships()
        {
            foreach (self::$_relsAdded as $rel) {
                $relationships = new DeployedRelationships($rel['lhs_module']);
                $relationships->delete($rel['relationship_name']);
                $relationships->save();
                $relationships->build();
                LanguageManager::clearLanguageCache($rel['lhs_module']);
                require_once "data/Relationships/RelationshipFactory.php";
                SugarRelationshipFactory::deleteCache();
                SugarRelationshipFactory::rebuildCache();
            }
            // since we are creating a relationship we need to unset this global var
            if (isset($GLOBALS['reload_vardefs'])) {
                unset($GLOBALS['reload_vardefs']);
            }
        }
        /**
         * Make sure we have at least the required keys
         *
         * @static
         * @param array $relationship_def
         * @return bool
         */
        protected static function checkRequiredFields(array $relationship_def)
        {
            foreach (self::$_relRequiredKeys as $key) {
                if (!array_key_exists($key, $relationship_def)) {
                    return false;
                }
            }
            return true;
        }
    }

    $arr_def=array(
         'relationship_type' => 'one-to-many',
         'lhs_module' => 'Bugs',
         'rhs_module' => 'AOS_Quotes'
      );

    $rel= new SugarTestRelationshipUtilities();

    $res=$rel->createRelationship($arr_def);
    ?>
    <pre>
      <?php
      var_dump($res);
      ?>
    </pre>

You problem may be with cache of php. I recommend off to use cache:

ini_set('opcache.enable',false);

before call:

$relationships->build();

or

$relationships->delete();

Hi,
I tried to insert the suggested instruction

    public static function createRelationship(array $relationship_def)
        {
            if (!self::checkRequiredFields($relationship_def)) {
                return false;
            }
            $relationships = new DeployedRelationships($relationship_def['lhs_module']);
            if (!isset($relationship_def['view_module'])) {
                $relationship_def['view_module'] = $relationship_def['lhs_module'];
            }
            $REQUEST_Backup = $_REQUEST;
            $_REQUEST = $relationship_def;
            $relationship = $relationships->addFromPost();
            $relationships->save();
            ini_set('opcache.enable',false);
            $relationships->build();
            LanguageManager::clearLanguageCache($relationship_def['lhs_module']);
            SugarRelationshipFactory::rebuildCache();
            // rebuild the dictionary to make sure that it has the new relationship in it
            //SugarTestHelper::setUp('dictionary');
            // reset the link fields since we added one
            VardefManager::$linkFields = array();
            $_REQUEST = $REQUEST_Backup;
            unset($REQUEST_Backup);
            self::$_relsAdded[] = $relationship->getDefinition();
            return $relationship;
        }

but the result is the same, in the subpanel I don’t see anything.
It seems I’m missing a function that goes to create the information for the subpanel, maybe an instruction that goes to write in the /Layoutdefs ?

You array should be have subpanel.

arr_def=array(
    'lhs_label' => 'Bugs',
    'rhs_label' =>'AOS_Quotes',
    'lhs_subpanel' => 'default',
    'rhs_subpanel' => 'default',
    'lhs_module' => 'Bugs',
    'rhs_module' => 'AOS_Quotes',
    'relationship_type' => 'one-to-many',
);

As example you can look at my solution here:

2 Likes

Thanks, it works!
Congratulations also for your SuiteCRM_three_new_fields_types plugin

Hi,
in reference to the creation of relationships between modules how do I always dynamically set in the layoutdef of the main module my settings?

Do I have to create the file in /custom/Extension/modules/1st_Module/Ext/Layoutdefs/1stmodule_np_noteplus_1_1stModule.php with the definitions?

For example:

$layout_defs["Leads"]["subpanel_setup"]['leads_np_noteplus_1'] = array (
  'order' => 100,
  'module' => 'NP_NotePlus',
  'subpanel_name' => 'default',
  'sort_order' => 'asc',
  'sort_by' => 'id',
  'title_key' => 'LBL_LEADS_NP_NOTEPLUS_1_FROM_NP_NOTEPLUS_TITLE',
  'get_subpanel_data' => 'leads_np_noteplus_1',
  'top_buttons' => 
  array (
    0 => 
    array (
      'widget_class' => 'SubPanelTopButtonQuickCreate',
    ),
    1 => 
    array (
      'widget_class' => 'SubPanelTopSelectButton',
      'mode' => 'MultiSelect',
    ),
  ),
);

Or is there a method of a class to call for dynamic creation of layoutdefs?

Thanks.

@web_elinet
Your function createRelationship calls method $relationships->build() and this metod make files.
You can look at the methods build of child and parent classes for detail:

  • modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php
  • modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php

Hi,
as suggested I inspected the code… but I had another doubt, probably the subpanel definitions are not written in the layoutdefs.
I tried to create 2 new one-to-many relationships with the custom module, at this point the settings of the fields presented in the subpanel were default.
Next I went to studio-> primary module (Bugs) -> Subpanels -> secondary module (NP_NotePlus).
Here I set the fields to be displayed.
At this point after saving and publishing the changes I checked the files on disk.
No new files were written in /custom/Extension/modules/1st_Module/Ext/Layoutdefs/ , instead a new file was created in /custom/modules/NP_NotePlus/metadata/subpanels/ named Bug_subpanel_bugs_np_noteplus_1.php
By checking the classes in the

modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php
modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php

I can’t find any indication about creation of this file in /custom/modules/NP_NotePlus/metadata/subpanels/

This has confused me a lot

@web_elinet

It do ModuleInstaller. It is called by the build method in file modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php.

Hi,
I checked the build() method in the DeployedRelationships Class and it is true that it uses the ModuleInstaller Class… but it seems to me that it does not deal with the creation of the files used to define the fields visible in the sub menu list.

I tried to search in /ModuleBuilder/Module/StudioModule.php and in /ModuleBuilder/MB/MBModule.php but I find only methods to load the settings set for the submenu, I could not find a function that writes these values.

@web_elinet

  • build call the method install_relationships of file ModuleInstall/ModuleInstaller.php
  • install_relationships call the method install_layoutdef

Look at it.

Hi,
By thoroughly inspecting the classes involved, the SubPanel class the subpanels.txt file located in the /include/SubPanel folder I found that the solution is simpler than I thought.
When the build() method of the DeployedRelationships Class is called, all concatenated events generate the vardefs and layoutdefs related to the relationship.
Take for example the one-to-many relationship between the Lead module and my custom NP_NotePlus module. After calling the build method the file leads_np_noteplus_1_Leads.php is created in /custom/Extension/modules/Leads/Ext/Layoutdefs/.

The content of the file is as follows:

<?php
 // created: 2021-02-08 17:59:10
$layout_defs["Leads"]["subpanel_setup"]['leads_np_noteplus_1'] = array (
  'order' => 100,
  'module' => 'NP_NotePlus',
  'subpanel_name' => 'default',
  'sort_order' => 'asc',
  'sort_by' => 'id',
  'title_key' => 'LBL_LEADS_NP_NOTEPLUS_1_FROM_NP_NOTEPLUS_TITLE',
  'get_subpanel_data' => 'leads_np_noteplus_1',
  'top_buttons' => 
  array (
    0 => 
    array (
      'widget_class' => 'SubPanelTopButtonQuickCreate',
    ),
    1 => 
    array (
      'widget_class' => 'SubPanelTopSelectButton',
      'mode' => 'MultiSelect',
    ),
  ),
);

The value 'subpanel_name' => 'default' specifies to take the ‘default’ display of the subpanel, which is specified in the default.php file in the /var/www/html/suitecrm/custom/modules/NP_NotePlus/metadata/subpanels folder.
Which was the folder mentioned in previous posts.

So modifying the structure in the default.php file I get that every subpanel created will have the visualization I want.
Moreover if you want to modify the visualization through Studio, a file will be created in the folder layoutdefs _overrideLead_subpanel_leads_np_noteplus_1.php :

<?php
//auto-generated file DO NOT EDIT
$layout_defs['Leads']['subpanel_setup']['leads_np_noteplus_1']['override_subpanel_name'] = 'Lead_subpanel_leads_np_noteplus_1';
?> 

Where the name of the file with the layout to be used in the subpanel edited by Studio is specified.
In my case the name is Lead_subpanel_leads_np_noteplus_1
and in the folder /var/www/html/suitecrm/custom/modules/NP_NotePlus/metadata/subpanels I will find the file with name Lead_subpanel_leads_np_noteplus_1.php that contains the changes to the layout.