-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error when querying with "sort" and "page" parameters. #295
Comments
Thanks for raising. My guess is it's originating from here: Which would imply some sort of change by Laravel. But not sure. Will need to investigate. |
Any news on this? 🙂 |
Apologies, had completely forgotten about this. Just taking a look now. |
@cswgr I can't reproduce. Please can you provide a full stack trace, and the schema for the relevant resource type that you're querying. Also, can you confirm whether you get this for every resource type, or if it is working for some and not others? |
@lindyhopchris I've just run into this problem as well. For me, some resource types are working properly some are not. In my case, the ones that aren't working are resources that are Eloquent models backed by MongoDB. I can sort the mongodb resources successfully, and can paginate the mongodb resources successfully, but when trying to both sort and paginate the resources, I get this same error. Any help would be greatly appreciated. My current package versions:
An example Schema that is showing this behavior: <?php
namespace App\JsonApi\V1\UserLmsActivities;
use App\Models\UserLmsActivity;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\ArrayHash;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Number;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
class UserLmsActivitySchema extends Schema
{
/**
* The model the schema corresponds to.
*
* @var string
*/
public static string $model = UserLmsActivity::class;
protected string $type = 'user-lms-activities';
protected ?array $defaultPagination = ['size' => 50, 'number' => 1];
protected $defaultSort = '-createdAt';
/**
* Get the resource fields.
*
* @return array
*/
public function fields(): array
{
return [
ID::make()->matchAs('[a-zA-Z_0-9]+'),
Number::make('userId', 'user_id'),
ArrayHash::make('activity'),
DateTime::make('createdAt')->sortable()->readOnly(),
DateTime::make('updatedAt')->sortable()->readOnly(),
];
}
/**
* Get the resource filters.
*
* @return array
*/
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
/**
* Get the resource paginator.
*
* @return Paginator|null
*/
public function pagination(): ?Paginator
{
return PagePagination::make();
}
} For good measure, this is the Eloquent model for this resource: <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use MongoDB\Laravel\Eloquent\Model;
class UserLmsActivity extends Model
{
protected $connection = 'mongodb';
protected $fillable = [
'user_id',
'activity',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id', 'userId');
}
} This is the full stacktrace of the error: {
"jsonapi": {
"version": "1.0"
},
"errors": [
{
"detail": "LaravelJsonApi\\Eloquent\\Pagination\\PagePagination::LaravelJsonApi\\Eloquent\\Pagination\\{closure}(): Argument #1 ($order) must be of type array, int given, called in /var/www/html/vendor/laravel/framework/src/Illuminate/Collections/Arr.php on line 208",
"meta": {
"exception": "TypeError",
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/Pagination/PagePagination.php",
"line": 223,
"trace": [
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Collections/Arr.php",
"line": 208,
"function": "LaravelJsonApi\\Eloquent\\Pagination\\{closure}",
"class": "LaravelJsonApi\\Eloquent\\Pagination\\PagePagination",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Collections/Collection.php",
"line": 412,
"function": "first",
"class": "Illuminate\\Support\\Arr",
"type": "::"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Collections/Collection.php",
"line": 180,
"function": "first",
"class": "Illuminate\\Support\\Collection",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/Pagination/PagePagination.php",
"line": 223,
"function": "contains",
"class": "Illuminate\\Support\\Collection",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/Pagination/PagePagination.php",
"line": 192,
"function": "doesRequireOrdering",
"class": "LaravelJsonApi\\Eloquent\\Pagination\\PagePagination",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/Pagination/PagePagination.php",
"line": 118,
"function": "defaultOrder",
"class": "LaravelJsonApi\\Eloquent\\Pagination\\PagePagination",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/QueryBuilder/JsonApiBuilder.php",
"line": 363,
"function": "paginate",
"class": "LaravelJsonApi\\Eloquent\\Pagination\\PagePagination",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/QueryToMany.php",
"line": 112,
"function": "paginate",
"class": "LaravelJsonApi\\Eloquent\\QueryBuilder\\JsonApiBuilder",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/eloquent/src/QueryToMany.php",
"line": 129,
"function": "paginate",
"class": "LaravelJsonApi\\Eloquent\\QueryToMany",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/core/src/Core/Store/QueryManyHandler.php",
"line": 121,
"function": "getOrPaginate",
"class": "LaravelJsonApi\\Eloquent\\QueryToMany",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/laravel/src/Http/Controllers/Actions/FetchRelated.php",
"line": 64,
"function": "getOrPaginate",
"class": "LaravelJsonApi\\Core\\Store\\QueryManyHandler",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Controller.php",
"line": 54,
"function": "showRelated",
"class": "App\\Http\\Controllers\\Api\\V1\\UserController",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
"line": 43,
"function": "callAction",
"class": "Illuminate\\Routing\\Controller",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 265,
"function": "dispatch",
"class": "Illuminate\\Routing\\ControllerDispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 211,
"function": "runController",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 808,
"function": "run",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 144,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel-json-api/laravel/src/Http/Middleware/BootJsonApi.php",
"line": 97,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "LaravelJsonApi\\Laravel\\Http\\Middleware\\BootJsonApi",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/passport/src/Http/Middleware/CheckCredentials.php",
"line": 84,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Laravel\\Passport\\Http\\Middleware\\CheckCredentials",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
"line": 51,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
"line": 124,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
"line": 89,
"function": "handleRequestUsingNamedLimiter",
"class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 119,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 807,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 786,
"function": "runRouteWithinStack",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 750,
"function": "runRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 739,
"function": "dispatchToRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 201,
"function": "dispatch",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 144,
"function": "Illuminate\\Foundation\\Http\\{closure}",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/var/www/html/vendor/livewire/livewire/src/Features/SupportDisablingBackButtonCache/DisableBackButtonCacheMiddleware.php",
"line": 19,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Livewire\\Features\\SupportDisablingBackButtonCache\\DisableBackButtonCacheMiddleware",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/vapor-core/src/Http/Middleware/ServeStaticAssets.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Laravel\\Vapor\\Http\\Middleware\\ServeStaticAssets",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
"line": 31,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
"line": 51,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php",
"line": 27,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\ValidatePostSize",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
"line": 110,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php",
"line": 62,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\HandleCors",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php",
"line": 58,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\TrustProxies",
"type": "->"
},
{
"file": "/var/www/html/app/Http/Middleware/UseRequestId.php",
"line": 25,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "App\\Http\\Middleware\\UseRequestId",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 119,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 176,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 145,
"function": "sendRequestThroughRouter",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/var/www/html/public/index.php",
"line": 51,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/resources/server.php",
"line": 23,
"function": "require_once"
}
]
},
"status": "500",
"title": "Internal Server Error"
}
]
} |
I've dug into this a bit further, and in my case, the issue stems from a difference between how the base Eloquent Query Builder stores information about When getting the base query from the base Eloquent Builder here https://github.com/laravel-json-api/eloquent/blob/98899c6d3ebaf801fb35ff4d1b7b9447fcede23b/src/Pagination/PagePagination.php#L221 $query = $query->toBase(); The value of [
[ 'column' => 'column_name', 'direction' => 'asc|desc' ]
]; For that same call but with the MongoDB Query Builder, the value of [
'column_name' => 1 // mongdb uses 1 or -1 to indicate asc or desc sorting
]; I've managed to get it working by creating my own paginator, private function doesRequireOrdering($query): bool
{
if (!$this->primaryKey) {
return false;
}
$qualified = $query->getModel()->qualifyColumn($this->primaryKey);
$query = $query->toBase();
return !Collection::make($query->orders ?: [])->contains(
function (int $order, string $key) use ($qualified) {
return ($this->primaryKey === $key || $qualified === $key);
},
);
} It would be great to just be able to extend that built-in Paginator class but because a lot of the methods and all the properties, have a visibility of I don't know if this is something you would want to do as it technically strays from strictly only supporting the built-in Eloquent Query Builder, but updating the private function doesRequireOrdering($query): bool
{
if (!$this->primaryKey) {
return false;
}
$qualified = $query->getModel()->qualifyColumn($this->primaryKey);
$query = $query->toBase();
return !Collection::make($query->orders ?: [])->contains(function (array|int $order, int|string $key) use ($qualified) {
$col = is_array($order) ?
($order['column'] ?? '')
: (is_int($key) ? '' : $key);
return ($this->primaryKey === $col || $qualified === $col);
});
} |
In Laravel 11.20.0 I get this error when using a GET request with "sort" and "page" parameters together:
LaravelJsonApi\Eloquent\Pagination\PagePagination::LaravelJsonApi\Eloquent\Pagination\{closure}(): Argument #1 ($order) must be of type array, int given, called in /var/www/html/vendor/laravel/framework/src/Illuminate/Collections/Arr.php on line 208
For example when using
?page[number]=1&page[size]=1&sort=email
.Is maybe something wrong with the query string I don't get?
The text was updated successfully, but these errors were encountered: