Skip to content

LaraJS Query

Introduction

LaraJS Query is a powerful package that simplifies querying Eloquent models by dynamically filtering, sorting, and including relationships based on incoming requests. It provides a flexible interface for client-side queries and streamlines the process of retrieving resources in your Laravel applications.

Quick Start

Here's how to integrate the package with your Laravel controllers:

php
<?php

use App\Models\Category;
use LaraJS\Query\LaraJSQuery;
use LaraJS\Query\DTO\QueryParserAllowDTO;

class CategoryController
{
    use LaraJSQuery;

    public function index(Request $request)
    {
        return $this->applyLaraJSQuery(Category::query(), QueryParserRequestDTO::fromArray($request->query()), QueryParserAllowDTO::fromArray([]))->get();
    }
}

Filtering

Easily filter resources by attributes using the filter query string parameter. The following operations are supported:

http
?filter=expression
OperationFunctionExample
Equalityequals?filter=equals(name,'Smith')
Equality relationshipequalsRelation?filter=equalsRelation(articles, name,'Smith')
Less thanlessThan?filter=lessThan(age,'25')
Less than relationshiplessThanRelation?filter=lessThanRelation(articles,age,'25')
Less than or equallessOrEqual?filter=lessOrEqual(lastModified,'2001-01-01')
Less than or equal relationshiplessOrEqualRelation?filter=lessOrEqualRelation(articles,lastModified,'2001-01-01')
Greater thangreaterThan?filter=greaterThan(duration,'6:12:14')
Greater than relationshipgreaterThanRelation?filter=greaterThanRelation(articles,duration,'6:12:14')
Greater or equalgreaterOrEqual?filter=greaterOrEqual(percentage,'33.33')
Greater or equal relationshipgreaterOrEqualRelation?filter=greaterOrEqualRelation(articles,percentage,'33.33')
Containscontains?filter=contains(description,'cooking')
Contains relationshipcontainsRelation?filter=containsRelation(articles,description,'cooking')
Starts withstartsWith?filter=startsWith(description,'The')
Starts with relationshipstartsWithRelation?filter=startsWithRelation(articles,description,'The')
Ends withendsWith?filter=endsWith(description,'End')
Ends with RelationshipendsWithRelation?filter=endsWithRelation(articles,description,'End')
Equals one value from setany?filter=any(chapter,'Intro','Summary','Conclusion')
Equals relationship one value from setanyRelation?filter=anyRelation(chapter,name,'Intro','Summary')
Filter betweenbetween?filter=between(updated_at,'2025-01-01 00:00:00','2025-01-15 23:59:59')
Filter between relationshipbetweenRelation?filter=betweenRelation(articles,price,'10','20')
Filter relationrelation?filter=relation(users,and(equals(name,'Smith'),greaterThan(age,'25')))
Negationnot?filter=not(equals(lastName,null))
Existence of a relationshiphas?filter=has(articles,'2')
Conditional logical ORor?filter=or(has(orders,'1'),has(invoices,'1'))
Conditional logical ANDand?filter=and(has(orders,'1'),has(invoices,'1'))

Sorting

You can sort resources by attributes using the sort query string parameter. The following operations are available:

OperationExample
Ascending?sort=id
Descending?sort=-id
Multiple attributes?sort=id,created_at,-updated_at
Relationships?sort=roles.name
Relationships Count?sort=roles_count

Sort Relationships

We leverage the BelongsToThrough package to manage complex relationships.

Comment → belongs to → Post → belongs to → User → belongs to → Country

Example:

You can sort Comment records based on the name attribute of the related Country model by using the following query:

http
{url}/comments?sort=country.name

Searching

Perform searches across attributes using the search query string parameter,. The search will apply a whereLike(attribute, '%value%') query, performing a partial match on the specified value.

OperationExample
An Attribute?search[column]=name&search[value]=larajs
Multiple attributes?search[column]=name,content&search[value]=larajs
Relationships?search[column]=roles.name&search[value]=admin

Including Relationships

Include related models with the include query string parameter:

OperationExample
An Attribute?include[]=roles
Multiple Attributes?include[]=roles&include[]=roles.permissions
Nested Relationship?include[]=roles:id,name&include[]=roles.permissions&include[]=roles.permissions.users:id,username
Aggregates count|exists|sum|min|max|avg?include[]=roles|count&include[]=roles|exists&include[]=permissions|exists
Filter relationship?include[]=comments|and(equals(user_id, '1'),equals(status, '1'))

Selecting Fields

Select specific fields using the select query string parameter:

OperationExample
Attributes?select=id,name,description

Date Filtering

Filter resources by date attributes using either the date query string parameter or the filter function.

The date parameter automatically calculates the startOfDay and endOfDay values.

Both methods apply a whereBetween(attribute, [startDate, endDate]) query.

OperationExample
Attribute (option 1)?date[column]=updated_at&date[value][0]=2024-10-01&date[value][1]=2024-10-15
Attribute (option 2)?date[column]=updated_at&date[value][start]=2024-10-01&date[value][end]=2024-10-15
Attribute (option 3)?filter=between(updated_at,'2025-01-01 00:00:00','2025-01-15 23:59:59')
Attribute (option 4)?filter=and(greaterOrEqual(start_date, '2025-01-01'),lessOrEqual(end_date, '2025-01-15'))

Pagination

Paginate resources with the pagination query string parameter:

OperationExample
default?pagination[limit]=25&pagination[page]=1
simple?pagination[type]=simple&pagination[limit]=25&pagination[page]=1
cursor?pagination[type]=cursor&pagination[cursor]=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

Allow Query

By default, all fields in a model are available for querying. However, you can configure the system to exclude certain fields as necessary by overriding the allowQueryParsers method in your models.

php
<?php

use App\Models\Category;
use LaraJS\Query\LaraJSQuery;

class CategoryController
{
    use LaraJSQuery;

    public function index(Request $request)
    {
        return $this->applyLaraJSQuery(Category::query(), QueryParserRequestDTO::fromArray($request->query()), QueryParserAllowDTO::fromArray([
            'field' => ['id', 'name', 'description'],
            'include' => ['roles'],
            'sort' => ['id', 'updated_at'],
            'filter' => ['name', 'age'],
            'search' => ['id', 'name', 'roles'],
            'date' => ['updated_at'],
        ]))->get();
    }
}

Repository Structure

Here’s the structure of the repository and its core classes:

txt
.
├── BaseLaraJSRepository.php
├── ReadRepository.php
├── ReadRepositoryInterface.php
├── WriteRepository.php
└── WriteRepositoryInterface.php

BaseLaraJSRepository

This abstract class defines the foundation for all repositories:

php
<?php

namespace LaraJS\Query\Repositories;

abstract class BaseLaraJSRepository implements ReadRepositoryInterface, WriteRepositoryInterface
{
}

ReadRepository

This repository handles reading data from the database:

php
<?php

namespace LaraJS\Query\Repositories;

use LaraJS\Query\LaraJSQuery;

class ReadRepository implements ReadRepositoryInterface
{
    use LaraJSQuery;

    public function __construct(protected readonly Model $model, protected readonly int $limit, protected readonly int $maxLimit) {}

    public function findAll(QueryParserAllowDTO $allow): LengthAwarePaginator|CursorPaginator|Paginator|Collection;

    public function find(int $id, QueryParserAllowDTO $allow);

    public function findOrFail(int $id, QueryParserAllowDTO $allow);

    public function query(): Builder;

    public function laraJSQuery(QueryParserAllowDTO $allow, bool $clearFilter = false): Builder;
}

WriteRepository

Handles the creation, updating, and deletion of data:

php
<?php

namespace LaraJS\Query\Repositories;

class WriteRepository implements WriteRepositoryInterface
{
    public function __construct(protected readonly Model $model) {}

    public function create(array $data);

    public function update(int $id, array $data);

    public function delete(int $id): bool;
}