N°3198 Relations Table Mode : control duplicates server side

This commit is contained in:
Pierre Goiffon
2020-09-22 12:07:05 +02:00
parent 36f8344ec9
commit 98789f28bb
4 changed files with 79 additions and 28 deletions

View File

@@ -295,7 +295,7 @@ abstract class AttributeDefinition
* @param \DBObject $oHostObject
* @param $value Object error if any, null otherwise
*
* @return bool
* @return bool|string true for no errors, false or error message otherwise
*/
public function CheckValue(DBObject $oHostObject, $value)
{
@@ -2273,22 +2273,17 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
/** @var \AttributeExternalKey $oExtKeyToRemote */
$oExtKeyToRemote = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sRemoteClass = $oExtKeyToRemote->GetTargetClass();
foreach(MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef)
{
if (!$oRemoteAttDef instanceof AttributeLinkedSetIndirect)
{
foreach(MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef) {
if (!$oRemoteAttDef instanceof AttributeLinkedSetIndirect) {
continue;
}
if ($oRemoteAttDef->GetLinkedClass() != $this->GetLinkedClass())
{
if ($oRemoteAttDef->GetLinkedClass() != $this->GetLinkedClass()) {
continue;
}
if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetExtKeyToRemote())
{
if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetExtKeyToRemote()) {
continue;
}
if ($oRemoteAttDef->GetExtKeyToRemote() != $this->GetExtKeyToMe())
{
if ($oRemoteAttDef->GetExtKeyToRemote() != $this->GetExtKeyToMe()) {
continue;
}
$oRet = $oRemoteAttDef;

View File

@@ -1822,13 +1822,13 @@ abstract class DBObject implements iDisplay
/**
* Check if the given (or current) value is suitable for the attribute
*
* @api
* @api-advanced
*
* @param string $sAttCode
* @param boolean|string $value true if successful, the error description otherwise
* @api
* @api-advanced
*
* @return bool|string
* @param string $sAttCode
* @param mixed $value if not passed, then will get value using Get method
*
* @return bool|string true if successful, the error description otherwise
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \OQLException
@@ -2108,23 +2108,75 @@ abstract class DBObject implements iDisplay
}
$aChildClassesWithRuleDisabled = MetaModel::GetChildClassesWithDisabledUniquenessRule($sRuleRootClass, $sUniquenessRuleId);
if (!empty($aChildClassesWithRuleDisabled))
{
if (!empty($aChildClassesWithRuleDisabled)) {
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
}
return $oUniquenessQuery;
}
/**
* @param string $sAttCode
* @param mixed $value
*
* @uses m_aCheckWarnings to log to user duplicates found
*
* @since 2.8.0 N°3198 check duplicates if necessary :<br>
* Before we could only add or remove lnk using the uilinks widget. This widget has a filter based on existing values, and so
* forbids to add duplicates.<br>
* Now we can modify existing entries using the extkey widget, and the widget doesn't have (yet !) such
* a filter. The widget will be updated later on, but as a first step we're checking for duplicates server side. This is a non
* blocking warning, as it was already possible to add duplicates by other means.
*/
protected function DoCheckLinkedSetDuplicates($sAttCode, $value)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if (!($oAttDef instanceof AttributeLinkedSetIndirect)) {
return;
}
if ($oAttDef->DuplicatesAllowed()) {
return true;
}
/** @var \ormLinkSet $value */
$aModifiedLnk = $value->ListModifiedLinks();
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$aExistingRemotesId = $value->GetColumnAsArray($sExtKeyToRemote, true);
$aExistingRemotesFriendlyName = $value->GetColumnAsArray($sExtKeyToRemote.'_friendlyname', true);
$aDuplicatesFields = [];
foreach ($aModifiedLnk as $oModifiedLnk) {
$iModifiedLnkId = $oModifiedLnk->GetKey();
$iModifiedLnkRemoteId = $oModifiedLnk->Get($sExtKeyToRemote);
$aExistingRemotesIdToCheck = array_filter($aExistingRemotesId, function ($iLnkKey) use ($iModifiedLnkId) {
return ($iLnkKey != $iModifiedLnkId);
}, ARRAY_FILTER_USE_KEY);
if (!in_array($iModifiedLnkRemoteId, $aExistingRemotesIdToCheck, true)) {
continue;
}
$iLnkId = $oModifiedLnk->GetKey();
$aDuplicatesFields[] = $aExistingRemotesFriendlyName[$iLnkId];
}
if (!empty($aDuplicatesFields)) {
$this->m_aCheckWarnings[] = Dict::Format('Core:AttributeLinkedSetDuplicatesFound',
$oAttDef->GetLabel(),
implode(', ', $aDuplicatesFields));
}
}
/**
* Check integrity rules (before inserting or updating the object)
*
* **This method is not meant to be called directly, use DBObject::CheckToWrite()!**
* **This method is not meant to be called directly, use DBObject::CheckToWrite()!**
* Errors should be inserted in $m_aCheckIssues and $m_aCheckWarnings arrays
*
* @overwritable-hook You can extend this method in order to provide your own logic.
* @see CheckToWrite()
* @see $m_aCheckIssues
*
* @overwritable-hook You can extend this method in order to provide your own logic.
* @see CheckToWrite()
* @see $m_aCheckIssues
* @see $m_aCheckWarnings
*
* @throws \ArchivedObjectException
@@ -2140,14 +2192,14 @@ abstract class DBObject implements iDisplay
$aChanges = $this->ListChanges();
foreach($aChanges as $sAttCode => $value)
{
foreach($aChanges as $sAttCode => $value) {
$res = $this->CheckValue($sAttCode);
if ($res !== true)
{
if ($res !== true) {
// $res contains the error description
$this->m_aCheckIssues[] = "Unexpected value for attribute '$sAttCode': $res";
}
$this->DoCheckLinkedSetDuplicates($sAttCode, $value);
}
if (count($this->m_aCheckIssues) > 0)
{

View File

@@ -33,6 +33,8 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:AttributeLinkedSet' => 'Array of objects',
'Core:AttributeLinkedSet+' => 'Any kind of objects of the same class or subclass',
'Core:AttributeLinkedSetDuplicatesFound' => 'Duplicates in the \'%1$s\' field : %2$s',
'Core:AttributeDashboard' => 'Dashboard',
'Core:AttributeDashboard+' => '',

View File

@@ -31,6 +31,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Core:AttributeLinkedSet' => 'Objets liés (1-n)',
'Core:AttributeLinkedSet+' => 'Liste d\'objets d\'une classe donnée et pointant sur l\'objet courant',
'Core:AttributeLinkedSetDuplicatesFound' => 'Des doublons sont présents dans le champ \'%1$s\' : %2$s',
'Core:AttributeDashboard' => 'Tableau de bord',
'Core:AttributeDashboard+' => '',