Skip to main content

ORM: Extending Models

See how to extend models implementations in Athenna Framework.

Introduction

All the Athenna models extends the BaseModel class of the @athenna/database package. This class provides everything that a model needs to communicate with database in the best way possible. But sometimes you may need to write your own methods depending on your business logic and to save some time. You will see on this page how you can easily create your own static and instance methods for your models.

tip

Also, we recommend you to take a look at the implementation of the BaseModel class, this way you could have more knowledge about all the methods available for your in the this property when implementing your own methods.

Writing static methods

Let's start by defining a new static method in our User model that will retrieve a user and also load relationships:

import { BaseModel } from '@athenna/database'

export class User extends BaseModel {
public static async findWith(relation: string, where: Partial<InstanceType<User>> = {}): Promise<User> {
const query = this.query().with(relationName)

// Verify if the where object is not
// empty to apply it in the query.
if (Object.keys(where).length) {
query.where(where)
}

return this.query().with(relationName).find()
}

/*...*/
}

Now we can start finding our user with his relations with a little less code:

const user = await User.findWith('posts', { id: 1 })

Let's implement another method that will get all the users that were created in the last 15 minutes ordering then by name:

import { BaseModel } from '@athenna/database'

export class User extends BaseModel {
public static async getRecentlyCreatedUsers(): Promise<User[]> {
const nowDate = new Date()
const nowDateLessFifteenMinutes = new Date(nowDate - 15 * 60000)

return this.query()
.whereBetween('createdAt', [nowDate, nowDateLessFifteenMinutes])
.findMany()
}

/*...*/
}

Calling our new static method:

const recentlyUsers = await User.getRecentlyCreatedUsers()

Writing instance methods

You can also implement your own instance methods for your models. Let's implement a new instance method that will confirm the user email:

import { BaseModel } from '@athenna/database'

export class User extends BaseModel {
public async confirmEmailNow() {
this.emailVerifiedAt = new Date()

await this.save()
}

/*...*/
}

Now we can easily confirm the user email with one method call:

const user = await User.find()

await user.confirmEmailNow()

Now let's implement a method that will load the user posts if they are not loaded and also get the oldest post of the user:

import { type Post } from '#app/models/Post'
import { BaseModel } from '@athenna/database'

export class User extends BaseModel {
public async getOldestPost(): Promise<Post> {
if (!this.posts) {
await this.load('posts', query => query.oldest())
}

return this.posts[0]
}

/*...*/
}

Using our new method:

const user = await User.find()
const oldestPost = await user.getOldestPost()
tip

Always be carefully to not break the single responsibility principle of SOLID when implementing your own model methods. But you are free to do whatever you want with Athenna 😎🤙. All of these methods implementations will always depend on your business logic and creativity.