diff --git a/projects/plugins/crm/changelog/fix-crm-3477-fix-export-crash-when-company-custom-field b/projects/plugins/crm/changelog/fix-crm-3477-fix-export-crash-when-company-custom-field new file mode 100644 index 0000000000000..9a81dde96f524 --- /dev/null +++ b/projects/plugins/crm/changelog/fix-crm-3477-fix-export-crash-when-company-custom-field @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fixed a fatal error that occurred when exporting contacts with a custom field that had the same name as a linked field (e.g., 'company'). \ No newline at end of file diff --git a/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Export.php b/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Export.php index e16fc51a27ebe..50be5533c5b6e 100644 --- a/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Export.php +++ b/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Export.php @@ -320,6 +320,11 @@ function jpcrm_export_process_file_export() { $objRow = array(); foreach ( $fields as $fK ) { + // Checking and fixing name clashes between custom fields and linked objects + // (e.g. custom field with slug `company` and the company linked object) + // See: https://github.com/Automattic/zero-bs-crm/issues/3477 + $objDALLayer->fix_name_clash_if_needed( $fK ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase + $v = ''; // default (means always right col count) if ( isset( $obj[ $fK ] ) ) { $v = zeroBSCRM_textExpose( $obj[ $fK ] ); diff --git a/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Obj.Contacts.php b/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Obj.Contacts.php index 52b653d2545b3..3c61ba448b3a1 100644 --- a/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Obj.Contacts.php +++ b/projects/plugins/crm/includes/ZeroBSCRM.DAL3.Obj.Contacts.php @@ -2072,6 +2072,25 @@ public function getContacts($args=array()){ } + // Checking and fixing name clashes between custom fields and linked objects + // (e.g. custom field with slug `company` and the company linked object) + // See: https://github.com/Automattic/zero-bs-crm/issues/3477 + $this->add_name_clash_suffix_if_needed( + $resArr, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase + array( + 'tags', + 'dnd', + 'company', + 'lastlog', + 'owner', + 'invoices', + 'quotes', + 'transactions', + 'tasks', + 'external_sources', + ) + ); + if ($withTags){ // add all tags lines @@ -2114,25 +2133,25 @@ public function getContacts($args=array()){ if (is_array($potentialLogs) && count($potentialLogs) > 0) $resArr['lastlog'] = $potentialLogs[0]; - // CONTACT logs specifically - // doesn't return singular, for now using arr - $potentialLogs = $this->DAL()->logs->getLogsForObj( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - array( + // CONTACT logs specifically + // doesn't return singular, for now using arr + $potentialLogs = $this->DAL()->logs->getLogsForObj( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase + array( - 'objtype' => ZBS_TYPE_CONTACT, - 'objid' => $resDataLine->ID, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase + 'objtype' => ZBS_TYPE_CONTACT, + 'objid' => $resDataLine->ID, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - 'notetypes' => $zbs->DAL->logs->contact_log_types, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + 'notetypes' => $zbs->DAL->logs->contact_log_types, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - 'incMeta' => true, + 'incMeta' => true, - 'sortByField' => 'zbsl_created', - 'sortOrder' => 'DESC', - 'page' => 0, - 'perPage' => 1, + 'sortByField' => 'zbsl_created', + 'sortOrder' => 'DESC', + 'page' => 0, + 'perPage' => 1, - ) - ); + ) + ); if (is_array($potentialLogs) && count($potentialLogs) > 0) $resArr['lastcontactlog'] = $potentialLogs[0]; diff --git a/projects/plugins/crm/includes/ZeroBSCRM.DAL3.ObjectLayer.php b/projects/plugins/crm/includes/ZeroBSCRM.DAL3.ObjectLayer.php index 936c4ba4d5b9a..d7fd3f981353d 100644 --- a/projects/plugins/crm/includes/ZeroBSCRM.DAL3.ObjectLayer.php +++ b/projects/plugins/crm/includes/ZeroBSCRM.DAL3.ObjectLayer.php @@ -44,6 +44,18 @@ class zbsDAL_ObjectLayer { // e.g. Invoice object type may be commonly linked to 'contact' or 'company' object types protected $linkedToObjectTypes = array(); + /** This field is used to store name clashes so we can change custom field names + * with name clashes in function `fix_name_clash_if_needed()` + * See: https://github.com/Automattic/zero-bs-crm/issues/3477 + * + * @var array + */ + protected $name_clashes_temp_fix = array(); + + /** Suffix used to fix name clashes + * See: https://github.com/Automattic/zero-bs-crm/issues/3477 + */ + protected const NAME_CLASH_FIX_SUFFIX = '_zbs-name-clash-tmp-fix'; function __construct($args=array()) { @@ -1327,7 +1339,41 @@ public function addUpdateCustomField($args=array()){ } - + /** + * Fixes name clashes between linked objects (e.g. company) and custom fields with the same slug. + * Adds a suffix to conflicting field names to prevent clashes. + * + * @param array &$array The array containing the fields that need to be checked for name clashes. + * @param string[] $keys An array of keys to check within the provided array for potential name clashes. + * + * @return void + * + * @see https://github.com/Automattic/zero-bs-crm/issues/3477 + */ + public function add_name_clash_suffix_if_needed( &$array, $keys ) { + foreach ( $keys as $key ) { + if ( isset( $array[ $key ] ) ) { + $this->name_clashes_temp_fix[] = $key; + $new_key = $key . self::NAME_CLASH_FIX_SUFFIX; + $array[ $new_key ] = $array[ $key ]; + } + } + } + + /** + * Fixes a field name if it has been identified as having a name clash by appending a suffix. + * + * @param string &$field_name The field name to check and potentially modify to avoid a name clash. + * + * @return void + * + * @see https://github.com/Automattic/zero-bs-crm/issues/3477 + */ + public function fix_name_clash_if_needed( &$field_name ) { + if ( in_array( $field_name, $this->name_clashes_temp_fix, true ) ) { + $field_name = $field_name . self::NAME_CLASH_FIX_SUFFIX; + } + } // =========== / DAL2 WRAPPERS =================================================== // ===============================================================================