/** * @param $linkName String name of a link field in the module's vardefs * @param $bean SugarBean focus bean for this link (one half of a relationship) * @param $linkDef Array Optional vardef for the link in case it can't be found in the passed in bean for the global dictionary * @return void * */ function __construct($linkName, $bean, $linkDef = false) { $this->focus = $bean; //Try to load the link vardef from the beans field defs. Otherwise start searching if (empty($bean->field_defs) || empty($bean->field_defs[$linkName]) || empty($bean->field_defs[$linkName]['relationship'])) { if (empty($linkDef)) { //Assume $linkName is really relationship_name, and find the link name with the vardef manager $this->def = VardefManager::getLinkFieldForRelationship($bean->module_dir, $bean->object_name, $linkName); } else { $this->def = $linkDef; } //Check if multiple links were found for a given relationship if (is_array($this->def) && !isset($this->def['name'])) { //More than one link found, we need to figure out if we are currently on the LHS or RHS //default to lhs for now if (isset($this->def[0]['side']) && $this->def[0]['side'] == 'left') { $this->def = $this->def[0]; } else { if (isset($this->def[1]['side']) && $this->def[1]['side'] == 'left') { $this->def = $this->def[1]; } else { $this->def = $this->def[0]; } } } if (empty($this->def['name'])) { $GLOBALS['log']->fatal("failed to find link for {$linkName}"); return false; } $this->name = $this->def['name']; } else { //Linkdef was found in the bean (this is the normal expectation) $this->def = $bean->field_defs[$linkName]; $this->name = $linkName; } //Instantiate the relationship for this link. $this->relationship = SugarRelationshipFactory::getInstance()->getRelationship($this->def['relationship']); // Fix to restore functionality from Link.php that needs to be rewritten but for now this will do. $this->relationship_fields = !empty($this->def['rel_fields']) ? $this->def['rel_fields'] : array(); if (!$this->loadedSuccesfully()) { global $app_strings; $GLOBALS['log']->error(string_format($app_strings['ERR_DATABSE_RELATIONSHIP_QUERY'], array($this->name, $this->def['relationship']))); } //Following behavior is tied to a property(ignore_role) value in the vardef. It alters the values of 2 properties, ignore_role_filter and add_distinct. //the property values can be altered again before any requests are made. if (!empty($this->def) && is_array($this->def)) { if (isset($this->def['ignore_role'])) { if ($this->def['ignore_role']) { $this->ignore_role_filter = true; $this->add_distinct = true; } } if (!empty($this->def['primary_only'])) { $this->relationship->primaryOnly = true; } } }
/** * The collector method for relationships. * * @return array An array of relationships, indexed by the relationship name */ public function getRelationshipData() { $relFactory = SugarRelationshipFactory::getInstance(); // Request fresh relationship metadata always $data = $relFactory->getRelationshipDefs(true); // Sanity check the rel defs, just in case they came back empty if (is_array($data)) { // Certain elements of the relationship defs need to be pruned $unsets = array('table', 'fields', 'indices', 'relationships'); foreach ($data as $relKey => $relData) { // Prune the relationship defs as needed foreach ($unsets as $unset) { unset($relData[$unset]); } // Sort each def array for consistency to ensure sameness between // metadata cache refreshes ksort($relData); // Reset the defs for this key $data[$relKey] = $relData; } } // To maintain hashes between requests, make sure this array is always // in the same order. Otherwise, the serialized value of this data will // potentially be different from one request to another. ksort($data); $data["_hash"] = $this->hashChunk($data); return $data; }
/** * Updates relationships based on changes to fields of type 'parent' which * may or may not have links associated with them * * @param array $exclude */ protected function update_parent_relationships($exclude = array()) { foreach ($this->field_defs as $def) { if (!empty($def['type']) && $def['type'] == "parent") { if (empty($def['type_name']) || empty($def['id_name'])) { continue; } $typeField = $def['type_name']; $idField = $def['id_name']; if (in_array($idField, $exclude)) { continue; } //Determine if the parent field has changed. if (!empty($this->fetched_row[$typeField]) && !empty($this->fetched_row[$idField]) && (empty($this->{$typeField}) || empty($this->{$idField})) || !empty($this->{$typeField}) && !empty($this->{$idField}) && (empty($this->fetched_row[$typeField]) || empty($this->fetched_row[$idField]) || $this->fetched_row[$idField] != $this->{$idField}) || $this->deleted == 1) { $parentLinks = array(); //Correlate links to parent field module types foreach ($this->field_defs as $ldef) { if (!empty($ldef['type']) && $ldef['type'] == "link" && !empty($ldef['relationship'])) { $relDef = SugarRelationshipFactory::getInstance()->getRelationshipDef($ldef['relationship']); if (!empty($relDef['relationship_role_column']) && $relDef['relationship_role_column'] == $typeField) { $parentLinks[$relDef['lhs_module']] = $ldef; } } } // Save $this->$idField, because it can be resetted in case of link->delete() call $idFieldVal = $this->{$idField}; //If we used to have a parent, call remove on that relationship if (!empty($this->fetched_row[$typeField]) && !empty($this->fetched_row[$idField]) && !empty($parentLinks[$this->fetched_row[$typeField]]) && $this->fetched_row[$idField] != $this->{$idField}) { $oldParentLink = $parentLinks[$this->fetched_row[$typeField]]['name']; //Load the relationship if ($this->load_relationship($oldParentLink)) { $this->{$oldParentLink}->delete($this->fetched_row[$idField]); // Should resave the old parent SugarRelationship::addToResaveList(BeanFactory::getBean($this->fetched_row[$typeField], $this->fetched_row[$idField])); } } // If both parent type and parent id are set, save it unless the bean is being deleted if (!empty($this->{$typeField}) && !empty($idFieldVal) && !empty($parentLinks[$this->{$typeField}]['name']) && $this->deleted != 1) { //Now add the new parent $parentLink = $parentLinks[$this->{$typeField}]['name']; if ($this->load_relationship($parentLink)) { $this->{$parentLink}->add($idFieldVal); } } } } } }
/** * Updates relationships based on changes to fields of type 'parent' which * may or may not have links associated with them * * @param array $exclude */ protected function update_parent_relationships($exclude = array()) { foreach ($this->field_defs as $def) { if (!empty($def['type']) && $def['type'] == "parent") { if (empty($def['type_name']) || empty($def['id_name'])) { continue; } $typeField = $def['type_name']; $idField = $def['id_name']; // save the new id $newIdValue = $this->{$idField}; if (in_array($idField, $exclude)) { continue; } //Determine if the parent field has changed. if (!empty($this->fetched_row[$typeField]) && !empty($this->fetched_row[$idField]) && (empty($this->{$typeField}) || empty($this->{$idField})) || !empty($this->{$typeField}) && !empty($this->{$idField}) && (empty($this->fetched_row[$typeField]) || empty($this->fetched_row[$idField]) || $this->fetched_row[$idField] != $this->{$idField}) || $this->deleted == 1) { $parentLinks = array(); //Correlate links to parent field module types foreach ($this->field_defs as $ldef) { if (!empty($ldef['type']) && $ldef['type'] == "link" && !empty($ldef['relationship'])) { $rel = SugarRelationshipFactory::getInstance()->getRelationship($ldef['relationship']); $relColumns = $rel->getRelationshipRoleColumns(); if (isset($relColumns[$typeField])) { $parentLinks[$rel->getLHSModule()] = $ldef; } } } //If we used to have a parent, call remove on that relationship if (!empty($this->fetched_row[$typeField]) && !empty($this->fetched_row[$idField]) && !empty($parentLinks[$this->fetched_row[$typeField]]) && $this->fetched_row[$idField] != $this->{$idField}) { $oldParentLink = $parentLinks[$this->fetched_row[$typeField]]['name']; //Load the relationship if ($this->load_relationship($oldParentLink)) { $this->{$oldParentLink}->delete($this->id, $this->fetched_row[$idField]); // Should resave the old parent, if the current user has access to it and can save it $beanToSave = BeanFactory::getBean($this->fetched_row[$typeField], $this->fetched_row[$idField]); if (!empty($beanToSave->id)) { SugarRelationship::addToResaveList($beanToSave); } } } // If both parent type and parent id are set, save it unless the bean is being deleted if (!empty($this->{$typeField}) && !empty($newIdValue) && !empty($parentLinks[$this->{$typeField}]['name']) && $this->deleted != 1) { //Now add the new parent $parentLink = $parentLinks[$this->{$typeField}]['name']; if ($this->load_relationship($parentLink)) { $this->{$parentLink}->add($newIdValue); } } } } } }
/** * @param $linkName String name of a link field in the module's vardefs * @param $bean SugarBean focus bean for this link (one half of a relationship) * @param $linkDef Array Optional vardef for the link in case it can't be found in the passed in bean for the global dictionary * @return void * */ function __construct($linkName, $bean, $linkDef = false) { $this->focus = $bean; //Try to load the link vardef from the beans field defs. Otherwise start searching if (empty($bean->field_defs) || empty($bean->field_defs[$linkName]) || empty($bean->field_defs[$linkName]['relationship'])) { if (empty($linkDef)) { //Assume $linkName is really relationship_name, and find the link name with the vardef manager $this->def = VardefManager::getLinkFieldForRelationship($bean->module_dir, $bean->object_name, $linkName); } else { $this->def = $linkDef; } //Check if multiple links were found for a given relationship if (is_array($this->def) && !isset($this->def['name'])) { //More than one link found, we need to figure out if we are currently on the LHS or RHS //default to lhs for now if (isset($this->def[0]['side']) && $this->def[0]['side'] == 'left') { $this->def = $this->def[0]; } else { if (isset($this->def[1]['side']) && $this->def[1]['side'] == 'left') { $this->def = $this->def[1]; } else { $this->def = $this->def[0]; } } } if (empty($this->def['name'])) { $GLOBALS['log']->fatal("failed to find link for {$linkName}"); return false; } $this->name = $this->def['name']; } else { //Linkdef was found in the bean (this is the normal expectation) $this->def = $bean->field_defs[$linkName]; $this->name = $linkName; } //Instantiate the relationship for this link. $this->relationship = SugarRelationshipFactory::getInstance()->getRelationship($this->def['relationship']); if (!$this->loadedSuccesfully()) { $GLOBALS['log']->fatal("{$this->name} for {$this->def['relationship']} failed to load\n"); } //Following behavior is tied to a property(ignore_role) value in the vardef. It alters the values of 2 properties, ignore_role_filter and add_distinct. //the property values can be altered again before any requests are made. if (!empty($this->def) && is_array($this->def)) { if (array_key_exists('ignore_role', $this->def)) { if ($this->def['ignore_role']) { $this->ignore_role_filter = true; $this->add_distinct = true; } } } }
/** * Used internally to determine if a field def is a valid link for use in formulas * @static * @param $def array, Link field definition. * @return bool true if field is valid. */ protected static function validLinkField($def) { global $dictionary; $invalidModules = array("Emails" => true, "Teams" => true); if (empty($def['relationship'])) { return false; //Not a good link field } $rel = SugarRelationshipFactory::getInstance()->getRelationship($def['relationship']); if ($rel === false) { return false; //Unable to find a relationship definition } if (!empty($invalidModules[$rel->lhs_module]) || !empty($invalidModules[$rel->rhs_module])) { return false; //Invalid module } //Otherwise this link looks ok return true; }
function retrieve_by_name($relationship_name) { if (empty($GLOBALS['relationships'])) { $this->load_relationship_meta(); } if (!array_key_exists($relationship_name, $GLOBALS['relationships'])) { $def = SugarRelationshipFactory::getInstance()->getRelationshipDef($relationship_name); if (!empty($def)) { $GLOBALS['relationships'][$relationship_name] = $def; } } if (array_key_exists($relationship_name, $GLOBALS['relationships'])) { foreach ($GLOBALS['relationships'][$relationship_name] as $field => $value) { $this->{$field} = $value; } } else { $GLOBALS['log']->fatal('Error fetching relationship from cache ' . $relationship_name); return false; } }
/** * @static * @param $module String name of module. * @param $object String name of module Bean. * Updates a list of link fields which have relationships to modules with calculated fields * that use this module. Needed to cause an update to those modules when this module is updated. * @return bool */ protected static function updateRelCFModules($module, $object) { global $dictionary, $beanList; if (empty($dictionary[$object]) || empty($dictionary[$object]['fields'])) { return false; } $linkFields = self::getLinkFieldsForModule($module, $object); if (empty($linkFields)) { $dictionary[$object]['related_calc_fields'] = array(); return false; } $linksWithCFs = array(); foreach ($linkFields as $name => $def) { $relName = $def['relationship']; //Start by getting the relation $relDef = false; if (!empty($def['module'])) { $relMod = $def['module']; } else { if (!empty($dictionary[$relName]['relationships'][$relName])) { $relDef = $dictionary[$relName]['relationships'][$relName]; } else { if (!empty($dictionary[$object][$relName])) { $relDef = $dictionary[$object][$relName]; } else { $relDef = SugarRelationshipFactory::getInstance()->getRelationshipDef($relName); if (!$relDef) { continue; } } } if (empty($relDef['lhs_module'])) { continue; } $relMod = $relDef['lhs_module'] == $module ? $relDef['rhs_module'] : $relDef['lhs_module']; } if (empty($beanList[$relMod])) { continue; } $relObject = BeanFactory::getObjectName($relMod); $relLinkFields = self::getLinkFieldsForModule($relMod, $relObject); if (!empty($relLinkFields)) { foreach ($relLinkFields as $rfName => $rfDef) { if ($rfDef['relationship'] == $relName && self::modHasCalcFieldsWithLink($relMod, $relObject, $rfName)) { $linksWithCFs[$name] = true; } } } } $dictionary[$object]['related_calc_fields'] = array_keys($linksWithCFs); }