@chcaa/strapi-utils
v0.5.0
Published
Various strapi utils
Downloads
7
Readme
@chcaa/strapi-utils
A set of utility modules to make it easier to work with strapi.
Installation
Run npm install @chcaa/strapi-utils
Modules
@chcaa/strapi-utils
contains the following modules:
lifecycle/CollectionChangedHook
Strapi's native collection lifecycle hooks are fine when you only care about changes to the collection itself, but if you also need to know when relations have changed, e.g. to update a slug field, it can become a complex task to handle when only using the native hooks.
The @chcaa/strapi-utils/lifecycle.CollectionChangedHook
makes it easy to be notified whenever a collection or it's
relations has changed simply by registering the paths of interest.
Create an instance using let cch = new CollectionChangedHook()
.
This would typically be done in strapi's bootstrap()
startup hook, but could be done anywhere.
The constructor can take an optional options object to configure the registered listeners (can be overwritten pr. listener). The following options can be set:
options:object
onSelf:object
emitBeforeCreate:boolean default=false
should listeners observing their own collection receivebeforeCreate
events?emitAfterCreate:boolean default=true
should listeners observing their own collection receiveafterCreate
events?emitBeforeUpdate:boolean default=false
should listeners observing their own collection receivebeforeUpdate
events?emitAfterUpdate:boolean default=true
should listeners observing their own collection receiveafterUpdate
events?emitBeforeDelete:boolean default=false
should listeners observing their own collection receivebeforeDelete
events?emitAfterDelete:boolean default=false
should listeners observing their own collection receiveafterDelete
events?
causedBy:object
includeBeforeState:boolean default=false
include before state of changed entries on afterUpdate events. To e.g. create diffsincludeAfterState:boolean default=false
include after state of changed entries on afterUpdate events. To e.g. create diffs in combination withincludeBeforeState
calculateAttributesChanged:boolean default=false
calculate and include which attributes did change on afterUpdate events. For each entity changed an object with properties matching the entities will be included withtrue
|false
if the attribute value was changed or not. If not set explicitly tofalse
includeBeforeState
andincludeAfterState
will be included as wellincludeInferredAfterState:boolean default=false
include inferred after state of changed entries onbeforeUpdate
events on listeners observing the collection itself. The state is inferred using the current state and thenstrapiEvent.params.data
object.calculateInferredAttributesChanged:boolean, default=false
calculate and include inbeforeUpdate
events on listeners observing the collection itself which inferred attributes would change. The changes are based on comparing the current state with the inferred after state.NOTE
includeInferredAfterState
andcalculateInferredAttributesChanged
cannot infer changes to component data due to insufficient info in the strapiEvent, for components use theafterState
andcalculateAttributesChanges
in afterXXX events.
HINT
ids
,beforeState
,afterState
andchangedAttributes
arrays are all ordered in the same way so to find the matching object ofbeforeState
,afterState
andchangedAttributes
can be done as:for (let i = 0; i < ids.length; i ++) { let beforeEntity = causedBy.beforeState[i]; let afterEntity = causedBy.afterState[i]; let attributesChanged = causedBy.attributesChanged[i]; }
Register listeners for each collection to be notified about changes to using:
register(collectionUid, relationsPaths, listener, options)
collectionUid:string
the uid of the collection to be notified about changes torelationPaths:string[]
one or more relation paths to track for changes.listener:function(event:object)
the function to call when changes occurevent.idsAffected:number[]
the ids of the collection objects affected by the changeevent.causedBy:object
an object with information about the changesrelationPath:string
one of the registered paths which resulted in the change eventcollectionUid:string
the uid of the collection which changedids:number[]
: the ids of the collection which changedeventType:string
describes what happened to the affected path in form of one of"afterCreate"
|"afterUpdate"
|"afterDelete"
and equivalent"beforeXXX"
if enabled inoptions
strapiEvent:object
the strapi-event that triggered this event
options:object [optional]
a set of options to configure the listener (overwrites the default options set in the constructor). See the constructor above for details
All relational paths must point to a collection but are allowed to have intermediate "components" in their path.
To get notifications about the collection itself register an empty string (""
) along with the
other paths of interest.
In the example below we want to be notified about changes to movies when the movie itself (""
) changes,
directors of the movie ("directors"
) changes or an actor in the cast ("cast.actor"
) changes.
const { CollectionChangedHook } = require('@chcaa/strapi-utils/lifecycle');
let cch = new CollectionChangedHook({ onSelf: { emitBeforeEvents: true }});
cch.register("api::movie.movie", ["", "directors", "cast.actor"], ({ idsAffected, causedBy }) => {
if (causedBy.eventType.startsWith('after')) { // we are also listening for beforeEvents on the path "", but for now we only react on afterXxx
for (let idChunk of _.chunk(idsAffected, 100)) { // use lodash to chunk the ids into sub-arrays of 100 ids each
let entries = await strapi.entityService.findMany('api::movie.movie', {
filters: {
$and: [{ id: { $in: idChunk } }]
},
populate: {
genres: true,
cast: { populate: { actor: true } }
}
});
// do something with the entries
}
}
});
Warning! Prevent Infinite Loops
- Do only modify data of paths not listened to or objects and relations attached to the owning collection identified by
idsAffected
.
Performance Note
Nested paths, especially ones including "components" can cause performance issues on larger datasets as they require a lot of joins for strapi to resolve the relations from the database.
To unregister use:
unregister(collectionUid, relationaths, listener)
collectionUid:string
the uid of the collection to NOT be notified about changes to anymore. E.g. "api::movie.movie"relationPaths:string[]
the relational paths to stop listening tolistener:function
the listener to unregister (must be the same function which was passed toregister()
If the listener was registered for the relationPaths ["a", "b"] but only unregisters for ["a"] it will still be notified about changes to "b".