Many-to-many relations are slightly more complicated than other relationships. An example of such a relationship is a user with many roles, where the roles are also shared by other users. For example, many users may have the role of "Admin". To define this relationship, three models are needed: User, Role, and RoleUser.
The RoleUser contains the fields to hold id of User and Role model. We'll define user_id
and role_id
fields here. The name of RoleUser model could be anything, but for this example, we'll keep it this way to make it easy to understand.
Many-to-many relationships are defined by defining this.belongsToMany()
.
class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), roles: this.belongsToMany(Role, RoleUser, 'user_id', 'role_id') } }}class Role extends Model { static entity = 'roles' static fields () { return { id: this.attr(null) } }}class RoleUser extends Model { static entity = 'roleUser' static primaryKey = ['role_id', 'user_id'] static fields () { return { role_id: this.attr(null), user_id: this.attr(null) } }}
The argument order of the belongsToMany
attribute is:
You may also define custom local key at 5th and 6th argument.
class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null), roles: this.belongsToMany( Role, RoleUser, 'user_id', 'role_id', 'user_local_id', 'role_local_id' ) } }}
To define the inverse of a many-to-many relationship, you can place another belongsToMany
attribute on your related model. To continue our user roles example, let's define the users method on the Role model:
class User extends Model { static entity = 'users' static fields () { return { id: this.attr(null) } }}class Role extends Model { static entity = 'roles' static fields () { return { id: this.attr(null), users: this.belongsToMany(User, RoleUser, 'role_id', 'user_id') } }}class RoleUser extends Model { static entity = 'roleUser' static primaryKey = ['role_id', 'user_id'] static fields () { return { role_id: this.attr(null), user_id: this.attr(null) } }}
As you can see, the relationship is defined the same as its User
counterpart, except referencing the User
model and the order of 3rd and 4th argument is reversed.
Working with many-to-many relations requires the presence of an intermediate model. Pinia ORM provides some helpful ways of interacting with this model. For example, let's assume our User
object has many Role
objects that it is related to. After accessing this relationship, we may access the intermediate model using the pivot
attribute on the models.
const user = User.query().with('roles').first()user.roles.forEach((role) => { console.log(role.pivot)})
Notice that each Role
model we retrieve is automatically assigned a pivot
attribute. This attribute contains a model representing the intermediate model and may be used like any other model.