iwastenot/laravel-api-query-helper 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

iwastenot/laravel-api-query-helper

Composer 安装命令:

composer require iwastenot/laravel-api-query-helper

包简介

A helper class for building API queries in Laravel

README 文档

README

A structured query parser and Eloquent/Scout integration toolkit for Laravel APIs

The iWasteNot API Query Helper provides a consistent way to transform HTTP query parameters into robust Eloquent (or Laravel Scout) queries.
It standardizes filtering, sorting, field selection, and relationship inclusion for JSON APIs without requiring repetitive controller code.

🧱 Core Concept

Write API endpoints like:

GET /api/items?_with=photos&_fields=id,title,price&_sort=-created_at&price-gte=100&status-eq=active

and get this automatically:

Item::with(['photos'])
    ->select(['id', 'title', 'price'])
    ->where('price', '>=', 100)
    ->where('status', '=', 'active')
    ->orderBy('created_at', 'desc')
    ->get();

All query parsing, validation, and recursion are handled internally.

✅ Compatibility

ComponentSupported versions
PHP>= 7.4
Laravel / Illuminate8.x, 9.x, 10.x, 11.x, 12.x

Notes:

  • When PHP is 7.4, only Laravel/Illuminate 8.x is supported.

⚡ Quick Start

composer install --no-interaction --prefer-dist
vendor/bin/phpunit

🚀 Installation

composer require iwastenot/laravel-api-query-helper

Then import the helper where you build your API queries:

use IWasteNot\ApiQueryHelper\Services\ApiQueryHelper;

class ItemController extends Controller
{
    public function index(Request $request, ApiQueryHelper $api)
    {
        $query = Item::query();

        $results = $api->indexHelper($query, $request->all())
            ->paginate();

        return new IndexCollection($results);
    }
}

📚 Documentation

  • docs/tutorials/getting-started.md — walkthrough from model to request
  • docs/how-to/filters.md — filter patterns + suffix list
  • docs/how-to/relations.md — nested _with, _fields, _sort, filters
  • docs/how-to/scout.md — Scout search and _q
  • docs/ApiQueryHelper.md — reference for helper methods
  • docs/ApiModel.md — reference for model helpers
  • docs/glossary.md — glossary of query terms
  • docs/faq.md — common questions and troubleshooting
  • docs/migration-notes.md — behavior changes between releases
  • docs/developer-guide.md — contributor setup notes

⚙️ Supported Parameters

ParameterTypeDescription
_withcomma-separated listEager-load relationships (with())
_fieldscomma-separated listRestrict selected columns
_sortcomma-separated listSort fields, prefix with - for descending; dotted paths (category-name) sort by related columns
_qstringModel-directed search across the model's $searchable fields
_or[N][...]bracketed groupsOr-groups: entries or-combine, groups and-combine with everything else
all othersfiltersTranslated into where*() clauses based on suffix

🔎 Filter Syntax

Filters are derived from all parameters except _with, _fields, _sort, _q, and _or. Keys that start with _ are ignored by the filter parser.

ExampleEffectMethod
status-eq=activestatus = 'active'where()
price-gte=100price >= 100where()
deleted-nulldeleted IS NULLwhereNull()
deleted-not-nulldeleted IS NOT NULLwhereNotNull()
id-in=1,2,3id IN (1,2,3)whereIn()
role-not-in=admin,staffrole NOT IN (...)whereNotIn()
created-between=2024-01-01,2024-12-31Date rangewhereBetween()
gte=price,100price >= 100where()
status=activestatus = 'active'where()
title-lk=%a%|%b%title LIKE '%a%' OR title LIKE '%b%'grouped orWhere()
category-name-lk=%x%parent rows with a matching categorywhereHas()
_or[0][a-lk]=%x%&_or[0][b-lk]=%y%(a LIKE '%x%' OR b LIKE '%y%')nested where()

Supported suffixes

The helper supports these suffixes by default:

  • lk, like => where ... like
  • not-lk, not-like => where ... not like
  • gte => where ... >=
  • lte => where ... <=
  • gt => where ... >
  • lt => where ... <
  • eq => where ... =
  • not, not-eq => where ... !=
  • null => whereNull
  • not-null => whereNotNull
  • in => whereIn
  • not-in => whereNotIn
  • between => whereBetween
  • not-between => whereNotBetween
  • date => whereDate
  • year => whereYear
  • month => whereMonth
  • day => whereDay
  • time => whereTime
  • column => whereColumn

For null / not-null, the value list is ignored and no parameters are passed to the where method.

Suffixes and aliases are normalized via a configurable $suffixes map. In the suffix-as-key form (for example, gte=price,100), the first value is the field name and the remaining values are parameters for the where clause. In the suffix-in-key form, hyphens in the field portion are interpreted as dots for nested relations (for example, photos-filename-like=image targets photos.filename).

🧮 Eloquent Integration

Models intended for API use should extend the provided ApiModel class.

Example

use IWasteNot\ApiQueryHelper\Models\ApiModel;

class Item extends ApiModel
{
    protected $fillable = ['title', 'price', 'status'];
    protected $joinable = ['photos', 'tags'];
    protected $details = ['description', 'notes'];

    public function photos() { return $this->hasMany(Photo::class); }
    public function tags() { return $this->belongsToMany(Tag::class); }
}

This gives ApiQueryHelper introspection capabilities through:

  • getJoinable() — whitelist of relationships allowed in _with
  • getKeys() — all known safe fields
  • getIndexKeys() — indexable, visible subset
  • getIndexAttributes() — filtered attributes for display

💡 Usage Examples

Basic filtering, sorting, and field selection

GET /api/items?_fields=id,title,price&_sort=-created_at&price-lte=100&status-eq=active
Item::select(['id','title','price'])
    ->where('price', '<=', 100)
    ->where('status', '=', 'active')
    ->orderBy('created_at', 'desc')
    ->get();

Eager loading and nested filtering

GET /api/items?_with=photos,tags&photos.filename-like=image

→ Loads related photos where the filename matches "image".

Nested relation fields and sorts

GET /api/items?_with=photos&photos._fields=id,filename&photos._sort=-created_at

→ Scopes _fields and _sort to the photos relation.

Full-text search with Laravel Scout

$results = $api->indexHelper(Item::class, ['_q' => 'hammer']);

When you call indexHelper() with a model class name, Scout mode is used only when the params are limited to _q, per_page, and offset. The helper does not apply pagination itself; those values are for your controller/paginator.

_q searches the fields that your Scout driver indexes for the model. Define them by overriding toSearchableArray() and reindex after changes.

Custom prefiltering in models

public function indexHelperEloquent($query, $joins, $fields, $sorts, $filters)
{
    $filters['status'][] = ['=', 'active'];
    return [$query, $joins, $fields, $sorts, $filters];
}

See docs/ApiQueryHelper.md for extension-point details.

Show helper for single models

$item = Item::findOrFail($id);
$item = $api->showHelper($item, request()->all());

→ Loads _with relations without altering the base query.

🧩 Internal Structure

FileRole
ApiQueryHelper.phpMain query parser and orchestrator
Arr.phpArray filtering utilities for dotted keys
ApiModel.phpBase model with introspection hooks
IndexCollection.php(optional) Resource wrapper for paginated results

🧪 Testing

vendor/bin/phpunit

⚠️ Limitations

  • Scout filters are not whitelisted via getKeys().
  • Pagination is controlled by your controller (the helper does not paginate).

🩹 Troubleshooting

  • If Scout returns no results, confirm the driver is configured and the model index is built/rebuilt.
  • If filters are ignored, ensure the field is present in getKeys().
  • If nested filters do nothing, confirm the parent relation is in _with.

Changelog

See CHANGELOG.md for release notes and historical changes.

📄 License

MIT. See LICENSE.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-14

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固