* @param $importFilename
* @param string $definitionName
* @param bool $dryrun
+ * @param string $duplicateResolveStrategy
* @return array
* @throws Tinebase_Exception_NotFound
*/
- protected function _importHelper($importFilename, $definitionName = 'crm_tine_import_csv', $dryrun = true)
+ protected function _importHelper($importFilename, $definitionName = 'crm_tine_import_csv', $dryrun = true, $duplicateResolveStrategy = null)
{
$this->_testNeedsTransaction();
'dryrun' => $dryrun,
);
+ if ($duplicateResolveStrategy) {
+ $options['duplicateResolveStrategy'] = $duplicateResolveStrategy;
+ }
+
$result = $this->_doImport($options, $definitionName);
return $result;
* Abstract test class
*
* @package Tests
+ *
+ * TODO separation of concerns: split into multiple classes/traits with cleanup / fixture / ... functionality
*/
abstract class TestCase extends PHPUnit_Framework_TestCase
{
*/
protected function _deleteUsers()
{
-
-
foreach ($this->_usernamesToDelete as $username) {
try {
if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
}
/**
+ * removes records and their relations
+ *
+ * @param Tinebase_Record_RecordSet $records
+ */
+ protected function _deleteRecordRelations($records, $modelsToDelete = array(), $typesToDelete = array())
+ {
+ $controller = Tinebase_Core::getApplicationInstance($records->getRecordClassName());
+
+ if (! method_exists($controller, 'deleteLinkedRelations')) {
+ return;
+ }
+
+ foreach ($records as $record) {
+ $controller->deleteLinkedRelations($record, $modelsToDelete, $typesToDelete);
+ }
+ }
+
+ /**
* get personal container
*
* @param string $applicationName
{name: 'customer', omitDuplicateResolving: true, label: 'Customer', group: 'Relationen'},
{name: 'partner', omitDuplicateResolving: true, label: 'Partner', group: 'Relationen'},
{name: 'tasks', omitDuplicateResolving: true},
- {name: 'relations', omitDuplicateResolving: true},
+ {name: 'relations', label: 'Relationen', group: 'Relationen'},
{name: 'products', omitDuplicateResolving: true, label: 'Products', group: 'Relationen'},
{name: 'tags', label: 'Tags'},
{name: 'notes', omitDuplicateResolving: true},
}
if ($_record->has('relations')) {
- $this->_deleteLinkedRelations($_record);
+ // TODO check if this needs to be done, as we might already deleting this "from the other side"
+ $this->deleteLinkedRelations($_record);
}
if ($_record->has('attachments') && Tinebase_Core::isFilesystemAvailable()) {
/**
* delete linked relations
*
- * @param Tinebase_Record_Interface $_record
- *
- * TODO check if this needs to be done, as we might already deleting this "from the other side"
+ * @param Tinebase_Record_Interface $record
+ * @param array $modelsToDelete
+ * @param array $typesToDelete
*/
- protected function _deleteLinkedRelations(Tinebase_Record_Interface $_record)
+ public function deleteLinkedRelations(Tinebase_Record_Interface $record, $modelsToDelete = array(), $typesToDelete = array())
{
- $relations = Tinebase_Relations::getInstance()->getRelations($this->_modelName, $this->_getBackendType(), $_record->getId());
- if (empty($relations)) {
+ $relations = isset($record->relations) && $record->relations instanceof Tinebase_Record_RecordSet
+ ? $record->relations
+ : Tinebase_Relations::getInstance()->getRelations($this->_modelName, $this->_getBackendType(), $record->getId());
+
+ if (count($relations) === 0) {
return;
}
- // remove relations
- Tinebase_Relations::getInstance()->setRelations($this->_modelName, $this->_getBackendType(), $_record->getId(), array());
+ // unset record relations
+ Tinebase_Relations::getInstance()->setRelations($this->_modelName, $this->_getBackendType(), $record->getId(), array());
- if (empty($this->_relatedObjectsToDelete)) {
+ if (empty($modelsToDelete)) {
+ $modelsToDelete = $this->_relatedObjectsToDelete;
+ }
+ if (empty($modelsToDelete) && empty($typesToDelete)) {
return;
}
// remove related objects
Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Deleting all '
- . implode(',', $this->_relatedObjectsToDelete) . ' relations.');
+ . implode(',', $modelsToDelete) . ' relations.');
foreach ($relations as $relation) {
- if (in_array($relation->related_model, $this->_relatedObjectsToDelete)) {
+ if (in_array($relation->related_model, $modelsToDelete) || in_array($relation->type, $typesToDelete)) {
list($appName, $i, $itemName) = explode('_', $relation->related_model);
$appController = Tinebase_Core::getApplicationInstance($appName, $itemName);
}
$values = (isset($field['separator'])) ? $this->_splitBySeparator($field['separator'], $fieldValue): array($fieldValue);
-
+
$relations = array();
-
foreach ($values as $value) {
- // check if related record exists
- $controller = Tinebase_Core::getApplicationInstance($field['related_model']);
- $filterModel = $field['related_model'] . 'Filter';
- $operator = isset($field['operator']) ? $field['operator'] : 'equals';
+ $relation = $this->_getRelationForValue($value, $field, $data);
+ $relations[] = $relation;
+ }
- $filterValueToAdd = '';
- if (isset($field['filterValueAdd']) && isset($data[$field['filterValueAdd']])) {
- if ($field['filter'] === 'query') {
- $filterValueToAdd = ' ' . $data[$field['filterValueAdd']];
- } else {
- if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
- Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__
- . ' "filterValueAdd" Currently only working for query filter');
- }
- }
+ // TODO how do we handle this with multiple relations/values?
+ if (isset($field['targetField']) && isset($field['targetFieldData']) && count($relations) > 0) {
+ $this->_setTargetFieldFromRelation($field, $data, $relations[0]);
+ }
+
+ return $relations;
+ }
+
+ protected function _setTargetFieldFromRelation($field, &$data, $relation)
+ {
+ $unreplaced = $targetField = $field['targetFieldData'];
+ $recordArray = $relation['related_record'];
+ foreach ($recordArray as $key => $value) {
+ if (preg_match('/' . preg_quote($key) . '/', $targetField) && is_scalar($value)) {
+ $targetField = preg_replace('/' . preg_quote($key) . '/', $value, $targetField);
+ $unreplaced = preg_replace('/^[, ]*' . preg_quote($key) . '/', '', $unreplaced);
}
+ }
- $filter = new $filterModel(array(
- array('field' => $field['filter'], 'operator' => $operator, 'value' => $value . $filterValueToAdd)
- ));
- $result = $controller->search($filter);
- if (count($result) > 0) {
- $record = $result->getFirstRecord()->toArray();
+ // remove unreplaced stuff
+ $targetField = str_replace($unreplaced, '', $targetField);
+
+ // finally set the target field value
+ $data[$field['targetField']] = trim($targetField);
+ }
+
+ protected function _getRelationForValue($value, $field, $data)
+ {
+ // check if related record exists
+ $controller = Tinebase_Core::getApplicationInstance($field['related_model']);
+ $filterModel = $field['related_model'] . 'Filter';
+ $operator = isset($field['operator']) ? $field['operator'] : 'equals';
+
+ $filterValueToAdd = '';
+ if (isset($field['filterValueAdd']) && isset($data[$field['filterValueAdd']])) {
+ if ($field['filter'] === 'query') {
+ $filterValueToAdd = ' ' . $data[$field['filterValueAdd']];
} else {
- // create new related record
- $record = array(
- (isset($field['related_field']) ? $field['related_field'] : $field['filter']) => $value
- );
- if (! empty($filterValueToAdd)) {
- $record[str_replace($field['destination'] . '_', '', $field['filterValueAdd'])] = trim($filterValueToAdd);
+ if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
+ Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__
+ . ' "filterValueAdd" Currently only working for query filter');
}
}
+ }
+
+ $filter = new $filterModel(array(
+ array('field' => $field['filter'], 'operator' => $operator, 'value' => $value . $filterValueToAdd)
+ ));
+ $result = $controller->search($filter, null, /* $_getRelations */ true);
+ $relation = $this->_getRelationData($result->getFirstRecord(), $field, $data, $value);
+
+ return $relation;
+ }
+
+ protected function _getRelationData($record, $field, $data, $value)
+ {
+ $relationType = $field['destination'];
+ $relation = array(
+ 'type' => $relationType,
+ 'related_model' => $field['related_model'],
+ // TODO move this to product (modelconfig?)
+ 'remark' => $relationType == 'PRODUCT' ? array('quantity' => 1) : null,
+ );
- $relation = array(
- 'type' => $field['destination'],
- 'related_record' => $record,
- // TODO move this to product (modelconfig?)
- 'remark' => $field['destination'] == 'PRODUCT' ? array('quantity' => 1) : null
+ if ($record) {
+ $relation['related_id'] = $record->getId();
+ $recordArray = $record->toArray();
+ } else {
+ // create new related record
+ $recordArray = array(
+ (isset($field['related_field']) ? $field['related_field'] : $field['filter']) => $value
);
-
- $relations[] = $relation;
+ if (! empty($filterValueToAdd)) {
+ $recordArray[str_replace($relationType . '_', '', $field['filterValueAdd'])] = trim($filterValueToAdd);
+ }
}
- if (isset($field['targetField'])&& isset($field['targetFieldData']) && isset($record)) {
- $unreplaced = $targetField = $field['targetFieldData'];
- foreach ($record as $key => $value) {
- if (preg_match('/' . preg_quote($key) . '/', $targetField) && is_scalar($value)) {
- $targetField = preg_replace('/' . preg_quote($key) . '/', $value, $targetField);
- $unreplaced = preg_replace('/^[, ]*' . preg_quote($key) . '/', '', $unreplaced);
- }
+ // add more data for this relation if available
+ foreach ($data as $key => $value) {
+ $regex = '/^' . preg_quote($relationType) . '_/';
+ if (preg_match($regex, $key)) {
+ $relatedField = preg_replace($regex, '', $key);
+ $recordArray[$relatedField] = trim($value);
}
- // remove unreplaced stuff
- $targetField = str_replace($unreplaced, '', $targetField);
- $data[$field['targetField']] = $targetField;
}
-
- return $relations;
+
+ $relation['related_record'] = $recordArray;
+
+ return $relation;
}
/**
foreach ($destinations as $destination) {
if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
. ' destination ' . $destination);
- $data[$destination] = $values[$i++];
+ if (isset($values[$i])) {
+ $data[$destination] = trim($values[$i]);
+ }
+ $i++;
}
} else {
$data[$field['destination']] = $value;
), $args);
} else {
throw new Tinebase_Exception(
- 'Plugin ' . $method . ' was not found in haystack');
+ 'Plugin ' . $method . ' was not found in haystack of class ' . get_class($this));
}
}
}
*
* @param array $_properties
* @return $this
+ *
+ * TODO implement
*/
public function addIndices(array $_properties)
{
// compute relations to add/delete
$currentRelations = $this->getRelations($_model, $_backend, $_id, NULL, array(), $_ignoreACL);
$currentIds = $currentRelations->getArrayOfIds();
- $relationsIds = $relations->getArrayOfIds();
+ $relationsIds = $this->_getRelationIds($relations, $currentRelations);
$toAdd = $relations->getIdLessIndexes();
$toDel = array_diff($currentIds, $relationsIds);
$toUpdate = array_intersect($currentIds, $relationsIds);
- // prevent two empty related_id s of the same relation type
+ // prevent two empty related_ids of the same relation type
$emptyRelatedId = array();
foreach ($toAdd as $idx) {
if (empty($relations[$idx]->related_id)) {
}
}
}
-
+
+ /**
+ * appends missing relation ids if related records + type match
+ *
+ * @param $relations
+ * @param $currentRelations
+ * @return mixed
+ */
+ protected function _getRelationIds($relations, $currentRelations)
+ {
+ $clonedRelations = clone $relations;
+
+ if (count($currentRelations) > 0) {
+ foreach ($clonedRelations as $relation) {
+ if ($relation->getId()) {
+ continue;
+ }
+
+ // if relation has no id, maybe we have the same relation already in current relations
+ $subset = $currentRelations->filter('own_id', $relation->own_id)
+ ->filter('related_id', $relation->related_id)
+ ->filter('type', $relation->type);
+
+ if (count($subset) === 1) {
+ // remove and add to make sure index is updated in record set
+ $relations->removeRecord($relation);
+ $relation->setId($subset->getFirstRecord()->getId());
+ $relations->addRecord($relation);
+ //$result[] = $subset->getFirstRecord()->getId();
+ }
+ }
+ }
+
+ $result = $relations->getArrayOfIds();
+
+ return $result;
+ }
+
/**
* returns the constraints config for the given models and their mirrored values (seen from the other side
*