Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
00:00 - Setting the Baseline 01:01 - Where Do We Define our Macros? 01:47 - Defining A GetCount Model Query Builder Macro 03:15 - Defining our Macro Types on the Model Query Builder 05:54 - Passing Arguments into Macros
What is a Model Query Builder Macro?
In AdonisJS, macros provide us a way to easily extend internal AdonisJS classes with our own methods. A lot in AdonisJS is macroable, including the request, response, and query builder.
When we add a macro, that function will then be readily available to use with instances of whatever class it is we've defined that macro. In the case of the Model Query Builder, adding a macro will make that function available within all model's query builders.
In order for macros to be properly added, we'll need to register them before our application is booted. That makes this an ideal job for a preload, which are preloaded before our application is booted and are typically located within the start directory of our application.
Then, when asked if you'd like to register the preload file in your adonisrc.ts file, be sure to select yes so it is used.
This will create a folder called macros, which we can use to house any/all macros we make for our application. Inside this macros folder it'll then create our model_query_builder_macros.ts file, which will be empty.
Here, we're counting the total number of published posts we have in our database by using the Post model and it's published query scope. We're aliasing that count as "total" via the * as total string inside our count method.
With aggregates, the results are returned in the $extras object of the first index, so we're grabbing just the first item, then reaching for $extras.total
Note that query scopes are a great option when you need to add reusable query methods for a specific model. If you need to add reusable query methods to all models, that's where a macro becomes ideal.
Our Macro
If you're performing a lot of counts, sums, or other aggregations the above query might be all throughout your application, making it a prime candidate for a macro called getCount, getSum, or the like. So, let's convert the count portion from our above query into a getCount Model Query Builder macro.
import { ModelQueryBuilder } from'@adonisjs/lucid/orm'ModelQueryBuilder.macro('macroName', function (this:ModelQueryBuilder) {// perform query operations using `this`})
Copied!
start
macros
model_query_builder_macros.ts
First, we'll import the ModelQueryBuilder class. This class, along with many others in AdonisJS, extend a Macroable class. It is this Macroable class that adds in this macro functionality for us to use.
The first argument is the name we'd like to give this macro. This name is the same name we'll use to call the macro and apply it to our query. The second argument is the function that will get called whenever our macro is called. This function is provided this, which is our query builder instance.
So, we'll want to give our macro a name of "getCount" and within the function, we'll want to apply the count to our query, select the first result, and return back the value within that first result's $extras.total.
import { ModelQueryBuilder } from'@adonisjs/lucid/orm'ModelQueryBuilder.macro('getCount', function (this:ModelQueryBuilder) {returnthis.count('* as total') .first() .then((result:LucidRow) => result.$extras.total)})
Copied!
start
macros
model_query_builder_macros.ts
The only difference from our original query is that we've replaced the Post type with LucidRow. Since this macro will be available for all our model query builders, we need the type to be shared or generic and all model row instances extend LucidRow. Making this the perfect type for us since we only care about the $extras object here for our returned value.
Informing TypeScript
Before we use our macro, we need to inform TypeScript about it. As of right now, this includes adding the macro to two difference interfaces
ModelQueryBuilder - to make the macro name happy
ModelQueryBuilderContract - to make the actual macro function available on the query builder methods.
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!