diff --git a/examples/getstarted/api/like/models/Like.settings.json b/examples/getstarted/api/like/models/Like.settings.json
index f83298ddca..8be0a41644 100755
--- a/examples/getstarted/api/like/models/Like.settings.json
+++ b/examples/getstarted/api/like/models/Like.settings.json
@@ -18,6 +18,14 @@
"review": {
"model": "review",
"via": "likes"
+ },
+ "like_left": {
+ "collection": "like",
+ "via": "like_right"
+ },
+ "like_right": {
+ "model": "like",
+ "via": "like_left"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js
index d33b8f2b01..e9ff467aac 100644
--- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js
+++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js
@@ -73,12 +73,21 @@ const DataManagerProvider = ({ children }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading, pathname]);
- const addAttribute = (attributeToSet, forTarget, targetUid) => {
+ const addAttribute = (
+ attributeToSet,
+ forTarget,
+ targetUid,
+ isEditing = false,
+ initialAttribute
+ ) => {
+ const actionType = isEditing ? 'EDIT_ATTRIBUTE' : 'ADD_ATTRIBUTE';
+
dispatch({
- type: 'ADD_ATTRIBUTE',
+ type: actionType,
attributeToSet,
forTarget,
targetUid,
+ initialAttribute,
});
};
@@ -138,7 +147,7 @@ const DataManagerProvider = ({ children }) => {
return ;
}
- console.log({ modifiedData, contentTypes });
+ console.log({ modifiedData: modifiedData.contentType });
return (
{
+ if (originalNature === 'manyToOne') {
+ return 'oneToMany';
+ } else if (originalNature === 'oneToMany') {
+ return 'manyToOne';
+ } else {
+ return originalNature;
+ }
+};
+
const reducer = (state, action) => {
switch (action.type) {
case 'ADD_ATTRIBUTE': {
@@ -49,13 +60,14 @@ const reducer = (state, action) => {
target === currentUid
) {
const oppositeAttribute = {
- nature,
+ nature: getOppositeNature(nature),
target,
- type,
unique: rest.unique,
required: rest.required,
dominant: nature === 'manyToMany' ? !rest.dominant : null,
targetAttribute: name,
+ columnName: rest.targetColumnName,
+ targetColumnName: rest.columnName,
};
return obj.update(rest.targetAttribute, () => {
@@ -67,6 +79,150 @@ const reducer = (state, action) => {
}
);
}
+ case 'EDIT_ATTRIBUTE': {
+ const {
+ attributeToSet: { name, ...rest },
+ forTarget,
+ targetUid,
+ initialAttribute,
+ } = action;
+ const initialAttributeName = get(initialAttribute, ['name'], '');
+ const pathToDataToEdit = ['component', 'contentType'].includes(forTarget)
+ ? [forTarget]
+ : [forTarget, targetUid];
+
+ return state.updateIn(
+ ['modifiedData', ...pathToDataToEdit, 'schema'],
+ obj => {
+ let oppositeAttributeNameToRemove = null;
+ let oppositeAttributeNameToUpdate = null;
+ let oppositeAttributeNameToCreateBecauseOfNatureChange = null;
+ let oppositeAttributeToCreate = null;
+
+ const newObj = OrderedMap(
+ obj
+ .get('attributes')
+ .keySeq()
+ .reduce((acc, current) => {
+ const isEditingCurrentAttribute =
+ current === initialAttributeName;
+
+ if (isEditingCurrentAttribute) {
+ const currentUid = state.getIn([
+ 'modifiedData',
+ ...pathToDataToEdit,
+ 'uid',
+ ]);
+ const isEditingRelation = has(initialAttribute, 'nature');
+ const didChangeTargetRelation =
+ initialAttribute.target !== rest.target;
+ const didCreateInternalRelation = rest.target === currentUid;
+ const nature = rest.nature;
+ const initialNature = initialAttribute.nature;
+ const hadInternalRelation =
+ initialAttribute.target === currentUid;
+ const didChangeRelationNature =
+ initialAttribute.nature !== nature;
+ const shouldRemoveOppositeAttributeBecauseOfTargetChange =
+ didChangeTargetRelation &&
+ !didCreateInternalRelation &&
+ hadInternalRelation &&
+ isEditingRelation;
+ const shouldRemoveOppositeAttributeBecauseOfNatureChange =
+ didChangeRelationNature &&
+ hadInternalRelation &&
+ ['oneWay', 'manyWay'].includes(nature) &&
+ isEditingRelation;
+ const shouldUpdateOppositeAttributeBecauseOfNatureChange =
+ !ONE_SIDE_RELATIONS.includes(initialNature) &&
+ !ONE_SIDE_RELATIONS.includes(nature) &&
+ hadInternalRelation &&
+ didCreateInternalRelation &&
+ isEditingRelation;
+ const shouldCreateOppositeAttributeBecauseOfNatureChange =
+ ONE_SIDE_RELATIONS.includes(initialNature) &&
+ !ONE_SIDE_RELATIONS.includes(nature) &&
+ hadInternalRelation &&
+ didCreateInternalRelation &&
+ isEditingRelation;
+
+ // Update the opposite attribute name so it is removed at the end of the loop
+ if (
+ shouldRemoveOppositeAttributeBecauseOfTargetChange ||
+ shouldRemoveOppositeAttributeBecauseOfNatureChange
+ ) {
+ oppositeAttributeNameToRemove =
+ initialAttribute.targetAttribute;
+ }
+
+ // Set the opposite attribute that will be updated when the loop attribute matches the name
+ if (
+ shouldUpdateOppositeAttributeBecauseOfNatureChange ||
+ shouldCreateOppositeAttributeBecauseOfNatureChange
+ ) {
+ oppositeAttributeNameToUpdate =
+ initialAttribute.targetAttribute;
+ oppositeAttributeNameToCreateBecauseOfNatureChange =
+ rest.targetAttribute;
+
+ oppositeAttributeToCreate = {
+ nature: getOppositeNature(rest.nature),
+ target: rest.target,
+ unique: rest.unique,
+ required: rest.required,
+ dominant:
+ rest.nature === 'manyToMany' ? !rest.dominant : null,
+ targetAttribute: name,
+ columnName: rest.targetColumnName,
+ targetColumnName: rest.columnName,
+ };
+
+ // First update the current attribute with the value
+ acc[name] = fromJS(rest);
+
+ // Then (if needed) create the opposite attribute the case is changing the relation from
+ // We do it here so keep the order of the attributes
+ // oneWay || manyWay to something another relation
+ if (shouldCreateOppositeAttributeBecauseOfNatureChange) {
+ acc[
+ oppositeAttributeNameToCreateBecauseOfNatureChange
+ ] = fromJS(oppositeAttributeToCreate);
+
+ oppositeAttributeToCreate = null;
+ oppositeAttributeNameToCreateBecauseOfNatureChange = null;
+ }
+
+ return acc;
+ }
+
+ acc[name] = fromJS(rest);
+ } else if (current === oppositeAttributeNameToUpdate) {
+ acc[
+ oppositeAttributeNameToCreateBecauseOfNatureChange
+ ] = fromJS(oppositeAttributeToCreate);
+ } else {
+ acc[current] = obj.getIn(['attributes', current]);
+ }
+
+ return acc;
+ }, {})
+ );
+
+ let updatedObj;
+
+ // Remove the opposite attribute
+ if (oppositeAttributeNameToRemove !== null) {
+ console.log('ppppp');
+ updatedObj = newObj.remove(oppositeAttributeNameToRemove);
+ } else {
+ updatedObj = newObj;
+ }
+
+ return obj.set('attributes', updatedObj);
+ }
+ );
+ }
+
case 'GET_DATA_SUCCEEDED':
return state
.update('components', () => fromJS(action.components))
diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js
index 990bdf9778..05a37d5680 100644
--- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js
+++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js
@@ -285,7 +285,13 @@ const FormModal = () => {
search: nextSearch,
});
} else if (state.modalType === 'attribute') {
- addAttribute(modifiedData, state.forTarget, state.targetUid);
+ addAttribute(
+ modifiedData,
+ state.forTarget,
+ state.targetUid,
+ state.actionType === 'edit',
+ initialData
+ );
push({ search: nextSearch });
} else {
console.log('Do something with component later');
diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js
index 7ada2a6551..b5fd30d9bd 100644
--- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js
+++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js
@@ -47,6 +47,13 @@ const reducer = (state, action) => {
: oldValue,
shouldPluralizeTargetAttribute(value)
);
+ })
+ .update('targetColumnName', oldValue => {
+ if (['oneWay', 'manyWay'].includes(value)) {
+ return null;
+ }
+
+ return oldValue;
});
}
@@ -110,6 +117,8 @@ const reducer = (state, action) => {
unique: false,
required: false,
dominant: null,
+ columnName: null,
+ targetColumnName: null,
};
} else {
dataToSet = { type: attributeType, default: null };