Giter Club home page Giter Club logo

laravel-query-builder's Introduction

Build Eloquent queries from API requests

Latest Version on Packagist Test Status Code Style Status Total Downloads

This package allows you to filter, sort and include eloquent relations based on a request. The QueryBuilder used in this package extends Laravel's default Eloquent builder. This means all your favorite methods and macros are still available. Query parameter names follow the JSON API specification as closely as possible.

Basic usage

Filter a query based on a request: /users?filter[name]=John:

use Spatie\QueryBuilder\QueryBuilder;

$users = QueryBuilder::for(User::class)
    ->allowedFilters('name')
    ->get();

// all `User`s that contain the string "John" in their name

Read more about filtering features like: partial filters, exact filters, scope filters, custom filters, ignored values, default filter values, ...

Including relations based on a request: /users?include=posts:

$users = QueryBuilder::for(User::class)
    ->allowedIncludes('posts')
    ->get();

// all `User`s with their `posts` loaded

Read more about include features like: including nested relationships, including relationship count, custom includes, ...

Sorting a query based on a request: /users?sort=id:

$users = QueryBuilder::for(User::class)
    ->allowedSorts('id')
    ->get();

// all `User`s sorted by ascending id

Read more about sorting features like: custom sorts, sort direction, ...

Works together nicely with existing queries:

$query = User::where('active', true);

$userQuery = QueryBuilder::for($query) // start from an existing Builder instance
    ->withTrashed() // use your existing scopes
    ->allowedIncludes('posts', 'permissions')
    ->where('score', '>', 42); // chain on any of Laravel's query builder methods

Selecting fields for a query: /users?fields[users]=id,email

$users = QueryBuilder::for(User::class)
    ->allowedFields(['id', 'email'])
    ->get();

// the fetched `User`s will only have their id & email set

Read more about selecting fields.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/laravel-query-builder

Read the installation notes on the docs site: https://spatie.be/docs/laravel-query-builder/v5/installation-setup.

Documentation

You can find the documentation on https://spatie.be/docs/laravel-query-builder/v5.

Find yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving the media library? Feel free to create an issue on GitHub, we'll try to address it as soon as possible.

If you've found a bug regarding security please mail [email protected] instead of using the issue tracker.

Upgrading

Please see UPGRADING.md for details.

Testing

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you've found a bug regarding security please mail [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

laravel-query-builder's People

Contributors

adrianmrn avatar alexvanderbist avatar amaelftah avatar brendt avatar chapeupreto avatar cornelp avatar dependabot[bot] avatar designvoid avatar dominikb avatar drbyte avatar enricodelazzari avatar freekmurze avatar github-actions[bot] avatar juststeveking avatar klimov-paul avatar laravel-shift avatar marcoocram avatar medalink avatar nielsvanpach avatar ntzm avatar oanhnn avatar patinthehat avatar rainerkent avatar robsontenorio avatar rubenvanassche avatar samuelhgf avatar shaunluedeke avatar stevebauman avatar steven-fox avatar vyuldashev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-query-builder's Issues

'for' method triggers phpfmt to mark that method as a control structure

When using phpfmt to auto-psr2 php files, phpfmt treats the QueryBuilder::for method as a control structure and will attempt to auto format it, resulting in broken code.

$products = QueryBuilder::for(Product::class)->allowedFilters('name')->paginate($count);

becomes

    $products = QueryBuilder::for (Product::class) {
            ->allowedFilters('name')->paginate($count);
    }

Suggest renaming the method or adding a proxy method named forModel.

[Feature] Adding extra parameters to filter/sort

In some cases the operator is selected from client. For example, the user wants to list the people who's salary is less than 2500$ or in a different filter request greater than 2500$. So they want to make this operation switch with the client. Would that be possible to add this feature?
something similar to this filter

First request:
['field' => 'salary', 'value' => '2500', 'operator' => 'less than']

Second request;
['field' => 'salary', 'value' => '2500', 'operator' => 'greater than']

Add ILike varaint for postgres

In mysql "like" is case-insensitive, for postgres ilike is more useful;
or common variant for any db - "like" compare with LOWER() wrapper for column value and searchable value

[QUESTION] Any Ideas to Improve my Source Code

Hi, I am new to this kind of development. So please spare with me If my question sounds silly to you. I found your package and really like it much. I am planning to test Laravel with Vue.js including your package for data exchange.

I faced an issue that I solved at the end but in an very strange way. I wonder if you have an idea on how to achieve this in a smarter and more simpler way.

I have an EAV model that looks like this:

screen shot 2018-01-30 at 23 50 08

** Edit: Relationship PartnerAttribute and AttributeValue is N-1 .. it's to late in the evening :D **

my plan is to output this in vue.js:

screen shot 2018-01-30 at 23 50 19

Here is how I did it:

First, I used your package to query data:

return $this->respond(
            QueryBuilder::for(Attribute::class)
                ->allowedIncludes('attribute.attribute', 'attribute.value')
                ->get()
        );

this gives me following result back:


[
  {
    "id": 1,
    "partner_id": 1,
    "attribute_value_id": 1,
    "level": 2,
    "created_by": 1,
    "updated_by": 1,
    "created_at": "2018-01-30 22:54:43",
    "updated_at": "2018-01-30 22:54:43",
    "attribute": {
      "id": 1,
      "attribute_id": 1,
      "value_id": 1,
      "created_by": 1,
      "updated_by": 1,
      "created_at": "2018-01-30 22:54:43",
      "updated_at": "2018-01-30 22:54:43",
      "value": {
        "id": 1,
        "name": "Javascript",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      },
      "attribute": {
        "id": 1,
        "name": "Software Development",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      }
    }
  },
  {
    "id": 2,
    "partner_id": 1,
    "attribute_value_id": 2,
    "level": 3,
    "created_by": 1,
    "updated_by": 1,
    "created_at": "2018-01-30 22:54:43",
    "updated_at": "2018-01-30 22:54:43",
    "attribute": {
      "id": 2,
      "attribute_id": 1,
      "value_id": 2,
      "created_by": 1,
      "updated_by": 1,
      "created_at": "2018-01-30 22:54:43",
      "updated_at": "2018-01-30 22:54:43",
      "value": {
        "id": 2,
        "name": "PHP",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      },
      "attribute": {
        "id": 1,
        "name": "Software Development",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      }
    }
  },
  {
    "id": 3,
    "partner_id": 1,
    "attribute_value_id": 3,
    "level": 1,
    "created_by": 1,
    "updated_by": 1,
    "created_at": "2018-01-30 22:54:43",
    "updated_at": "2018-01-30 22:54:43",
    "attribute": {
      "id": 3,
      "attribute_id": 2,
      "value_id": 3,
      "created_by": 1,
      "updated_by": 1,
      "created_at": "2018-01-30 22:54:43",
      "updated_at": "2018-01-30 22:54:43",
      "value": {
        "id": 3,
        "name": "Photoshop",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      },
      "attribute": {
        "id": 2,
        "name": "Design",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      }
    }
  },
  {
    "id": 4,
    "partner_id": 1,
    "attribute_value_id": 3,
    "level": 5,
    "created_by": 1,
    "updated_by": 1,
    "created_at": "2018-01-30 22:54:43",
    "updated_at": "2018-01-30 22:54:43",
    "attribute": {
      "id": 3,
      "attribute_id": 2,
      "value_id": 3,
      "created_by": 1,
      "updated_by": 1,
      "created_at": "2018-01-30 22:54:43",
      "updated_at": "2018-01-30 22:54:43",
      "value": {
        "id": 3,
        "name": "Photoshop",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      },
      "attribute": {
        "id": 2,
        "name": "Design",
        "created_by": 1,
        "updated_by": 1,
        "created_at": "2018-01-30 22:54:43",
        "updated_at": "2018-01-30 22:54:43"
      }
    }
  }
]

First Issue: The Attributes like "Software Development" are rendered multiple times. So an easy aggregation of result wasn't possible in HTML. So I looped through the result in Javascript and created my own object

const fetchSkills = (context) => {
  fetchItems()
    .then((response) => {
      var curr = 0
      var item = null
      var data = []
      var record = {}
      var value = {}

      for (var i = 0, len = response.length; i < len; i++) {
        item = response[i]

        if (item.attribute.attribute.id !== curr ) {
          if (curr !== 0) {
            data.push(record)
          }

          curr = item.attribute.attribute.id
          record = {values: []}

          for (var property in item.attribute.attribute) {
            if (item.attribute.attribute.hasOwnProperty(property)) {
              record[property] = item.attribute.attribute[property]
            }
          }
        }

        value = {}
        value = {level: item.level}
        for (var property in item.attribute.value) {
          if (item.attribute.value.hasOwnProperty(property)) {
            value[property] = item.attribute.value[property]
          }
        }

        record.values.push(value)
      }
      data.push(record)

      console.log(data)
      context.commit('PARTNER_SKILLS_SET', data);

    })
    .catch((error) => {
      console.error(error);
    });
};

the result is as expected and I could easily loop through it with v-loop in JS

screen shot 2018-01-31 at 00 10 11

However, as mentioned above, this looks very cumbersome and somehow wrong. I wonder if you have an idea on how to make this better by keeping same flexibility in the data model ?

thank you so much for your time !! And sorry if this if this questions sounds odd to you. It is the first time I am dealing with this kind of challenge ;-)

Johnson

Example selecting specific columns on relationship not working

The following given example does not seem to work:
GET /posts?include=author&fields[author]=name

The value of the author relation will be null.

It looks like when calling a One To One (Inverse) or a One To Many (Inverse) relationship and specifying at least one field of the relationship, you have to include the primary_key as following:
GET /posts?include=author&fields[author]=id,name

Same goes for the One To One and the One To Many relationships while you are specifying at leat one field for the relationship. There it seems you have to include the foreign_key as following:
GET /author?include=post&fields[post]=author_id,body
and
GET /author?include=posts&fields[posts]=author_id,body

Many To Many Relationships work as expected.

Is this supposed to work this way or should the example work as given?

Skip exceptions

Hello, are there any ability to skip exceptions and do silent query?

Nested arrays does not work when filtering

PHP Version: 7.1.13
Laravel Version: 5.5

My filter seems like this filter[scope_function][foo][bar]=1, which should be handled by query builder as a nested array on the query string.
Although, the request fails with this message:

Type error: Argument 1 passed to Illuminate\\Support\\Collection::map() must be callable, object given, called in /path/to/QueryBuilderServiceProvider.php on line 113

Taking a look to the source code, when the filter receives a nested array, it should recursively handle and parse the content.

if (is_array($value)) {
    return collect($value)->map($this)->all();
} 

The code above is inside the closure which parse the filter's content, and the this is not the Request instance, but the same closure instance. But the map method from collection receives a callable, so it fails because this is a closure object. Searching a little more about it, I've found that we should bind the closure instance to an object, that could be the same closure instance again. So it would be something like:

if (is_array($value)) {
    return collect($value)->map($this->bindTo($this))->all();
} 

Sorting by related properties

As described in the spec here: http://jsonapi.org/format/#fetching-sorting

It is recommended that dot-separated (U+002E FULL-STOP, โ€œ.โ€) sort fields be used to request sorting based upon relationship attributes. For example, a sort field of author.name could be used to request that the primary data be sorted based upon the name attribute of the author relationship.

However this is not possible on an eloquent query builder. Model::with('related')->orderBy('related.name')->get() doesn't work as the related models would be fetched in a second query and the first query doesn't know the related.name field.
I'm also not sure what the intended behaviour would be relationships returning multiple models. (Sort the related models first; then sort parent models by first related model?)

As I see it there a couple different solutions:

  • if there are any sort queries for related properties (containing .) fetch the results first and sort the collection after that. This doesn't work if you're paginating or limiting the query
  • use a select to include the related property on the initial query and then sort โ€“ only works for belongsTo and hasOne relationships and feels like a pain to implement

Any thoughts/insights on this are very welcome!

Model guarded attribute doesn't play

Hi,

Model {
guarded : deleted_at
}

QueryBuilder(Model)->get()

returns all Model attributes, is there a way to link the QueryBuilder and Laravel Model guarded attribute together ?

Nested array in filters

I'll start of by saying thank you for a great package.

How ever there is one functionality that I'm missing, that is to have nested array filter query params. To give you some context is that I'm trying to build a filter that need a very nested set from the url query string. I was able to build a normal range filter by calling filter[price][min]=1 and filter[price][max]=10 That works all fine and good when I'm querying directly to a Products model.

However our database set up is something like this Product->hasMany(Stock)->hasOne(Warehouse) So what I wanted to achieve is to build a filter such as this: filter[stock][warehouseName][min]=10 Currently if I try this query then it blows up with a 500 error.

I was able to get my filter to work but then the query params look something like this filter[stock][warehouseName:min]=10 but then in the filter I need to explode the keys and the whole thing is very dirty and doesn't match the way i'm filtering the price for example.

Is this something that could be implemented/allowed?

[Feature] ->allowedAppends()

@AlexVanderbist
It would be nice if we can dynamically append a property (or many) from Models

/users?append=fullname

$users = QueryBuilder::for(User::class)
                    ->allowedAppends(['fullname'])
                    ->paginate();
class User extends Model {

   public function getFullnameAttribute()
   {
         return $this->first_name.'  '.$this->last_name
   }
}

Exception thrown when no sort is provided in query string and calling allowedSorts

It seems as though when allowedSorts() is called, not including a sort in the query string, the sort is assumed to be "" (empty string) and an exception is thrown.

The query string:

.../transactions?filter[type]=deposit

The exception:

"Exception": {
    "messages": [
        "Given sort `` is not allowed. Allowed sorts are `id, created_at, type, amount, balance`."
    ],
    "exception": "Spatie\\QueryBuilder\\Exceptions\\InvalidQuery",
    "file": "/Users/jeffrose/projects/rivalry/vendor/spatie/laravel-query-builder/src/Exceptions/InvalidQuery.php",
    "line": 27
}

The code:

$paginator = QueryBuilder::for($query)
    ->allowedFilters(Filter::exact('id'), 'created_at', Filter::exact('type'), 'amount', 'balance')
    ->allowedSorts('id', 'created_at', 'type', 'amount', 'balance') // If I comment out this line no exception is thrown
    ->paginate($pageSize);

The workaround:

Leave out the call to allowedSorts() and everything's fine.

If I get a chance, I'll submit a PR in the coming days to address this.

Pagination query parameter throws an error

Removed filter name from the query parameter by specifying null reference in the config file.

'filter' => null,

After removing filter query parameter name from the URL. Now it throws throws an error when i use the pagination query parameter page Given filter(s) page are not allowed.

Query:
$users = QueryBuilder::for(User::class)->allowedFilters(Filter::custom('role', UserRole::class),Filter::custom('search', UserSearch::class))->with('roles')->paginate(1);

URL
?search=john&role=4&page=2

Essential information on install instructions

As this package does not provide a facade, the install instructions have a missing important step, say the needed add an alias on config/app.php. Without this, is impossible use the Package on controllers.

PostgreSQL exception

#71 seems to break the FilterPartial in combination with Postgres.

I am getting:
SQLSTATE[42883]: Undefined function: 7 ERROR: operator does not exist: ` character varying LINE 1: ..._accounts"."user_id" is not null and LOWER(`some_filter`...

HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. (SQL: select "my_accounts".* from "my_accounts" where "my_accounts"."user_id" = 1 and "my_accounts"."user_id" is not null and LOWER(`some_filter`) LIKE %something%)

PostgreSQL 9.6.3

Downgrading to version 1.9.2 solves the issue.

QueryBuilder on multiple conections

my project uses two different databases, when i'm testing my querybuilder in one of my models it gives me the following error:

Illuminate \ Database \ QueryException
could not find driver (SQL: select * from [permissions])

have any workaround to this?

`fields` parameter doesn't play nicely with Laravel Resource by default

Not an issue with the package as such, but perhaps we should override Illuminate\Http\Resources\Json\JsonResource so that when requesting only certain fields, we don't get the stubbed properties with null values.

For example, when querying users in my API:

GET /users?fields[users]=first_name,last_name,id I don't want all of the none included parameters with a null value, but I get this:

{
    "data": [
        {
            "id": 1,
            "firstName": "Christopher",
            "lastName": "Reid",
            "email": null,
            "mobileNumber": null,
            "birthDate": null,
            "addressLine1": null,
            "addressLine2": null,
            "city": null,
            "county": null,
            "postcode": null,
            "balance": 3000
        }
    ],
    "links": {
        "first": "http://low6.test/users?page=1",
        "last": "http://low6.test/users?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "http://low6.test/users",
        "per_page": 15,
        "to": 1,
        "total": 1
    }
}

Obviously, I can filter this in the resource with a trait or something, and filter out only the keys asked for in the fields and include attributes. But it would be nice to add a standardised approach back into the community. Any idea / suggestions welcome. I will happily do a PR after discussion of what people think is the best practice / most appropriate solution for this.

Class 'Spatie\QueryBuilder\QueryBuilderFacade' not found

Hi I'm trying to use this package, but i just can't get it working.

I've got a fresh install of Laravel 5.5

laravel new myapp

Then I run

composer require spatie/laravel-query-builder

and finally a closure in api.php

Route::get('/users', function(){
	$users = QueryBuilder::for(User::class)->get();
	return $users;	
});

But, I get this error

Class 'Spatie\QueryBuilder\QueryBuilderFacade' not found

It's a fresh install of laravel 5.5, and i've tried

composer dump-autoload

and

php artisan cache:clear

Is there anything more I can try??

Thanks!!!

[Question] Query-Builder and JSON:API Output

Dear Spatie Team,

thanks a lot for your awesome open-source packages - i really like the effort you put into your work! Keep it up - you're doing great..

This issue is not an issue - it is rather a general question on how to use this package in combination with "following the JSON:API standard" ( http://jsonapi.org/ )

From what I understand, this package (Laravel-Query-Builder) is used to automatically "map" request query parameters (e.g., include / filter / sort / ...) to a QueryBuilder instance that is used to select respective models from the database. In your README description you state, that these parameters (and the handling of the latter) closely follow the JSON:API specs - which i really (!) really like..

BUT - the description lacks information on returing data to the client. In your README you state:

Once the relationships are loaded on the results collection you can include them in your response by using Eloquent API resources and conditional relationships.

However, the Eloquent API Resources do not allow for writing JSON:API compliant resources (at least not "out of the box). While it allows to "wrap" the content in a data field, it does not use the data.attributes and data.type fields, which should be added in order to comply with the standard. So there would be a huge overload to "re-build" the appropriate schema that is required to actually "use" JSON:API ... As far as i know, Laravels Resources do not have a concept like Serializers known from Fractal, which can change the "output format" automatically.

If i rely on Fractal (and your Laravel-Fractal wrappers) as described in your blog post here ( https://alexvanderbist.com/posts/2018/builder-apis-with-laravel-query-builder )

Alternatively, you can use the tried and tested Fractal package by The League.
i would lose almost all features provided by this package.. But if i stick to APIResources from Laravel i would need to "re-structure" all Resources to follow the JSON:API standard..

So basically here is the question:
What do you suggest? How to use the Query-Builder and generate JSON:API compliant responses? Am I missing something or is it really a "either this or that" situation?!

All the best and thank you very much!

Add feature filter/sort in JSON

I propose to add the ability to filter and sort for fields containing JSON:

Model (example), content - Is a JSON or JSONB field.

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * @var array
     */
    protected $casts = [
        'content' => 'array',
    ];
}

Laravel can handle this:

// filter
Flight::where('content->en->name', 'alexandr')->get();

// sort
Flight::orderBy('content->en->name', 'desc')->get();

My suggestion is to enable filtering and sorting for such things.
Defining allowed as content:

Filtering an API request: /users?filter[content][en][name]=alexandr:

$flight = QueryBuilder::for(Flight::class)
    ->allowedFilters('content')
    ->get();

To avoid overlapping several conditions using the same array.
You can add a check, for example:

if($query->getModel()->hasCast($property,['object','array'])){

}

I hope for your understanding. Thank you.

Nested relation includes too strict?

Hey,

Given this query builder:

QueryBuilder::for(Site::class)
    ->allowedIncludes('division.organisation')
    ->get();

If I only try to include division in my request I get an error saying division isn't in the list of allowed includes. Is this intended behaviour? Feels too restrictive/strict to me.

The work around is to do this:

QueryBuilder::for(Site::class)
    ->allowedIncludes('division', 'division.organisation')
    ->get();

Feels a little repetitive?

If I can get some time I don't mind attempting a PR but let me know how you feel about this.

Filtering data with relations

Hey so I have this issue where I cannot figure out how would I get data.

I'm getting filtered list of users which is connected to roles (many to many/ pivot).
So is there possibility to get role names from some query like this
$users = QueryBuilder::for(User::class) ->allowedIncludes('roles') ->allowedFilters('name', 'email', 'date_created', 'is_active') ->get();

[Question] throwing exception when no results found.

I have the following code which works perfectly when filtering the collection however it would be really nice to be able to simply throw a model not found exception when there are no results.

$users = QueryBuilder::for($model)->query(User::class)
          ->allowedIncludes('department')
          ->allowedFilters('id', 'fname', 'lname', 'uname', 'department_id')
          ->defaultSort('lname')
          ->get();

        return new UserCollection($users);

Is there a simple way to throw a Illuminate\Database\Eloquent\ModelNotFoundException when no results are found without adding additional code in the controller or extending the QueryBuilder class?

Eloquent query gets ignored since 1.9.1

Hi,
since Version 1.9.1 Eloquent Querys get ignored. Basically I do something like this:

QueryBuilder::for(Order::select(#some columns))
->allowedFilters(#same columns as above)
->paginate($request->per_page);

Before 1.9.1 only those columns I selected where in the endresult - with 1.9.1 onwards this part will be ignored. Is this a bug or do I have to refactor this code somehow?

Thanks

Mapping attribute to another name in sort order

I would like to know how do I sort by an attribute that is mapped differently in the database?

In my database, the km field exists, and when I return to the user I use class Resource and return this field as distance.

If it were to filter for example, this attribute, I would use Filter::custom.

use Spatie\QueryBuilder\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;

class FiltersFreightsDistance implements Filter
{
    public function __invoke(Builder $query, $value, string $property) : Builder
    {
        return $query->where('km', '>', $value);
    }
}

use Spatie\QueryBuilder\Filter;

// GET /freights?filter[distance]=200
$freights = QueryBuilder::for(Freights::class)
    ->allowedFilters(Filter::custom('distance', FiltersFreightsDistance::class))
    ->get();

But how would I order by distance?

// GET /freights?sort=distance

Because if I order this way it gives an error:

SQLSTATE [42S22]: Column not found: 1054 Unknown column 'distance' in 'order clause'

filter not worked when value is empty

i want define a filter

when value is empty ( ?filter[parent_id]= ) must be :

where('parent_id', null)
request()->filters() // return [];

when value not empty ( ?filter[parent_id]=1 ) :

where('parent_id', 1);
request()->filters() // return ['parent_id' => 1];

but in your library when value is empty, custom filters not called! how can solve it ?

How can i create a QueryBuilder from a model relation?

I have Product and Category model. A product belongs to many categories then i need a query like below

QueryBuilder::for($category->products())->get();

But it won't work. How can i make it work?

Thanks! great package.

[Feature Request] Range values

Hello. Thanks for great package.
One of thing I'm really missing is range values, here is few cases where it will be useful:

  • Price range on catalog page
  • Date range on orders/payments page

I can implement this feature, but let's discuss how it can be. I have two option:

Option 1

Add filter Filter::range('column_name')
and user will send next request /orders?filter[column_name][min]=VAL&filter[column_name][max]=VAL
If min is absent - take the lowest value
If max is absent - take the highest value

Option 2

Add filter Filter::range('column_name')
and user will send next request /orders?filter[column_name]=MIN_VAL...MAX_VAL
If user request for /orders?filter[column_name]=...MAX_VAL - take from the lowest value to MAX_VAL
If user request for /orders?filter[column_name]=MIN_VAL... - take from MIN_VAL to the highest value

order by created_at desc, created_at desc, created_at desc

->defaultSort('-updated_at')
->allowedSorts('id', 'created_at', 'updated_at')

Request: sort=-created_at&page[size]=25&page[number]=1&filter[status]=deleted
The generated query:

select * from `posts` where `created_by` in (?) 
and `status` = ? 
order by `created_at` desc, `created_at` desc, `created_at` desc
limit 25 offset 0

While debugging, I see that addSortsToQuery is called 3 times with this payload:

Collection {#434
  #items: array:1 [
    0 => "-created_at"
  ]
}

Ability to include withTrashed

In the index of my user controller I have the following:

return QueryBuilder::for(User::class)
	->with('phoneNumbers')
	->allowedIncludes('trashed')
	->get();

I was hoping to pass an include param like this:

http://127.0.0.1:8000/v1/users?include=trashed

To append the withTrashed() global scope to the query.

Is this possible? I am most likely missing something obvious, I have tried a few variations in my testing usually ending up with an error like:

"message": "Call to a member function addEagerConstraints() on boolean",
    "exception": "Symfony\\Component\\Debug\\Exception\\FatalThrowableError",
    "file": "/Users/timwickstrom/Sites/Wavefire/api/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php",
    "line": 522,

Thanks for the help! This has been a HUGE time saver.

Lumen compatibility

Just wanted to check the compatibility level of the package with Lumen 5.6.*.

I just did a base install and I'm currently stuck with the following exception:

Symfony\Component\Debug\Exception\FatalThrowableError thrown with message "Call to undefined function Spatie\QueryBuilder\request()"

Stacktrace:
#1 Symfony\Component\Debug\Exception\FatalThrowableError in /home/vagrant/restaurants-api/vendor/spatie/laravel-query-builder/src/QueryBuilder.php:90
#0 {main} in /home/vagrant/project-name/public/index.php:0

this was caused by a simple call:

QueryBuilder::for(Items::class)
            ->allowedSorts('name')->get();

I'm investigating further, but any insight would be appreciated.

hasMany relationship search filter throws an error

hasMany relationship search filter throws an error

Eloquent Query:
QueryBuilder::for(User::class)->allowedIncludes('roles')->allowedFilters(Filter::exact('role_id'))->with('roles')->paginate(20);
Query Parameters:
/users?include=roles&filter[role_id]=5
Exception:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'role_id' in 'where clause' (SQL: select count(*) as aggregate from users where role_id = 5)

image

Also i want to filter multiple columns(name, email) with single field

Eloquent Query:
QueryBuilder::for(User::class)->allowedFilters('name','email')->paginate(20);
Query Parameters:
/users?filter[search]=search

Expose a public "isAppended($field)" method on QueryBuilder.

I believe it would be a good solution for cases such as counting a relationship:

$builder = QueryBuilder::for(Customer::class);

if($builder->isAppended('orders_count')) {
    $builder->withCount('orders_count');
}

You may also consider adding whenAppended($field):

$builder->whenAppended('orders_count', function($builder){
    $builder->withCount('orders_count');
})

About paginate parameters

QueryBuilder::for(User::class)
      ->allowedFilters(['status'])
      ->paginate(20);   

HTTP GET /user?filter[status]=2

But paginate meta links will get /user?page=1, Filter parameters lost .

Any idea?
Thank all.

Usage with Lumen: request()->fields() method is undefined

I cannot get the request()->fields() method used in QueryBuilder.php to work. It appears to be a method instead of a magic field call.

When calling request()->fields it works as expected.

I am using Lumen 5.6 and also tested this functionality in a laravel 5.6 installation, with same results.

I use the newest release, 1.10.

Ability to include nested relationships

I would like to be able to include nested relationships like you do with load or with: ->load('relation1.relation2')

Obviously this is a bit more difficult because PHP translates . to _ in GET request values

Using in combination with nicolaslopezj/searchable

Hi,

I'm using this package (https://github.com/nicolaslopezj/searchable) to search for students (Student model) within the QueryBuilder package. I have a custom filter named "SearchesStudents". Within the __invoke method I have this:

return Student::search($value);

When I dump this, I get an Builder object. And when I dump this with ->toSql(), I get this:

select * from (select `students`.*, max((case when LOWER(`name`) LIKE ? then 150 else 0 end) + (case when LOWER(`name`) LIKE ? then 50 else 0 end) + (case when LOWER(`name`) LIKE ? then 10 else 0 end) + (case when LOWER(`student_number`) LIKE ? then 150 else 0 end) + (case when LOWER(`student_number`) LIKE ? then 50 else 0 end) + (case when LOWER(`student_number`) LIKE ? then 10 else 0 end)) as relevance from `students` group by `students`.`id` having relevance >= 5.00 order by `relevance` desc) as `students

But when I dump the QueryBuilder in my controller, I still get this:

select `students`.* from `students`

I realize my question is not completely fair, since I combine your package with another one, but maybe you see why it isn't working. The invoke method returns a Builder object as I described.

Awesome package! :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.