Skip to content

Instantly share code, notes, and snippets.

@FrenchMajesty
Created April 2, 2020 21:17
Show Gist options
  • Save FrenchMajesty/ca9348d62accd7f7d29219ddb1675a07 to your computer and use it in GitHub Desktop.
Save FrenchMajesty/ca9348d62accd7f7d29219ddb1675a07 to your computer and use it in GitHub Desktop.
AdonisJS URI Query Builder
'use strict';
class QueryBuilder {
constructor(params) {
this.query = null;
this.params = this.formatParams(params);
this.hasPagination = false;
this.hasLazyLoad = false;
}
/**
* Format the request URL params to be easy to use and apply
*
* @param {Object} params
* @returns {Object}
*/
formatParams(params) {
params.include = params.include || '';
params.filters = params.filters || {};
params.pagination = params.pagination || {};
params.lazyload = params.lazyload || {};
const include = params.include.split(',');
const filters = {};
Object.keys(params.filters).forEach((type) => {
const filter = params.filters[type];
filters[type] = filter.split(',');
});
return {
include,
filters,
pagination: params.pagination,
lazyload: params.lazyload,
};
}
for(query) {
this.query = query;
return this;
}
fetch() {
if(this.hasPagination) {
return this.fetchWithPagination(); //TODO: What happens if we need the data in JSON?
}
if(this.hasLazyLoad) {
return this.fetchWithLazyLoad();
}
return this.query.fetch();
}
first() {
return this.query.first();
}
firstOrFail() {
return this.query.firstOrFail();
}
/**
* Fetch the query and format the result for use in a lazy loaded list setting
*
* @async
* @returns {Object}
*/
async fetchWithLazyLoad() {
const total = await this.query.getCount();
let data = null;
if(this.hasLazyLoad) {
const { length } = this.params.lazyload;
data = await this.query.limit(length).fetch();
}else {
data = await this.query.fetch();
}
return {
current: data.toJSON().length,
total,
data,
};
}
/**
* Fetch the query and format the result for pagination
*
* @async
* @returns {Object}
*/
async fetchWithPagination() {
const { page, limit } = this.params.pagination;
const total = await this.query.getCount();
const data = await this.applyPagination();
return {
perPage: limit,
lastPage: Math.ceil(total / limit),
page,
total,
data,
};
}
/**
* Apply the pagination params provided to the query
*
* @returns {QueryBuilder}
*/
allowPagination() {
let { page } = this.params.pagination;
this.params.pagination.page = page ? Number(page) : 1;
this.params.pagination.limit = this.params.pagination.limit || 20;
if(page && Number(page)) {
this.hasPagination = true;
}
return this;
}
/**
* Apply query limiting params provided to the query
*
* @returns {Void}
*/
allowLazyLoad() {
let { length } = this.params.lazyload;
if(length) {
this.hasLazyLoad = true;
}
return this;
}
/**
* Apply the pagination to the query
*
* @returns {Array}
*/
applyPagination() {
const { page, limit } = this.params.pagination;
this.query.offset((page-1) * limit).limit(limit);
return this.query.fetch();
}
/**
* Apply the allowed filters provided with the request
*
* @param {Array} filters
* @returns {QueryBuilder}
*/
allowedFilters(filters) {
Object.keys(this.params.filters).forEach((key) => {
const value = this.params.filters[key];
const isAllowed = filters.find((filter) => filter == key);
if(!key.match('custom.') && isAllowed) {
this.query.where(key, 'like', `%${value}%`);
}
});
const {custom} = this.params.filters;
if(custom) {
custom.forEach((customQuery) => {
const isAllowed = filters.find((filter) => filter == `custom.${customQuery}`);
if(isAllowed) this.query[customQuery]();
});
}
return this;
}
/**
* Eager load the allowed relations that were provided in the request
*
* @param {Array} relations The name of the relations allowed to eager load
* @returns {QueryBuilder}
*/
allowedIncludes(relations) {
const toLoad = this.params.include.filter((value) => {
return relations.find((relation) => relation == value);
});
toLoad.forEach((relation) => this.query.with(relation));
return this;
}
}
module.exports = QueryBuilder;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment