A collection of customizable hooks for Directus. This is not an extension, but a library of scripts that could be used inside a Directus hook extension.
First create a Directus Extension and during setup choose the extension type hook
.
Inside the extension folder install directus-hook-library
:
npm install directus-hook-library
Import it in src/index.ts
, like:
import { setProjectSettingsFromEnvVars } from "directus-hook-library";
Have a look at the examples below.
Tip: You can use multiple of these hook scripts inside the same Directus hook.
Used to delete related M2O items that loose their relation and should not be kept, which is not possible via directus itself. This makes sense for a M2O relation that is used like a O2O relation.
Delete all oneCollection
items that loose their relationship to a manyCollections
item.
(!) Important: You have to specify a (hidden) reverse relationship O2M
in your oneCollection
inside Directus to make this work.
Example
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import { deleteUnusedM2OItems } from "directus-hook-library";
export default defineHook((register, context) => {
deleteUnusedM2OItems(register, context, {
oneCollection: "meta_infos",
manyCollections: {
pages: "pages",
posts: "posts",
},
});
});
This replaces the reference to a deleted user with a reference to the current user in the directus_files collection.
Example
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import { replaceDeletedUserReferences } from "directus-hook-library";
export default defineHook((register, context) => {
replaceDeletedUserReferences(register, context);
});
resetFieldsHiddenByOption
Set fields to null that have a value but are hidden by a condition.
Example
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import { resetFieldsHiddenByOption } from "directus-hook-library";
export default defineHook((register, context) => {
resetFieldsHiddenByOption(register, context, {
collection: "conditional",
optionsField: "detail",
resetGroups: [
{
not: ["yes"],
nullify: ["title", "description"],
},
{
not: ["no"],
nullify: ["external_link"],
},
],
});
});
Used for setting project settings from ENV vars like, PROJECT_URL
.
This overwrites the values for settings
in the Project Settings when starting Directus.
Example
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import { setProjectSettingsFromEnvVars } from "directus-hook-library";
export default defineHook((register, context) => {
setProjectSettingsFromEnvVars(register, context, [
"project_name",
"project_descriptor",
"project_url",
]);
});
For ENV variables like:
PROJECT_NAME=Directus
PROJECT_DESCRIPTOR=Hook
PROJECT_URL=http://localhost:3000
This library provides hooks implementing cascading functionality for M2A
relations, which is not possible via directus itself.
For more information and configuration
details, read the following sections referring to each function.
The configuration object for all M2A
type hooks is the same.
Configuration objects are described by:
-
export type JunctionCollection = { collectionName: string; foreignKeyFieldName: string; itemDiscriminatorFieldName: string; };
Described by:
- collectionName: The name of the junction collection (
article_block
in examples) - foreignKeyField: The name of the field containing the foreign key for related any items. (
item
if left as Directus default, left as default in examples) - itemDiscriminatorFieldName: The name of the field containing the table containing the related item. (
collection
if left as Directus default, left as default in examples)
- collectionName: The name of the junction collection (
-
export type AnyCollection = { collectionName: string; primaryKeyFieldName: string; };
Described by:
- collectionName: The name of the junction collection (
text_block
,image_block
orvideo_block
in examples) - primaryKeyFieldName: The name of the field containing the primary key, which is used to create a relation of items inside of
junctionCollection
collections. (id
if left as Directus default, left as default in examples)
- collectionName: The name of the junction collection (
Grouped together as an object describing the relation called M2AConfig
:
export type M2AConfig = {
anyCollections: AnyCollection[];
junctionCollection: JunctionCollection;
};
Two helper functions are provided, reducing configuration repetition if default fields are left as default.
The specified helper functions are:
-
toJunctionCollectionM2A
(optional):This functions takes the
collectionName
as string and sets the other properties to Directus defaults.
It is used in the following examples, but customizedJunctionCollection
items can also be passed.
For details regarding default values see M2A configuration or Directus documentation.export const toJunctionCollectionM2A = (collectionName: string): JunctionCollection => ({ collectionName, foreignKeyFieldName: "item", itemDiscriminatorFieldName: "collection", });
-
toAnyCollectionM2A
(optional):This functions takes the
collectionName
as string and sets the other properties to Directus defaults.
It is used in the following examples, but customizedAnyCollection
items can also be passed.
For details regarding default values see M2A configuration or Directus documentation.export const toAnyCollectionM2A = (collectionName: string): AnyCollection => ({ collectionName, primaryKeyFieldName: "id", });
Used to cascade deletes performed on junctionCollection
items to all configured anyCollections
(on junctionCollection
delete, the related anyCollection
item is also deleted).
Each Filter
instance is configured with:
- a
Directus register function
(see register function) - a
Directus extension context
(see context object) - a
M2AConfig
object, containing configuration as specified in M2A configuration On delete of an item inside a configuredjunctionCollection
item, theanyCollection
item which the junction collection item relates to is also deleted.
Works likeM2O
,O2M
orM2M
selection ofDELETE CASCADE
.
Examples
Basic in code usage/configuration (configure a single M2A relation):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
deleteUnusedItemFilterM2A,
} from "directus-hook-library";
export default defineHook((register, context) => {
deleteUnusedItemFilterM2A(register, context, {
anyCollections: [
"text_block",
"image_block",
"video_block"
].map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A("article_block")
});
});
Advanced in code usage/configuration (configure multiple M2A relations with multiple junction tables referencing identical item pools):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
deleteUnusedItemFilterM2A,
} from "directus-hook-library";
const advancedExampleConfig = [
{
configCollections: [
"image_block",
"video_block",
"text_block"
],
configJunctions: [
"article_block",
"blogpost_block"
]
},
{
configCollections: [
"pizza_item",
"pasta_item",
"dessert_item"
],
configJunctions: [
"order_item"
]
},
]
export default defineHook((register, context) => {
for (const relationConfig of advancedExampleConfig) {
for (const junctionCollectionName of relationConfig.configJunctions) {
deleteUnusedItemFilterM2A(register, context, {
anyCollections: relationConfig.configCollections.map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A(junctionCollectionName)
});
}
}
});
Used to cascade deletes performed on anyCollection
items to a configured junctionCollection
(on anyCollection
item delete, relating items in junctionCollection
are also deleted).
Each Action
instance is configured with:
- a
Directus register function
(see register function) - a
Directus extension context
(see context object) - a
M2AConfig
object, containing configuration as specified in M2A configuration On delete of an item inside a configuredany
collection,junction
entries pointing to the deletedany
item are deleted.
Works likeM2O
,O2M
orM2M
selection ofDELETE CASCADE
.
Examples
Basic in code usage/configuration (configure a single M2A relation):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
deleteUnusedRelationActionM2A,
} from "directus-hook-library";
export default defineHook((register, context) => {
deleteUnusedRelationActionM2A(register, context, {
anyCollections: [
"text_block",
"image_block",
"video_block"
].map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A("article_block")
});
});
Advanced in code usage/configuration (configure multiple M2A relations with multiple junction tables referencing identical item pools):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
deleteUnusedRelationActionM2A,
} from "directus-hook-library";
const advancedExampleConfig = [
{
configCollections: [
"image_block",
"video_block",
"text_block"
],
configJunctions: [
"article_block",
"blogpost_block"
]
},
{
configCollections: [
"pizza_item",
"pasta_item",
"dessert_item"
],
configJunctions: [
"order_item"
]
},
]
export default defineHook((register, context) => {
for (const relationConfig of advancedExampleConfig) {
for (const junctionCollectionName of relationConfig.configJunctions) {
deleteUnusedRelationActionM2A(register, context, {
anyCollections: relationConfig.configCollections.map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A(junctionCollectionName)
});
}
}
});
Used to prevent deletes performed on anyCollection
if the related junctionCollection
contains a relation to deleted anyCollection
item (delete request for junctionCollection
item should be prevented).
Each Filter
instance is configured with:
- a
Directus register function
(see register function) - a
Directus extension context
(see context object) - a
M2AConfig
object, containing configuration as specified in M2A configuration On delete request of ananyCollection
item, configuredjunctionCollection
is checked.
If an item containing a relation still exists, the delete request is denied and the operation is prohibited.
Works likeM2O
,O2M
orM2M
selection ofDELETE RESTRICT
.
Examples
Basic in code usage/configuration (configure a single M2A relation):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
preventItemDeleteFilterM2A,
} from "directus-hook-library";
export default defineHook((register, context) => {
preventItemDeleteFilterM2A(register, context, {
anyCollections: [
"text_block",
"image_block",
"video_block"
].map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A("article_block")
});
});
Advanced in code usage/configuration (configure multiple M2A relations with multiple junction tables referencing identical item pools):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
preventItemDeleteFilterM2A,
} from "directus-hook-library";
const advancedExampleConfig = [
{
configCollections: [
"image_block",
"video_block",
"text_block"
],
configJunctions: [
"article_block",
"blogpost_block"
]
},
{
configCollections: [
"pizza_item",
"pasta_item",
"dessert_item"
],
configJunctions: [
"order_item"
]
},
]
export default defineHook((register, context) => {
for (const relationConfig of advancedExampleConfig) {
for (const junctionCollectionName of relationConfig.configJunctions) {
preventItemDeleteFilterM2A(register, context, {
anyCollections: relationConfig.configCollections.map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A(junctionCollectionName)
});
}
}
});
Used to prevent deletes performed on junctionCollection
if the related anyCollection
item still exists (delete request for anyCollection
item should be prevented).
Each Filter
instance is configured with:
- a
Directus register function
(see register function) - a
Directus extension context
(see context object) - a
M2AConfig
object, containing configuration as specified in M2A configuration On delete request of ajunctionCollection
item, configuredanyCollections
are checked.
If related item still exists, the delete request is denied and the operation is prohibited.
Works likeM2O
,O2M
orM2M
selection ofDELETE RESTRICT
.
Examples
Basic in code usage/configuration (configure a single M2A relation):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
preventRelationDeleteFilterM2A,
} from "directus-hook-library";
export default defineHook((register, context) => {
preventRelationDeleteFilterM2A(register, context, {
anyCollections: [
"text_block",
"image_block",
"video_block"
].map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A("article_block")
});
});
Advanced in code usage/configuration (configure multiple M2A relations with multiple junction tables referencing identical item pools):
// src/index.ts
import { defineHook } from "@directus/extensions-sdk";
import {
toJunctionCollectionM2A,
toAnyCollectionM2A,
preventRelationDeleteFilterM2A,
} from "directus-hook-library";
const advancedExampleConfig = [
{
configCollections: [
"image_block",
"video_block",
"text_block"
],
configJunctions: [
"article_block",
"blogpost_block"
]
},
{
configCollections: [
"pizza_item",
"pasta_item",
"dessert_item"
],
configJunctions: [
"order_item"
]
},
]
export default defineHook((register, context) => {
for (const relationConfig of advancedExampleConfig) {
for (const junctionCollectionName of relationConfig.configJunctions) {
preventRelationDeleteFilterM2A(register, context, {
anyCollections: relationConfig.configCollections.map(toAnyCollectionM2A),
junctionCollection: toJunctionCollectionM2A(junctionCollectionName)
});
}
}
});