diff --git a/core/attributedef/AttributeExternalKey.php b/core/attributedef/AttributeExternalKey.php new file mode 100644 index 000000000..3be7a2989 --- /dev/null +++ b/core/attributedef/AttributeExternalKey.php @@ -0,0 +1,335 @@ +GetFinalAttDef(); + $sTargetClass = $oRemoteAtt->GetTargetClass(); + if (MetaModel::IsHierarchicalClass($sTargetClass)) { + return self::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY; + } + + return self::SEARCH_WIDGET_TYPE_EXTERNAL_KEY; + } catch (CoreException $e) { + } + + return self::SEARCH_WIDGET_TYPE_RAW; + } + + public static function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed", "on_target_delete")); + } + + public function GetEditClass() + { + return "ExtKey"; + } + + protected function GetSQLCol($bFullSpec = false) + { + return "INT(11)" . ($bFullSpec ? " DEFAULT 0" : ""); + } + + public function RequiresIndex() + { + return true; + } + + public function IsExternalKey($iType = EXTKEY_RELATIVE) + { + return true; + } + + public function GetTargetClass($iType = EXTKEY_RELATIVE) + { + return $this->Get("targetclass"); + } + + public function GetKeyAttDef($iType = EXTKEY_RELATIVE) + { + return $this; + } + + public function GetKeyAttCode() + { + return $this->GetCode(); + } + + public function GetDisplayStyle() + { + return $this->GetOptional('display_style', 'select'); + } + + + public function GetDefaultValue(DBObject $oHostObject = null) + { + return 0; + } + + public function IsNullAllowed() + { + if (MetaModel::GetConfig()->Get('disable_mandatory_ext_keys')) { + return true; + } + + return $this->Get("is_null_allowed"); + } + + + public function GetBasicFilterOperators() + { + return parent::GetBasicFilterOperators(); + } + + public function GetBasicFilterLooseOperator() + { + return parent::GetBasicFilterLooseOperator(); + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + return parent::GetBasicFilterSQLExpr($sOpCode, $value); + } + + // overloaded here so that an ext key always have the answer to + // "what are your possible values?" + public function GetValuesDef() + { + $oValSetDef = $this->Get("allowed_values"); + if (!$oValSetDef) { + // Let's propose every existing value + $oValSetDef = new ValueSetObjects('SELECT ' . $this->GetTargetClass()); + } + + return $oValSetDef; + } + + public function GetAllowedValues($aArgs = array(), $sContains = '') + { + //throw new Exception("GetAllowedValues on ext key has been deprecated"); + try { + return parent::GetAllowedValues($aArgs, $sContains); + } catch (Exception $e) { + // Some required arguments could not be found, enlarge to any existing value + $oValSetDef = new ValueSetObjects('SELECT ' . $this->GetTargetClass()); + + return $oValSetDef->GetValues($aArgs, $sContains); + } + } + + public function GetAllowedValuesForSelect($aArgs = array(), $sContains = '') + { + //$this->GetValuesDef(); + $oValSetDef = new ValueSetObjects('SELECT ' . $this->GetTargetClass()); + return $oValSetDef->GetValuesForAutocomplete($aArgs, $sContains); + } + + + public function GetAllowedValuesAsObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null) + { + $oValSetDef = $this->GetValuesDef(); + $oSet = $oValSetDef->ToObjectSet($aArgs, $sContains, $iAdditionalValue); + + return $oSet; + } + + public function GetAllowedValuesAsFilter($aArgs = array(), $sContains = '', $iAdditionalValue = null) + { + return DBObjectSearch::FromOQL($this->GetValuesDef()->GetFilterExpression()); + } + + public function GetDeletionPropagationOption() + { + return $this->Get("on_target_delete"); + } + + public function GetNullValue() + { + return 0; + } + + public function IsNull($proposedValue) + { + return ($proposedValue == 0); + } + + /** + * @inheritDoc + */ + public function HasAValue($proposedValue): bool + { + return ((int)$proposedValue) !== 0; + } + + public function MakeRealValue($proposedValue, $oHostObj) + { + if (is_null($proposedValue)) { + return 0; + } + if ($proposedValue === '') { + return 0; + } + if (MetaModel::IsValidObject($proposedValue)) { + return $proposedValue->GetKey(); + } + + return (int)$proposedValue; + } + + /** @inheritdoc @since 3.1 */ + public function WriteExternalValues(DBObject $oHostObject): void + { + $sTargetKey = $oHostObject->Get($this->GetCode()); + $oFilter = DBSearch::FromOQL('SELECT `' . TemporaryObjectDescriptor::class . '` WHERE item_class=:class AND item_id=:id'); + $oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]); + while ($oTemporaryObjectDescriptor = $oSet->Fetch()) { + $oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject)); + $oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey()); + $oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode()); + $oTemporaryObjectDescriptor->DBUpdate(); + } + } + + public function GetMaximumComboLength() + { + return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length')); + } + + public function GetMinAutoCompleteChars() + { + return $this->GetOptional('min_autocomplete_chars', MetaModel::GetConfig()->Get('min_autocomplete_chars')); + } + + /** + * @return int + * @since 3.0.0 + */ + public function GetMaxAutoCompleteResults(): int + { + return MetaModel::GetConfig()->Get('max_autocomplete_results'); + } + + public function AllowTargetCreation() + { + return $this->GetOptional('allow_target_creation', MetaModel::GetConfig()->Get('allow_target_creation')); + } + + /** + * Find the corresponding "link" attribute on the target class, if any + * + * @return null | AttributeDefinition + * @throws \CoreException + */ + public function GetMirrorLinkAttribute() + { + $oRet = null; + $sRemoteClass = $this->GetTargetClass(); + foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) { + if (!$oRemoteAttDef->IsLinkSet()) { + continue; + } + if (!is_subclass_of($this->GetHostClass(), + $oRemoteAttDef->GetLinkedClass()) && $oRemoteAttDef->GetLinkedClass() != $this->GetHostClass()) { + continue; + } + if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetCode()) { + continue; + } + $oRet = $oRemoteAttDef; + break; + } + + return $oRet; + } + + public static function GetFormFieldClass() + { + return '\\Combodo\\iTop\\Form\\Field\\SelectObjectField'; + } + + public function MakeFormField(DBObject $oObject, $oFormField = null) + { + /** @var \Combodo\iTop\Form\Field\Field $oFormField */ + if ($oFormField === null) { + // Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value + $sFormFieldClass = static::GetFormFieldClass(); + $oFormField = new $sFormFieldClass($this->GetCode()); + } + + // Setting params + $oFormField->SetMaximumComboLength($this->GetMaximumComboLength()); + $oFormField->SetMinAutoCompleteChars($this->GetMinAutoCompleteChars()); + $oFormField->SetMaxAutoCompleteResults($this->GetMaxAutoCompleteResults()); + $oFormField->SetHierarchical(MetaModel::IsHierarchicalClass($this->GetTargetClass())); + // Setting choices regarding the field dependencies + $aFieldDependencies = $this->GetPrerequisiteAttributes(); + if (!empty($aFieldDependencies)) { + $oTmpAttDef = $this; + $oTmpField = $oFormField; + $oFormField->SetOnFinalizeCallback(function () use ($oTmpField, $oTmpAttDef, $oObject) { + /** @var $oTmpField \Combodo\iTop\Form\Field\Field */ + /** @var $oTmpAttDef \AttributeDefinition */ + /** @var $oObject \DBObject */ + + // We set search object only if it has not already been set (overrided) + if ($oTmpField->GetSearch() === null) { + $oSearch = DBSearch::FromOQL($oTmpAttDef->GetValuesDef()->GetFilterExpression()); + $oSearch->SetInternalParams(array('this' => $oObject)); + $oTmpField->SetSearch($oSearch); + } + }); + } else { + $oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression()); + $oSearch->SetInternalParams(array('this' => $oObject)); + $oFormField->SetSearch($oSearch); + } + + parent::MakeFormField($oObject, $oFormField); + + return $oFormField; + } + + public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true) + { + if (!is_null($oHostObject)) { + return $oHostObject->GetAsHTML($this->GetCode(), $oHostObject); + } + + return DBObject::MakeHyperLink($this->GetTargetClass(), $sValue); + } +} \ No newline at end of file