August release - 2020
In the last few weeks, we have fixed a handful of issues and also shipped with some new features and improvements. This blog post summarizes the highlights of the release.
ORM helpers to work with relationships
In the true spirit of making it easier to work with Model relationships, we shipped new methods to count related rows , filter by relationships and apply group limit during preload.
Imagining, you have a blog with categories, posts and comments. Following are the code examples for some of the known use cases.
Get categories list with counts of posts inside them
Many popular blogs shows the count of posts for a category or a tag.
You can achieve the similar results using the following query.
const categories = await Category
.query()
.withCount('posts')
categories.forEach((category) => {
console.log(category.$extras.posts_count)
})
Check for relationship existence
Another frequent use case is to limit the number of parent model records based upon the existence of its relationships.
For example: Show all posts that has received one or more comments.
const posts = await Post
.query()
.has('comments')
The has
method has more variants. We recommend reading the docs
for same.
Preloading group limit
The groupLimit
method uses SQL window functions
to limit the number of rows for preloaded relationships.
Continuing with the blog categories and the posts example. Lets fetch all categories, along with the latest 3 posts in each category.
const categories = await Category
.query()
.preload('posts', (posts) => {
posts
.groupOrderBy('posts.created_at', 'desc')
.groupLimit(3) // 👈
})
The groupLimit
is not similar to just applying the limit
clause on the query. The regular limit clause will fetch a total of 3 posts across all the categories. Whereas, we want 3 post from each category.
New validator rules
The validator has received a bunch of new validation rules related to date-time validation.
after
and before
rules
The after
and the before
rules allows you to enforce a date to be after/before a specified date time or offset. Example:
{
checkin_date: schema.date({}, [
rules.after(4, 'days')
])
}
You can also use the today
and tomorrow
keywords with the after rule
.
{
checkin_date: schema.date({}, [
rules.after('today')
])
}
Similarly, the before
rule enforces the date to be before the specified date or offset.
afterField
and beforeField
rules
Another variant is to compare the date with the value of an existing field. This is super helpful for forms with before
and after
date columns.
{
checkin_date: schema.date({}, [
rules.after('today')
]),
checkout_date: schema.date({}, [
rules.afterField('checkin_date')
])
}
blacklist
The blacklist
rule dis-allows certain values. It is the opposite of the enum schema type. A practical use case is to blacklist certain usernames.
{
username: schema.string([
rules.blacklist([
'super',
'admin',
'root',
'bot',
'hacker'
])
])
}
Support for Basic Auth
We have also added the another auth driver that uses the HTTP basic auth for authenticating requests. Using it is as simple as dropping the auth middleware on a route.
Route
.get('posts', async ({ auth }) => {
return `You are logged in as ${auth.user!.email}`
})
.middleware('auth:basic')
Trap events
One of the primary goals of AdonisJS is to make it easier for you to test your applications. That's why along with the option of trapping emails, we now also allow trapping emitter events.
import User from 'App/Models/User'
import Event from '@ioc:Adonis/Core/Event'
Event.trap('new:user', (user) => {
assert.instanceOf(user, User)
})
To trap all the events, you can make use of the trapAll
method.
Event.trapAll((event, data) => {
})
Once done with the test, you can call the restore
method to dispose traps.
Event.restore()