ORM: Getting Started
See how to create models in Athenna Framework.
Introduction
Athenna has an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using the Athenna ORM, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, the models allow you to insert, update, and delete records from the table as well.
Before getting started, be sure to configure a database connection in your application's
Path.config('database.ts')
./src/config/database.ts
the database configuration documentation.
Generating models
To get started, let's create a model. Models typically live in the
Path.models()
./src/models
BaseModel
class. You may use the make:model
Artisan command to generate a
new model:
node artisan make:model Flight
Columns
You will have to define your database columns as properties on the
class and annotate them using the @Column()
annotation. Any property
annotate with it could be distinguished between standard class properties
and database columns. Let's see an example of defining the flight table
columns as properties on the Flight
model:
import { Column, BaseModel } from '@athenna/database'
export class Flight extends BaseModel {
@Column()
public id: number
@Column()
public from: string
@Column()
public to: string
@Column({ isCreateDate: true })
public createdAt: Date
@Column({ isUpdateDate: true })
public updatedAt: Date
}
For more information about model options visit the @Column()
annotation documentation section.
Model conventions
Models generated by the make:model
command will be placed in the
Path.models()
./src/models
import { BaseModel } from '@athenna/database'
export class Flight extends BaseModel {
@Column()
public id: number
public static attributes(): Partial<Flight> {
return {}
}
public static async definition(): Promise<Partial<Flight>> {
return {
id: this.faker.number.int()
}
}
}
Table names
After glancing at the example above, you may have noticed that we
did not tell the model which database table corresponds to our Flight
model. By convention, the "snake_case", plural name of the class will
be used as the table name unless another name is explicitly specified.
So, in this case, the model will assume the Flight
model stores records
in the flights
table, while an AirTrafficController
model would store
records in an air_traffic_controllers
table.
If your model's corresponding database table does not fit this convention, you may manually specify the model's table name by
defining a static getter table
on the model:
import { BaseModel } from '@athenna/database'
export class Flight extends BaseModel {
public static table() {
return 'my_flights'
}
/*...*/
}
Primary keys
The model will also assume that each model's corresponding database
table has a primary key column named id
if using a SQL driver and
_id
if using mongo driver. If necessary, you may define
a property isMainPrimary
as true in one of your model columns to
specify a different column that serves as your model's main primary
key:
import { Column, BaseModel } from '@athenna/database'
export class Flight extends BaseModel {
@Column({ isMainPrimary: true })
public id: number
/*...*/
}
Default attributes values
By default, a newly instantiated model instance will not contain any
attribute values. If you would like to define the default values
for some of your model's attributes, you may define a static method
attributes()
on your model:
import { Uuid } from '@athenna/common'
import { BaseModel } from '@athenna/database'
export class Flight extends BaseModel {
public static attributes(): Partial<Flight> {
return {
id: Uuid.generate()
}
}
/*...*/
}
As you can see we are defining an id
property in our static method
attributes()
. This property will have the value of a generated
uuid randomly everytime that Athenna calls the attributes()
method.
Athenna will call the attributes()
method everytime that create()
,
createMany()
, update()
and createOrUpdate()
methods are called,
this means that a new uuid will be generated for each call:
import { Flight } from '#src/models/Flight'
const flight1 = await Flight.create()
const flight2 = await Flight.query().create()
console.log(flight1.id) // 43bf66ec-658a-4f59-8f89-2aac5ae96e6a
console.log(flight2.id) // cbe35c9c-60f3-11ed-9b6a-0242ac120002
But always remember that if you have already set the property in
one of these methods, the attributes()
method will not overwrite
them:
import { Flight } from '#src/models/Flight'
// Setting my own id attribute
const flight = await Flight.create({
id: '299dabf8-60f4-11ed-9b6a-0242ac120002'
})
console.log(flight.id) // 299dabf8-60f4-11ed-9b6a-0242ac120002
Database connections
By default, all models will use the default database connection
configured for your application. If you would like to specify a
different connection that should be used when interacting with a
particular model, you should define a static connection()
method on
the model:
import { BaseModel } from '@athenna/database'
export class Article extends BaseModel {
public static connection() {
return 'mysql'
}
/*...*/
}
Factory definition
The static method definition()
of your model is used when calling
the factory()
method. As the name says, this method is used to define
a blueprint of your model to be used when fabricating fake records of your
model using factories.