Ready to get started?

Join Adocasts Plus for $8/mo, or sign into an existing Adocasts Plus account, to get access to all of our lessons.

robot mascot smiling

A Deep Dive Into Mixins & Compose

In This Lesson

We'll walk through what mixins are and how they work by first inspecting them as plain objects then working our way into classes then finally TypeScript. We'll then implement mixins via AdonisJS' compose utility.

Created by
@tomgobich
Published

Join the Discussion 6 comments

Create a free account to join in on the discussion
  1. Thank you for the video. I have few questions : Can we use decorators inside the mixin ? For exemple if we want to define a relationship between the model and itself (a recursive relationship)

    1
    1. Responding to ArthurFranckPat
      @tomgobich

      Hey Arthur! I just gave it a go and everything seems to be working a-okay with using decorators in a mixin! Only caveat is, it looks like you have to give the class a name and return it via that name on a separate line for TypeScript to be happy. Below is what I tried, note the imports are for AdonisJS 6, but the functionality would be the same between 5 & 6.

      
      import { BaseModel, belongsTo, column } from "@adonisjs/lucid/orm"
      import { NormalizeConstructor } from '@adonisjs/core/types/helpers'
      import Organization from "#models/organization"
      import type { BelongsTo } from "@adonisjs/lucid/types/relations"
      
      export const WithOrganization = <T extends NormalizeConstructor<typeof BaseModel>>(superclass: T) => {
        class parentclass extends superclass {
          @column()
          declare organizationId: number
      
          @belongsTo(() => Organization)
          declare organization: BelongsTo<typeof Organization>
        }
      
        return parentclass
      }
      Copied!
      • app
      • models
      • mixins
      • organization.ts
      
      import { DateTime } from 'luxon'
      import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
      import type { HasMany } from '@adonisjs/lucid/types/relations'
      import Course from '#models/course'
      import { compose } from "@adonisjs/core/helpers"
      import { WithOrganization } from "#models/mixins/organization"
      
      export default class Difficulty extends compose(BaseModel, WithOrganization) {
        @column({ isPrimary: true })
        declare id: number
      
        @column()
        declare name: string
      }
      
      Copied!
      • app
      • models
      • difficulty.ts

      As a heads up, you can define a parent/child relationship directly on the model without using compose. It'll look like the below.

      export default class Taxonomy extends BaseModel {
        // other columns
      
        @column()
        declare parentId: number | null // parent taxonomy's id (null for top-level)
      
        @belongsTo(() => Taxonomy, {
          foreignKey: 'parentId', // use parentId to define parent relationship
        })
        declare parent: BelongsTo<typeof Taxonomy>
      }
      Copied!

      Hope this helps!
      —-
      Edit: Updated to account for the TypeScript requirement of the mixin class needing a name and for the return to be on a separate line. I missed the small red squiggly on the decorator when I first attempted this.

      0
  2. @danilesky17

    Hello, what about this in adonisJS v6 , there is no such a Normalizing class or maybe i cannot find it in documentation ? Thanks for answear :)

    1
    1. Responding to danilesky17
      @tomgobich

      Hi Danilesky17! Hmm, yeah it seems to have slipped from the documentation. Luckily though, it is still within AdonisJS, it's just now under the core helpers. Below is the example from this lesson updated for AdonisJS 6!

      import { BaseModel } from '@adonisjs/lucid/orm'
      import { NormalizeConstructor } from '@adonisjs/core/types/helpers'
      
      export const WithExtras = <T extends NormalizeConstructor<typeof BaseModel>>(superclass: T) => {
        return class extends superclass {
          serializeExtras: boolean = true
      
          get meta() {
            return this.$extras
          }
        }
      }
      Copied!
      import { compose } from '@adonisjs/core/helpers'
      import { WithExtras } from './mixins/serialize.js'
      import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
      
      export default class Topic extends compose(BaseModel, WithExtras) {
        @column({ isPrimary: true })
        declare id: number
      
        @column()
        declare name: string
      }
      Copied!

      Hope this helps!

      1
      1. Responding to tomgobich
        @danilesky17

        Tom, thank you for quick response! It helped me a lot. I couldn't find this constructor before and VS code didn't offer me when i was trying to find it. Now it seems to be working correctly. Thanks.

        1
        1. Responding to danilesky17
          @tomgobich

          Anytime!! Yeah, I tried the same thing and had the same result. It's a hidden little bugger lol 😊

          0