In addition to being able to Extending Models you can also extend instances of models. This has been used in functional-models-orm
to add CRUD functionality to the instance itself. (save, delete, etc)
As with extending Models, everything occurs inside a custom ModelFactory. The create()
function of the Model needs to be wrapped so that you can insert your added functionality.
import merge from 'lodash/merge'
import {
Model,
ModelFactory,
ModelOptions,
TextProperty,
DataDescription,
MinimalModelDefinition,
PrimaryKeyUuidProperty,
CreateParams,
} from 'functional-models'
type MyExtendedInstance = {
/**
* A function that will check if this instance exists in the database or not.
*/
existsInDatabase: () => Promise<boolean>
/*
* A function that "saves" our model to a database.
*/
save: () => Promise<void>
}
// 2. Create a ModelFactory, pass in our type, which extends the implementation throughout the framework. NOTE: It is the second generic argument, the first is for model extensions (not used here).
const CustomModel =
(databaseConnection: any): ModelFactory<object, MyExtendedInstance> =>
<TData extends DataDescription>(
modelDefinitions: MinimalModelDefinition<TData>,
options?: ModelOptions<TData, object, MyExtendedInstance>
) => {
const model = Model(modelDefinitions)
const existsInDatabase =
(instance: ModelInstance<TData>) => async (): Promise<boolean> => {
// We use the underlying instance object to get information we need.
const data = await instance.toObj()
// We then use it to do something, like search a database in a custom way.
const found = databaseConnection.search(data)
return found
}
const save =
(instance: ModelInstance<TData>) => async (): Promise<void> => {
const data = await instance.toObj()
await databaseConnection.save(data)
return
}
// 3. We wrap the create function so that it injects our functions into the instance.
const create = (params: CreateParams<'', TData>) => {
const instance = model.create(params)
return merge(instance, {
existsInDatabase: existsInDatabase(instance),
save: save(instance),
}) as ModelInstance<TData, object, MyExtendedInstance>
}
return merge(model, {
create,
})
}
// 4. Lets create a data type
type User = {
id: string
name: string
}
const myDatabaseConnection = {}
// 5. Now using our custom model factory create a model.
const Users = CustomModel(myDatabaseConnection)<User>({
pluralName: 'Users',
namespace: '@my-package',
properties: {
id: PrimaryKeyUuidProperty(),
name: TextProperty({ required: true }),
},
})
const user = Users.create<'id'>({ name: 'Henry' })
// 6. Use our function on the instance.
const exists = await user.existsInDatabase()
console.info(exists)
// false
await user.save()
const existsNow = await user.existsInDatabase()
console.info(existsNow)
// true