mirror of
https://github.com/directus/directus.git
synced 2026-01-23 11:47:59 -05:00
Merge pull request #3224 from directus/m2a-settings
Add field-detail settings pane for m2a relationship
This commit is contained in:
@@ -311,15 +311,13 @@ export class CollectionsService {
|
||||
for (const relation of relations) {
|
||||
const isM2O = relation.many_collection === collection;
|
||||
|
||||
/** @TODO M2A — Handle m2a case here */
|
||||
|
||||
if (isM2O) {
|
||||
await this.knex('directus_relations')
|
||||
.delete()
|
||||
.where({ many_collection: collection, many_field: relation.many_field });
|
||||
|
||||
await fieldsService.deleteField(relation.one_collection!, relation.one_field!);
|
||||
} else {
|
||||
} else if (!!relation.one_collection) {
|
||||
await this.knex('directus_relations')
|
||||
.update({ one_field: null })
|
||||
.where({ one_collection: collection, one_field: relation.one_field });
|
||||
|
||||
@@ -187,6 +187,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
multiplePreviewThreshold: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { _items } = useItems();
|
||||
@@ -238,7 +242,7 @@ export default defineComponent({
|
||||
function useDisplayValue() {
|
||||
const displayValue = computed(() => {
|
||||
if (Array.isArray(props.value)) {
|
||||
if (props.value.length < 3) {
|
||||
if (props.value.length < props.multiplePreviewThreshold) {
|
||||
return props.value
|
||||
.map((value) => {
|
||||
return getTextForValue(value) || value;
|
||||
|
||||
@@ -294,7 +294,7 @@ export default defineComponent({
|
||||
? val[anyRelation.value.many_field][primaryKeys.value[collection]]
|
||||
: val[anyRelation.value.many_field];
|
||||
|
||||
const item = relatedItemValues.value[collection].find(
|
||||
const item = relatedItemValues.value[collection]?.find(
|
||||
(item) => item[primaryKeys.value[collection]] == key
|
||||
);
|
||||
|
||||
@@ -317,6 +317,8 @@ export default defineComponent({
|
||||
return { fetchValues, previewValues, loading, junctionRowMap, relatedItemValues };
|
||||
|
||||
async function fetchValues() {
|
||||
if (props.value === null) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
|
||||
@@ -180,6 +180,7 @@
|
||||
"field_m2o": "M2O Relationship",
|
||||
"field_o2m": "O2M Relationship",
|
||||
"field_m2m": "M2M Relationship",
|
||||
"field_m2a": "M2A Relationship",
|
||||
"field_translations": "Translations",
|
||||
"languages": "Languages",
|
||||
|
||||
@@ -229,6 +230,7 @@
|
||||
"configure_m2o": "Configure your Many-to-One Relationship...",
|
||||
"configure_o2m": "Configure your One-to-Many Relationship...",
|
||||
"configure_m2m": "Configure your Many-to-Many Relationship...",
|
||||
"configure_m2a": "Configure your Many-to-Any Relationship...",
|
||||
"configure_translations": "Configure your Translations...",
|
||||
"configure_languages": "Configure your Languages...",
|
||||
|
||||
@@ -264,6 +266,7 @@
|
||||
"m2o_relationship": "Many to One Relationship",
|
||||
"o2m_relationship": "One to Many Relationship",
|
||||
"m2m_relationship": "Many to Many Relationship",
|
||||
"m2a_relationship": "Many to Any Relationship",
|
||||
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
@@ -346,6 +349,7 @@
|
||||
"item_in": "Item {primaryKey} in {collection} | Items {primaryKey} in {collection}",
|
||||
"this_collection": "This Collection",
|
||||
"related_collection": "Related Collection",
|
||||
"related_collections": "Related Collections",
|
||||
"translations_collection": "Translations Collection",
|
||||
"languages_collection": "Languages Collection",
|
||||
|
||||
@@ -469,6 +473,8 @@
|
||||
"replace_from_library": "Replace File from Library",
|
||||
"replace_from_url": "Replace File from URL",
|
||||
|
||||
"collection_key": "Collection Key",
|
||||
|
||||
"name": "Name",
|
||||
"primary_key_field": "Primary Key Field",
|
||||
"type": "Type",
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-notice type="info">{{ $t('configure_m2a') }}</v-notice>
|
||||
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('this_collection') }}</div>
|
||||
<v-input disabled :value="relations[0].one_collection" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('junction_collection') }}</div>
|
||||
<v-input
|
||||
:class="{ matches: junctionCollectionExists }"
|
||||
v-model="junctionCollection"
|
||||
:placeholder="$t('collection') + '...'"
|
||||
:disabled="autoFill || isExisting"
|
||||
db-safe
|
||||
>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon
|
||||
name="list_alt"
|
||||
@click="toggle"
|
||||
v-tooltip="$t('select_existing')"
|
||||
:disabled="autoFill || isExisting"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-list class="monospace">
|
||||
<v-list-item
|
||||
v-for="collection in availableCollections"
|
||||
:key="collection.collection"
|
||||
:active="relations[0].many_collection === collection.collection"
|
||||
@click="relations[0].many_collection = collection.collection"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ collection.collection }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-list-group>
|
||||
<template #activator>{{ $t('system') }}</template>
|
||||
<v-list-item
|
||||
v-for="collection in systemCollections"
|
||||
:key="collection.collection"
|
||||
:active="relations[0].many_collection === collection.collection"
|
||||
@click="relations[0].many_collection = collection.collection"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ collection.collection }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list-group>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collections') }}</div>
|
||||
|
||||
<v-select
|
||||
:disabled="isExisting"
|
||||
:placeholder="$t('collection') + '...'"
|
||||
:items="availableCollections"
|
||||
item-value="collection"
|
||||
item-text="name"
|
||||
multiple
|
||||
v-model="relations[1].one_allowed_collections"
|
||||
:multiple-preview-threshold="0"
|
||||
/>
|
||||
</div>
|
||||
<v-input disabled :value="relations[0].one_primary" />
|
||||
<v-input
|
||||
:class="{ matches: junctionFieldExists(relations[0].many_field) }"
|
||||
v-model="relations[0].many_field"
|
||||
:placeholder="$t('foreign_key') + '...'"
|
||||
:disabled="autoFill || isExisting"
|
||||
db-safe
|
||||
>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon
|
||||
name="list_alt"
|
||||
@click="toggle"
|
||||
v-tooltip="$t('select_existing')"
|
||||
:disabled="autoFill || isExisting"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-list class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_field === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[0].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
<div class="related-collections-preview">{{ (relations[1].one_allowed_collections || []).join(', ') }}</div>
|
||||
<div class="spacer" />
|
||||
<v-input
|
||||
class="one-collection-field"
|
||||
:class="{ matches: junctionFieldExists(relations[0].one_collection_field) }"
|
||||
v-model="relations[1].one_collection_field"
|
||||
:placeholder="$t('collection_key') + '...'"
|
||||
:disabled="autoFill || isExisting"
|
||||
db-safe
|
||||
>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon
|
||||
name="list_alt"
|
||||
@click="toggle"
|
||||
v-tooltip="$t('select_existing')"
|
||||
:disabled="autoFill || isExisting"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-list class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_field === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[0].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
<div class="spacer" />
|
||||
<v-input
|
||||
:class="{ matches: junctionFieldExists(relations[1].many_field) }"
|
||||
v-model="relations[1].many_field"
|
||||
:placeholder="$t('foreign_key') + '...'"
|
||||
:disabled="autoFill || isExisting"
|
||||
db-safe
|
||||
>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon
|
||||
name="list_alt"
|
||||
@click="toggle"
|
||||
v-tooltip="$t('select_existing')"
|
||||
:disabled="autoFill || isExisting"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-list class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[1].many_field === item.value"
|
||||
:disabled="item.disabled"
|
||||
@click="relations[1].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
<v-input disabled :value="$t('primary_key')" />
|
||||
<div class="spacer" />
|
||||
<v-checkbox :disabled="isExisting" block v-model="autoFill" :label="$t('auto_fill')" />
|
||||
<v-icon class="arrow" name="arrow_forward" />
|
||||
<v-icon class="arrow" name="arrow_backward" />
|
||||
<v-icon class="arrow" name="arrow_backward" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref } from '@vue/composition-api';
|
||||
import { orderBy } from 'lodash';
|
||||
import { useCollectionsStore, useFieldsStore } from '@/stores/';
|
||||
import { Field } from '@/types';
|
||||
import i18n from '@/lang';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isExisting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const autoFill = computed({
|
||||
get() {
|
||||
return state.autoFillJunctionRelation;
|
||||
},
|
||||
set(newAuto: boolean) {
|
||||
state.autoFillJunctionRelation = newAuto;
|
||||
},
|
||||
});
|
||||
|
||||
const availableCollections = computed(() => {
|
||||
return orderBy(
|
||||
collectionsStore.state.collections.filter((collection) => {
|
||||
return collection.collection.startsWith('directus_') === false;
|
||||
}),
|
||||
['collection'],
|
||||
['asc']
|
||||
);
|
||||
});
|
||||
|
||||
const systemCollections = computed(() => {
|
||||
return orderBy(
|
||||
collectionsStore.state.collections.filter((collection) => {
|
||||
return collection.collection.startsWith('directus_') === true;
|
||||
}),
|
||||
['collection'],
|
||||
['asc']
|
||||
);
|
||||
});
|
||||
|
||||
const junctionCollection = computed({
|
||||
get() {
|
||||
return state.relations[0].many_collection;
|
||||
},
|
||||
set(collection: string) {
|
||||
state.relations[0].many_collection = collection;
|
||||
state.relations[1].many_collection = collection;
|
||||
},
|
||||
});
|
||||
|
||||
const junctionCollectionExists = computed(() => {
|
||||
return collectionsStore.getCollection(junctionCollection.value) !== null;
|
||||
});
|
||||
|
||||
const junctionFields = computed(() => {
|
||||
if (!junctionCollection.value) return [];
|
||||
|
||||
return fieldsStore.getFieldsForCollection(junctionCollection.value).map((field: Field) => ({
|
||||
text: field.field,
|
||||
value: field.field,
|
||||
disabled:
|
||||
state.relations[0].many_field === field.field ||
|
||||
field.schema?.is_primary_key ||
|
||||
state.relations[1].many_field === field.field,
|
||||
}));
|
||||
});
|
||||
|
||||
return {
|
||||
relations: state.relations,
|
||||
autoFill,
|
||||
availableCollections,
|
||||
systemCollections,
|
||||
junctionCollection,
|
||||
junctionFields,
|
||||
junctionCollectionExists,
|
||||
junctionFieldExists,
|
||||
};
|
||||
|
||||
function junctionFieldExists(fieldKey: string) {
|
||||
if (!junctionCollection.value || !fieldKey) return false;
|
||||
return !!fieldsStore.getField(junctionCollection.value, fieldKey);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.grid {
|
||||
--v-select-font-family: var(--family-monospace);
|
||||
--v-input-font-family: var(--family-monospace);
|
||||
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px 28px;
|
||||
margin-top: 48px;
|
||||
|
||||
.v-input.matches {
|
||||
--v-input-color: var(--primary);
|
||||
}
|
||||
|
||||
.v-icon.arrow {
|
||||
--v-icon-color: var(--primary);
|
||||
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
|
||||
&:first-of-type {
|
||||
top: 105px;
|
||||
left: 32.5%;
|
||||
}
|
||||
|
||||
&:nth-of-type(2) {
|
||||
bottom: 143px;
|
||||
left: 67.4%;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
bottom: 76px;
|
||||
left: 67.4%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.type-label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.v-list {
|
||||
--v-list-item-content-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
.v-notice {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.related-collections-preview {
|
||||
grid-row: 2 / span 2;
|
||||
grid-column: 3;
|
||||
padding: var(--input-padding);
|
||||
overflow: auto;
|
||||
color: var(--foreground-subdued);
|
||||
font-family: var(--family-monospace);
|
||||
background-color: var(--background-subdued);
|
||||
border: var(--border-width) solid var(--border-normal);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.one-collection-field {
|
||||
align-self: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -12,22 +12,24 @@
|
||||
:type="type"
|
||||
v-else-if="type === 'm2m' || type === 'files'"
|
||||
/>
|
||||
<relationship-m2a :collection="collection" :is-existing="isExisting" :type="type" v-else-if="type === 'm2a'" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from '@vue/composition-api';
|
||||
import { Relation } from '@/types';
|
||||
import { Field } from '@/types';
|
||||
import { Relation, Field } from '../../../../../../types';
|
||||
|
||||
import RelationshipM2o from './relationship-m2o.vue';
|
||||
import RelationshipO2m from './relationship-o2m.vue';
|
||||
import RelationshipM2m from './relationship-m2m.vue';
|
||||
import RelationshipM2a from './relationship-m2a.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
RelationshipM2o,
|
||||
RelationshipO2m,
|
||||
RelationshipM2m,
|
||||
RelationshipM2a,
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
|
||||
@@ -238,7 +238,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const typeDisabled = computed(() => {
|
||||
return ['file', 'files', 'o2m', 'm2m', 'm2o', 'translations'].includes(props.type);
|
||||
return ['file', 'files', 'o2m', 'm2m', 'm2a', 'm2o', 'translations'].includes(props.type);
|
||||
});
|
||||
|
||||
const typePlaceholder = computed(() => {
|
||||
|
||||
@@ -150,7 +150,7 @@ export default defineComponent({
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<
|
||||
'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation' | 'translations'
|
||||
'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'm2a' | 'presentation' | 'translations'
|
||||
>,
|
||||
default: null,
|
||||
},
|
||||
@@ -175,7 +175,7 @@ export default defineComponent({
|
||||
const localType = computed(() => {
|
||||
if (props.field === '+') return props.type;
|
||||
|
||||
let type: 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' | 'translations' =
|
||||
let type: 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2a' | 'm2o' | 'presentation' | 'translations' =
|
||||
'standard';
|
||||
type = getLocalTypeForField(props.collection, props.field);
|
||||
|
||||
@@ -232,7 +232,7 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
if (['o2m', 'm2o', 'm2m', 'files'].includes(localType.value)) {
|
||||
if (['o2m', 'm2o', 'm2m', 'm2a', 'files'].includes(localType.value)) {
|
||||
tabs.splice(1, 0, {
|
||||
text: i18n.t('relationship'),
|
||||
value: 'relationship',
|
||||
@@ -298,6 +298,19 @@ export default defineComponent({
|
||||
);
|
||||
}
|
||||
|
||||
if (localType.value === 'm2a') {
|
||||
return (
|
||||
state.relations.length !== 2 ||
|
||||
isEmpty(state.relations[0].many_collection) ||
|
||||
isEmpty(state.relations[0].many_field) ||
|
||||
isEmpty(state.relations[0].one_field) ||
|
||||
isEmpty(state.relations[1].many_collection) ||
|
||||
isEmpty(state.relations[1].many_field) ||
|
||||
isEmpty(state.relations[1].one_collection_field) ||
|
||||
isEmpty(state.relations[1].one_allowed_collections)
|
||||
);
|
||||
}
|
||||
|
||||
if (localType.value === 'presentation') {
|
||||
return isEmpty(state.fieldData.field);
|
||||
}
|
||||
|
||||
@@ -67,22 +67,9 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
|
||||
if (inter.system === true) return false;
|
||||
|
||||
const matchesType = inter.types.includes(state.fieldData?.type || 'alias');
|
||||
const matchesLocalType = inter.localTypes?.includes(type);
|
||||
let matchesRelation = false;
|
||||
const matchesLocalType = inter.localTypes?.includes(type) || true;
|
||||
|
||||
if (type === 'standard' || type === 'presentation') {
|
||||
matchesRelation = inter.relationship === null || inter.relationship === undefined;
|
||||
} else if (type === 'file') {
|
||||
matchesRelation = inter.relationship === 'm2o';
|
||||
} else if (type === 'files') {
|
||||
matchesRelation = inter.relationship === 'm2m';
|
||||
} else if (type === 'translations') {
|
||||
matchesRelation = inter.relationship === 'translations';
|
||||
} else {
|
||||
matchesRelation = inter.relationship === type;
|
||||
}
|
||||
|
||||
return matchesType && matchesRelation && (matchesLocalType === undefined || matchesLocalType);
|
||||
return matchesType && matchesLocalType;
|
||||
})
|
||||
.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||
});
|
||||
@@ -126,7 +113,15 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'file') {
|
||||
if (type === 'file') useFile();
|
||||
else if (type === 'm2o') useM2O();
|
||||
else if (type === 'm2m' || type === 'files' || type === 'translations') useM2M();
|
||||
else if (type === 'o2m') useO2M();
|
||||
else if (type === 'presentation') usePresentation();
|
||||
else if (type === 'm2a') useM2A();
|
||||
else useStandard();
|
||||
|
||||
function useFile() {
|
||||
if (!isExisting) {
|
||||
state.fieldData.type = 'uuid';
|
||||
|
||||
@@ -149,7 +144,7 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'm2o') {
|
||||
function useM2O() {
|
||||
const syncNewCollectionsM2O = throttle(() => {
|
||||
const collectionName = state.relations[0].one_collection;
|
||||
|
||||
@@ -224,7 +219,7 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
|
||||
watch([() => state.relations[0].one_collection, () => state.relations[0].one_primary], syncNewCollectionsM2O);
|
||||
}
|
||||
|
||||
if (type === 'o2m') {
|
||||
function useO2M() {
|
||||
delete state.fieldData.schema;
|
||||
state.fieldData.type = null;
|
||||
|
||||
@@ -321,7 +316,7 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
|
||||
watch([() => state.relations[0].many_collection, () => state.relations[0].many_field], syncNewCollectionsO2M);
|
||||
}
|
||||
|
||||
if (type === 'm2m' || type === 'files' || type === 'translations') {
|
||||
function useM2M() {
|
||||
delete state.fieldData.schema;
|
||||
state.fieldData.type = null;
|
||||
|
||||
@@ -660,14 +655,184 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'presentation') {
|
||||
function useM2A() {
|
||||
delete state.fieldData.schema;
|
||||
state.fieldData.type = null;
|
||||
|
||||
const syncNewCollectionsM2A = throttle(([junctionCollection, manyCurrent, manyRelated, oneCollectionField]) => {
|
||||
state.newCollections = state.newCollections.filter(
|
||||
(col: any) => ['junction', 'related'].includes(col.$type) === false
|
||||
);
|
||||
|
||||
state.newFields = state.newFields.filter(
|
||||
(field: Partial<Field> & { $type: string }) =>
|
||||
['manyCurrent', 'manyRelated', 'collectionField'].includes(field.$type) === false
|
||||
);
|
||||
|
||||
if (collectionExists(junctionCollection) === false) {
|
||||
state.newCollections.push({
|
||||
$type: 'junction',
|
||||
collection: junctionCollection,
|
||||
meta: {
|
||||
hidden: true,
|
||||
icon: 'import_export',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
field: 'id',
|
||||
type: 'integer',
|
||||
schema: {
|
||||
has_auto_increment: true,
|
||||
},
|
||||
meta: {
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
state.relations[0].many_primary = 'id';
|
||||
state.relations[1].many_primary = 'id';
|
||||
}
|
||||
|
||||
if (fieldExists(junctionCollection, manyCurrent) === false) {
|
||||
state.newFields.push({
|
||||
$type: 'manyCurrent',
|
||||
collection: junctionCollection,
|
||||
field: manyCurrent,
|
||||
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)!.type,
|
||||
schema: {},
|
||||
meta: {
|
||||
hidden: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (fieldExists(junctionCollection, manyRelated) === false) {
|
||||
state.newFields.push({
|
||||
$type: 'manyRelated',
|
||||
collection: junctionCollection,
|
||||
field: manyRelated,
|
||||
// We'll have to save the foreign key as a string, as that's the only way to safely
|
||||
// be able to store the PK of multiple typed collections
|
||||
type: 'string',
|
||||
schema: {},
|
||||
meta: {
|
||||
hidden: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (fieldExists(junctionCollection, oneCollectionField) === false) {
|
||||
state.newFields.push({
|
||||
$type: 'collectionField',
|
||||
collection: junctionCollection,
|
||||
field: oneCollectionField,
|
||||
type: 'string', // directus_collections.collection is a string
|
||||
schema: {},
|
||||
meta: {
|
||||
hidden: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.meta.special = [type];
|
||||
|
||||
state.relations = [
|
||||
{
|
||||
many_collection: '',
|
||||
many_field: '',
|
||||
many_primary: '',
|
||||
one_collection: collection,
|
||||
one_field: state.fieldData.field,
|
||||
one_primary: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field,
|
||||
},
|
||||
{
|
||||
many_collection: '',
|
||||
many_field: '',
|
||||
many_primary: '',
|
||||
one_collection: null,
|
||||
one_field: null,
|
||||
one_primary: null,
|
||||
one_allowed_collections: [],
|
||||
one_collection_field: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.relations[0].many_collection,
|
||||
() => {
|
||||
if (collectionExists(state.relations[0].many_collection)) {
|
||||
const pkField = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].many_collection)
|
||||
?.field;
|
||||
state.relations[0].many_primary = pkField;
|
||||
state.relations[1].many_primary = pkField;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[0].many_field,
|
||||
() => {
|
||||
state.relations[1].junction_field = state.relations[0].many_field;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[1].many_field,
|
||||
() => {
|
||||
state.relations[0].junction_field = state.relations[1].many_field;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
[
|
||||
() => state.relations[0].many_collection,
|
||||
() => state.relations[0].many_field,
|
||||
() => state.relations[1].many_field,
|
||||
() => state.relations[1].one_collection_field,
|
||||
],
|
||||
syncNewCollectionsM2A
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.fieldData.field,
|
||||
() => {
|
||||
state.relations[0].one_field = state.fieldData.field;
|
||||
|
||||
if (state.autoFillJunctionRelation) {
|
||||
state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.fieldData.field}`;
|
||||
state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.fieldData.field}`;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.autoFillJunctionRelation,
|
||||
() => {
|
||||
if (state.autoFillJunctionRelation === true) {
|
||||
state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.fieldData.field}`;
|
||||
state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.fieldData.field}`;
|
||||
state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`;
|
||||
state.relations[1].one_collection_field = 'collection';
|
||||
state.relations[1].many_field = 'item';
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
}
|
||||
|
||||
function usePresentation() {
|
||||
delete state.fieldData.schema;
|
||||
state.fieldData.type = null;
|
||||
|
||||
state.fieldData.meta.special = ['alias', 'no-data'];
|
||||
}
|
||||
|
||||
if (type === 'standard') {
|
||||
function useStandard() {
|
||||
watch(
|
||||
() => state.fieldData.type,
|
||||
() => {
|
||||
|
||||
@@ -145,6 +145,11 @@ export default defineComponent({
|
||||
icon: 'import_export',
|
||||
text: i18n.t('m2m_relationship'),
|
||||
},
|
||||
{
|
||||
type: 'm2a',
|
||||
icon: 'gesture',
|
||||
text: i18n.t('m2a_relationship'),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Relation } from '@/types';
|
||||
export function getLocalTypeForField(
|
||||
collection: string,
|
||||
field: string
|
||||
): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' | 'translations' {
|
||||
): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2a' | 'm2o' | 'presentation' | 'translations' {
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
|
||||
@@ -27,6 +27,9 @@ export function getLocalTypeForField(
|
||||
if ((fieldInfo.meta?.special || []).includes('translations')) {
|
||||
return 'translations';
|
||||
}
|
||||
if ((fieldInfo.meta?.special || []).includes('m2a')) {
|
||||
return 'm2a';
|
||||
}
|
||||
|
||||
const relationForCurrent = relations.find((relation: Relation) => {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user