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.
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 '#src/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()
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.