Skip to content
markdown
---
name: packages-larajs-query
description: Use when the user asks about LaraJS Query package, dynamic Eloquent query building, API filtering (equals, lessThan, greaterThan, contains, startsWith, endsWith, any, between, relation, not, has, or, and), sorting (ascending, descending, multi-attribute, relationship sorting with BelongsToThrough), searching (whereLike partial match), including relationships with aggregates (count, exists, sum, min, max, avg), field selection, date filtering, pagination (default, simple, cursor), allow query configuration (QueryParserAllowDTO), or the repository pattern (ReadRepository, WriteRepository, BaseLaraJSRepository).
---

# 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

```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:

```http
?filter=expression
```

| **Operation**                          | **Function**             | **Example**                                                               |
| -------------------------------------- | ------------------------ | ------------------------------------------------------------------------- |
| Equality                               | `equals`                 | `?filter=equals(name,'Smith')`                                            |
| Equality relationship                  | `equalsRelation`         | `?filter=equalsRelation(articles, name,'Smith')`                          |
| Less than                              | `lessThan`               | `?filter=lessThan(age,'25')`                                              |
| Less than relationship                 | `lessThanRelation`       | `?filter=lessThanRelation(articles,age,'25')`                             |
| Less than or equal                     | `lessOrEqual`            | `?filter=lessOrEqual(lastModified,'2001-01-01')`                          |
| Less than or equal relationship        | `lessOrEqualRelation`    | `?filter=lessOrEqualRelation(articles,lastModified,'2001-01-01')`         |
| Greater than                           | `greaterThan`            | `?filter=greaterThan(duration,'6:12:14')`                                 |
| Greater than relationship              | `greaterThanRelation`    | `?filter=greaterThanRelation(articles,duration,'6:12:14')`                |
| Greater or equal                       | `greaterOrEqual`         | `?filter=greaterOrEqual(percentage,'33.33')`                              |
| Greater or equal relationship          | `greaterOrEqualRelation` | `?filter=greaterOrEqualRelation(articles,percentage,'33.33')`             |
| Contains                               | `contains`               | `?filter=contains(description,'cooking')`                                 |
| Contains relationship                  | `containsRelation`       | `?filter=containsRelation(articles,description,'cooking')`                |
| Starts with                            | `startsWith`             | `?filter=startsWith(description,'The')`                                   |
| Starts with relationship               | `startsWithRelation`     | `?filter=startsWithRelation(articles,description,'The')`                  |
| Ends with                              | `endsWith`               | `?filter=endsWith(description,'End')`                                     |
| Ends with Relationship                 | `endsWithRelation`       | `?filter=endsWithRelation(articles,description,'End')`                    |
| Equals one value from set              | `any`                    | `?filter=any(chapter,'Intro','Summary','Conclusion')`                     |
| Equals relationship one value from set | `anyRelation`            | `?filter=anyRelation(chapter,name,'Intro','Summary')`                     |
| Filter between                         | `between`                | `?filter=between(updated_at,'2025-01-01 00:00:00','2025-01-15 23:59:59')` |
| Filter between relationship            | `betweenRelation`        | `?filter=betweenRelation(articles,price,'10','20')`                       |
| Filter relation                        | `relation`               | `?filter=relation(users,and(equals(name,'Smith'),greaterThan(age,'25')))` |
| Negation                               | `not`                    | `?filter=not(equals(lastName,null))`                                      |
| Existence of a relationship            | `has`                    | `?filter=has(articles,'2')`                                               |
| Conditional logical OR                 | `or`                     | `?filter=or(has(orders,'1'),has(invoices,'1'))`                           |
| Conditional logical AND                | `and`                    | `?filter=and(has(orders,'1'),has(invoices,'1'))`                          |

## Sorting

| **Operation**       | **Example**                       |
| ------------------- | --------------------------------- |
| 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**

LaraJS leverages [BelongsToThrough](https://github.com/staudenmeir/belongs-to-through) to manage complex relationships.

Example: Sort `Comment` records based on the `name` attribute of the related `Country` model:

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

## Searching

Perform searches using the `search` query string parameter. Applies a `whereLike(attribute, '%value%')` query:

| **Operation**       | **Example**                                         |
| ------------------- | --------------------------------------------------- |
| 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

| **Operation**                                  | **Example**                                                                                          |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| 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

| **Operation** | **Example**                   |
| ------------- | ----------------------------- |
| Attributes    | `?select=id,name,description` |

## Date Filtering

The `date` parameter automatically calculates the `startOfDay` and `endOfDay` values. Both methods apply a `whereBetween(attribute, [startDate, endDate])` query.

| **Operation**        | **Example**                                                                                 |
| -------------------- | ------------------------------------------------------------------------------------------- |
| 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

| **Operation** | **Example**                                                                                   |
| ------------- | --------------------------------------------------------------------------------------------- |
| `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 (Recommended)

Use allow query to control which fields are available for querying. By default, all fields in a model are available.

```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

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

### BaseLaraJSRepository

```php
<?php

namespace LaraJS\Query\Repositories;

abstract class BaseLaraJSRepository implements ReadRepositoryInterface, WriteRepositoryInterface
{
}
```

### ReadRepository

```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

```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;
}
```