SugarClub
SugarClub
  • User
  • Site
  • Search
  • User
  • Groups & Discussions
    Groups & Discussions
    • Product Forums
      Product-focused Q&A, discussions, best practices, fixes, and help
      Product Forums
      • Sugar Market
      • Sugar Sell & Enterprise
      • Sugar Serve
      • sales-i
    • User Groups
      Professional, Industry, Language
    • Get Involved
      Learn how to become a Raving Fan
    • Social Club
      Live, interactive, virtual meetups with other Sugar customers and Sugar’s Subject Matter experts!
    • Leadership Lounge
      Network with fellow organizational leaders, ask questions, and share insights
    • Developers
      Visit DevClub, the SugarClub group for Sugar Developers
      Developers
      • DevClub
      • Mobile Developers
      • Developer Builds
        Supplemental access level required. Inquiries: developers@sugarcrm.com
    • Additional Groups (Access Required)
      Groups that require special access will be displayed here. Contact sugarclub@sugarcrm.com for assistance. Click here to see all groups
      Additional Groups (Access Required)
      • SugarCloud Platform
  • Product Information
    Product Information
    • Release Central
      Find release-specific content to prepare for your next Sugar update
    • Documentation & Resources
      Looking to expand your Sugar knowledge? Explore our in-depth documentation and other helpful resources!
    • Product Update Blogs
      Updates about each Sugar product
    • Customer Stories »
      Case Studies by SugarCRM
  • Training & Certification
    Training & Certification
    • Training & Certification Home
      Live & On-Demand classes, Quick Videos, Sugar Certifications, and more!
    • Quick Videos
      Short videos about using Sugar
    • Event Recordings
      Recordings from SugarU Live Webinars and Sugar Market Academy
    • My SugarU Dashboard »
    • SugarU News & Updates
  • Adoption
    Adoption
    • Grow Adoption Framework
      Get started on your adoption journey and review the adoption resources from SugarCRM
  • Calendar
  • News
    News
    • Sugar News
    • SugarCRM.com News »
    • Dev Blog
    • SugarCRM Marketplace Blog
  • Help
    Help
    • Welcome to Sugar!
      New to Sugar? Get started here!
    • SugarClub Help & Instructions
      Learn more about SugarClub and find answers to questions about this site
    • New to SugarClub?
      Start your community journey here
    • Technical Support
      Sugar's support resources
      Technical Support
      • Case Portal »
        Access the SugarCRM Case Portal
      • Working with Sugar Support »
        Find out more about engaging with the SugarCRM Support team
      • SugarCloud Information
        Find information about SugarCloud service updates and site status. Contact sugarclub@sugarcrm.com to request access
  • More from Sugar
    More from Sugar
    • DevClub
    • PartnerClub
    • Support
    • SugarOutfitters Marketplace
    • sugarcrm.com
  • DevClub
  • PartnerClub
  • Support
  • Marketplace
  • sugarcrm.com
DevClub
DevClub
Dev Tutorials Declarative and Serializable Metadata
Click here to join this group and curate your SugarClub experience.
  • +On-Boarding Framework
  • +Customization Guides
  • +Modern UI Technical Guide
  • +Automated PHP Compatibility Tool
  • -Customization Best Practices in Sugar
    • Declarative and Serializable Metadata
  • How to write code for SugarCloud webinar Q&A
  • HOW TO: enforce ACL on Tags
  • Remove custom fields created via package installation
  • Sugar Developer Tools
  • Tutorial:  How to register custom platforms in Sugar instances via Platform extension
  • Adding a google reCAPTCHA in a Web-to-Lead form
  • Sugar Developer Blog Style Guide

Declarative and Serializable Metadata

This guide is here to help developers follow best practices when working with Declarative and Serializable Metadata in Sugar. We've identified some common mistakes developers make and put together practical tips on how to avoid them using proven best practices.

General Guidelines for Declarative Metadata:

  • Static Definitions: Always use static and explicit values rather than computed ones.
  • Avoid Direct Database Calls: Metadata should not perform direct database queries

Avoid Using $GLOBALS Array

Metadata definitions should directly declare configurations explicitly instead of relying on global variables.

Problematic Example:

Fullscreen
1
$GLOBALS["dictionary"]["MyModule"] = [/* ... */];
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$GLOBALS["dictionary"]["MyModule"] = [/* ... */];

Recommended Approach: Use the local $dictionary variable directly:

Fullscreen
1
$dictionary["MyModule"] = [/* ... */];
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$dictionary["MyModule"] = [/* ... */];

Eliminate Imperative PHP Code

Avoid embedding business logic or conditional statements directly in metadata files.

Problematic Example:

Fullscreen
1
2
$label_prefix = 'QUESTIONS_CONFIG';
$mod_strings["LBL_{$label_prefix}_" . strtoupper("view")] = 'My Configuration Panel';
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$label_prefix = 'QUESTIONS_CONFIG';
$mod_strings["LBL_{$label_prefix}_" . strtoupper("view")] = 'My Configuration Panel';

Recommended Approach: Define constants explicitly without dynamic runtime manipulation:

Fullscreen
1
$mod_strings['LBL_QUESTIONS_CONFIG_VIEW'] = 'My Configuration Panel';
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$mod_strings['LBL_QUESTIONS_CONFIG_VIEW'] = 'My Configuration Panel';

Remove Entry Point Validations

Entry point checks are unnecessary in metadata definitions, as these files should be simple data files..

Problematic Example:

Fullscreen
1
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

Recommended Approach: Simply omit such checks from metadata entirely.

Avoid Conditional Class Checks and Dynamic Includes

Don't use conditional logic or dynamic includes in metadata definitions.

Problematic Example:

Fullscreen
1
2
3
4
if (!class_exists('VardefManager')) {
require_once 'include/SugarObjects/VardefManager.php';
}
VardefManager::createVardef('MyModule','MyModule', ['basic','assignable']);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
if (!class_exists('VardefManager')) {
    require_once 'include/SugarObjects/VardefManager.php';
}
VardefManager::createVardef('MyModule','MyModule', ['basic','assignable']);

Recommended Approach: Rely on autoloading and straightforward declarations:

Fullscreen
1
VardefManager::createVardef('MyModule','MyModule', ['basic','assignable']);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
VardefManager::createVardef('MyModule','MyModule', ['basic','assignable']);

Note: we plan to introduce a better way of extending vardefs from module templates. It will be more declarative and should avoid VardefManager::createVardef calls inside the vardefs files.

Logic hooks

Separate definition from implementation where Metadata declares where Logic Hook should be triggered from.

Problematic Example:

Fullscreen
1
2
3
4
5
6
7
8
9
> head -n8 custom/modules/logic_hooks.php
<?php
class InstallationReviewHistory
{
static $already_ran = false;
private $update_project_relations;
public function createRecordFromX($bean, $event, $arguments)
{
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
> head -n8 custom/modules/logic_hooks.php

<?php
class InstallationReviewHistory
{
    static $already_ran = false;
    private $update_project_relations;
    public function createRecordFromX($bean, $event, $arguments)
    {

Recommended Approach: custom/modules/logic_hooks.php must contain the Logic Hook definition and not the class itself

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$hook_version = 1;
$hook_array = Array();
$hook_array['after_session_start'] = Array();
$hook_array['after_session_start'][] = Array(
//Processing index. For sorting the array.
1,
//Label. A string value to identify the hook.
'after_session_start example',
//The PHP file where your class is located.
'custom/modules/application_hooks_class.php',
//The class the method is in.
'application_hooks_class',
//The method to call.
'after_session_start_method'
);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<?php
    $hook_version = 1;
    $hook_array = Array();
    $hook_array['after_session_start'] = Array();
    $hook_array['after_session_start'][] = Array(
        //Processing index. For sorting the array.
        1, 
        //Label. A string value to identify the hook.
        'after_session_start example',
        //The PHP file where your class is located.
        'custom/modules/application_hooks_class.php',
        //The class the method is in.
        'application_hooks_class', 
        //The method to call.
        'after_session_start_method' 
    );

Metadata files

(view defs, vardefs, etc) that are supposed to have specific code defined as arrays should not have any function calls in them or any other logic like string concatenation or loops.

Problematic Example:

Fullscreen
1
2
3
4
5
foreach (["after_retrieve", "after_fetch_query", "after_entry_point", "CallsApiHelper_formatForApi_after", "MeetingsApiHelper_formatForApi_after"] as $hkNameToInit) {
...
foreach ($aHooks as &$aHook) {
...
foreach ($hook_array as $sEvent => &$aHooks) {
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
foreach (["after_retrieve", "after_fetch_query", "after_entry_point", "CallsApiHelper_formatForApi_after", "MeetingsApiHelper_formatForApi_after"] as $hkNameToInit) {
    ...
foreach ($aHooks as &$aHook) {
    ...
foreach ($hook_array as $sEvent => &$aHooks) {

Views/Layouts definitions

Compiled definitions should only contain array definitions.

Problematic Example: existing code in custom/application/Ext/clients/base/layouts/header/header.ext.php

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$u2af72f100c356273d46284f6fd1dfc08 = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module1\Libraries\PHP\Classes\Sugar\Version($GLOBALS["sugar_version"]);
---
$u2af72f100c356273d46284f6fd1dfc08 = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module2\Libraries\PHP\Classes\Sugar\Version($GLOBALS["sugar_version"]);
---
$u2af72f100c356273d46284f6fd1dfc08 = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module1\Libraries\PHP\Classes\Sugar\Version($GLOBALS["sugar_version"]);
---
$version = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module3\Libraries\PHP\Classes\Sugar\Version($GLOBALS['sugar_version']);
---
$version = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module2\Libraries\PHP\Classes\Sugar\Version($GLOBALS['sugar_version']);
---
$version = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module1\Libraries\PHP\Classes\Sugar\Version($GLOBALS['sugar_version']);
---
if (\SugarAutoloader::fileExists('custom/src/MyCompany/Module3/Libraries/PHP/Classes/Sugar/Version.php') && class_exists('\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module3\\Libraries\\PHP\\Classes\\Sugar\\Version') && isset($GLOBALS['sugar_version'])) {
---
if (\SugarAutoloader::fileExists('custom/src/MyCompany/Module2/Libraries/PHP/Classes/Sugar/Version.php') && class_exists('\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module2\\Libraries\\PHP\\Classes\\Sugar\\Version') && isset($GLOBALS['sugar_version'])) {
---
if (\SugarAutoloader::fileExists('custom/src/MyCompany/Module1/Libraries/PHP/Classes/Sugar/Version.php') && class_exists('\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module1\\Libraries\\PHP\\Classes\\Sugar\\Version') && isset($GLOBALS['sugar_version'])) {
---
if (\SugarAutoloader::fileExists("custom/src/MyCompany/CallCenter/Libraries/PHP/Classes/Sugar/Version.php") && class_exists("\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\CallCenter\\Libraries\\PHP\\Classes\\Sugar\\Version") && isset($GLOBALS["sugar_version"])) {
---
if (\SugarAutoloader::fileExists("custom/src/MyCompany/Module2/Libraries/PHP/Classes/Sugar/Version.php") && class_exists("\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module2\\Libraries\\PHP\\Classes\\Sugar\\Version") && isset($GLOBALS["sugar_version"])) {
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$u2af72f100c356273d46284f6fd1dfc08 = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module1\Libraries\PHP\Classes\Sugar\Version($GLOBALS["sugar_version"]);
---
$u2af72f100c356273d46284f6fd1dfc08 = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module2\Libraries\PHP\Classes\Sugar\Version($GLOBALS["sugar_version"]);
---
$u2af72f100c356273d46284f6fd1dfc08 = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module1\Libraries\PHP\Classes\Sugar\Version($GLOBALS["sugar_version"]);
---
$version = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module3\Libraries\PHP\Classes\Sugar\Version($GLOBALS['sugar_version']);
---
$version = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module2\Libraries\PHP\Classes\Sugar\Version($GLOBALS['sugar_version']);
---
$version = new \Sugarcrm\Sugarcrm\custom\MyCompany\Module1\Libraries\PHP\Classes\Sugar\Version($GLOBALS['sugar_version']);
---
if (\SugarAutoloader::fileExists('custom/src/MyCompany/Module3/Libraries/PHP/Classes/Sugar/Version.php') && class_exists('\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module3\\Libraries\\PHP\\Classes\\Sugar\\Version') && isset($GLOBALS['sugar_version'])) {
---
if (\SugarAutoloader::fileExists('custom/src/MyCompany/Module2/Libraries/PHP/Classes/Sugar/Version.php') && class_exists('\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module2\\Libraries\\PHP\\Classes\\Sugar\\Version') && isset($GLOBALS['sugar_version'])) {
---
if (\SugarAutoloader::fileExists('custom/src/MyCompany/Module1/Libraries/PHP/Classes/Sugar/Version.php') && class_exists('\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module1\\Libraries\\PHP\\Classes\\Sugar\\Version') && isset($GLOBALS['sugar_version'])) {
---
if (\SugarAutoloader::fileExists("custom/src/MyCompany/CallCenter/Libraries/PHP/Classes/Sugar/Version.php") && class_exists("\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\CallCenter\\Libraries\\PHP\\Classes\\Sugar\\Version") && isset($GLOBALS["sugar_version"])) {
---
if (\SugarAutoloader::fileExists("custom/src/MyCompany/Module2/Libraries/PHP/Classes/Sugar/Version.php") && class_exists("\\Sugarcrm\\Sugarcrm\\custom\\MyCompany\\Module2\\Libraries\\PHP\\Classes\\Sugar\\Version") && isset($GLOBALS["sugar_version"])) {

Problematic Example 2: existing code in custom/modules/Contacts/Ext/clients/base/views/record/record.ext.php

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$GLOBALS['log']->fatal('After Salesfusion Install');
---
$GLOBALS['log']->fatal('Install Marker 1');
---
$GLOBALS['log']->fatal('Install Marker 2');
---
$GLOBALS['log']->fatal('Install Marker 3: '.$k);
---
$GLOBALS['log']->fatal('Install Marker 4');
---
$GLOBALS['log']->fatal('Install Marker 5');
---
$GLOBALS['log']->fatal('Starting Extension code for Contacts.... ');
---
$GLOBALS['log']->fatal("Implicit for Module1: Error - " . $e->getMessage());
---
$GLOBALS['log']->info('After My MLP Install');
---
$GLOBALS['log']->info('Install Marker 1');
---
$GLOBALS['log']->info('Install Marker 2');
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$GLOBALS['log']->fatal('After Salesfusion Install');
---
$GLOBALS['log']->fatal('Install Marker 1');
---
$GLOBALS['log']->fatal('Install Marker 2');
---
$GLOBALS['log']->fatal('Install Marker 3: '.$k);
---
$GLOBALS['log']->fatal('Install Marker 4');
---
$GLOBALS['log']->fatal('Install Marker 5');
---
$GLOBALS['log']->fatal('Starting Extension code for Contacts.... ');
---
$GLOBALS['log']->fatal("Implicit for Module1: Error - " . $e->getMessage());
---
$GLOBALS['log']->info('After My MLP Install');
---
$GLOBALS['log']->info('Install Marker 1');
---
$GLOBALS['log']->info('Install Marker 2');
---
$GLOBALS['log']->info('Install Marker 3: ' . $k);
---
$GLOBALS['log']->info('Install Marker 3: '.$k);
---
$GLOBALS['log']->info('Install Marker 4');
---
$GLOBALS['log']->info('Install Marker 5');
---
$GLOBALS['log']->info('Starting Extension code for Contacts.... ');
---
$GLOBALS['log']->info('Starting Extension code for Contacts.... '); $module = 'Contacts'; $addAdhocButton = array( 'type' => 'rowaction', 'event' => 'button:my_adhoc:click', 'tooltip' => 'LBL_MY_ADHOC_MEET_TOOLTIP', 'css_class' => 'btn', 'showOn' => 'view', 'icon' => 'fa-comment', ); $addCreateMeetingButton = array( 'type' => 'rowaction', 'event' => 'button:my_create_meeting:click', 'tooltip' => 'LBL_MY_CREATE_MEET_TOOLTIP', 'css_class' => 'btn', 'showOn' => 'view', 'icon' => 'fa-calendar', ); if (!isset($viewdefs[$module]['base']['view']['record']['buttons'])) { $GLOBALS['log']->info('Install Marker 1'); require_once('clients/base/views/record/record.php'); $viewdefs[$module]['base']['view']['record']['buttons'] = $viewdefs['base']['view']['record']['buttons']; } array_unshift($viewdefs[$module]['base']['view']['record']['buttons'],$addAdhocButton,$addCreateMeetingButton); $GLOBALS['log']->info('After My_Component Install');

Recommended Approach: metadata definition only:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$viewdefs['base']['layout']['header'] = [
'components' => [
[
'layout' => 'module-list',
],
[
'layout' => 'quicksearch',
],
[
'view' => 'notifications',
],
[
'view' => 'profileactions',
],
[
'view' => 'quickcreate',
],
],
'last_state' => [
'id' => 'app-header',
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<?php
$viewdefs['base']['layout']['header'] = [
    'components' => [
        [
            'layout' => 'module-list',
        ],
        [
            'layout' => 'quicksearch',
        ],
        [
            'view' => 'notifications',
        ],
        [
            'view' => 'profileactions',
        ],
        [
            'view' => 'quickcreate',
        ],
    ],
    'last_state' => [
        'id' => 'app-header',
        'defaults' => [
            'last-home' => 'dashboard',
        ],
    ],
];

  • Development Best Practices
  • 0 comments
  • 0 members are here
  • Sign in to reply
Related
Recommended