Describe the bug
When using the @search directive in a scoped endpoint lighthouse tries to access the scope in the Scout Query Builder.
Say i have a "User" model with a customers global scope.
If i use the search parameter in the customers query (see below), i get the error reported in #592, wich is maked as closed.
Schema
users(orderBy: [OrderByClause!] @orderBy, search: String @search): [User!]! @paginate(type: "paginator",model: "MakeVendas\\ApiMakeVendas\\User")
customers(orderBy: [OrderByClause!] @orderBy, search: String @search): [User!]! @paginate(type: "paginator",model: "MakeVendas\\ApiMakeVendas\\User", scopes:["customers"])
Output/Logs
Click to expand
Method Laravel\\Scout\\Builder::customers does not exist.",
Environment
Lighthouse Version: 3.6.1
Laravel Version: 5.8.*
PHP Version: 7.3
Additional context
This is related to how Scout and indexed search systems work, not Lighthouse.
If you use TNTSearch as your Scout driver then you can implement directives with filtering effects, but for the most part any search driver which relies on a database external to your application environment is not going to be able to scope filter results because the indexed search database does not have the information necessary to do that.
Here is a directive I have built for use with this to filter results by a relationship property:
<?php
namespace app\GraphQL\Directives;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective;
class WhereHasDirective extends BaseDirective implements ArgBuilderDirective
{
/**
* Name of the directive.
*
* @return string
*/
public function name(): string
{
return 'whereHas';
}
/**
* @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $builder
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
*/
public function handleBuilder($builder, $value)
{
$key = $this->directiveArgValue('key', 'id');
$relationship = $this->directiveArgValue('relationship', $this->definitionNode->name->value);
return $builder->whereHas($relationship, function ($builder) use ($key, $value) {
return $builder
->where(
$key,
$value
);
});
}
/**
* Does this filter combine the values of multiple input arguments into one query?
*
* This is true for filter directives such as "whereBetween" that expects two
* different input values, given as separate arguments.
*
* @return bool
*/
public function combinesMultipleArguments(): bool
{
return false;
}
}
```js
extend type Query @group(middleware: ["auth:api"]) {
inspections(
search: String @search,
orderBy: [OrderByClause!] @orderBy,
): [Inspection!]! @paginate
inspection(project: String! @whereHas(key: "slug"), slug: String! @eq): Inspection @find
}
Here is a builder I am using to apply a "query scope" effect on a search results table. This is more "hard-coded" than I'd like and I'm sure it could be improved upon.
```php
<?php
namespace App\GraphQL\Builders;
use App\Models\Project;
use Laravel\Scout\Builder;
class QueryScopes
{
/**
* Add a limit constrained upon the query.
*
* @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $builder
* @param mixed $value
* @return \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder
*/
public function projects($builder, string $value)
{
if ($value !== 'ALL') {
if ($builder instanceof Builder) {
$constraints = new Project;
if ($value === 'SUBSCRIBED') {
$constraints = $constraints->userIsSubscribed();
} elseif ($value === 'LEAD') {
$constraints = $constraints->userIsAdmin();
}
return $builder->constrain($constraints);
} else {
if ($value === 'SUBSCRIBED') {
return $builder->userIsSubscribed();
} elseif ($value === 'LEAD') {
return $builder->userIsAdmin();
}
}
}
return $builder;
}
}
extend type Query @group(middleware: ["auth:api"]) {
projects(
search: String @search,
scope: ProjectScope @builder(method: "App\\GraphQL\\Builders\\QueryScopes@projects"),
orderBy: [OrderByClause!] @orderBy,
): [Project!]! @paginate
project(id: ID! @slugOrID): Project @find
}
Seems like @SirLamer has got it figured out.
I don't think this is within Lighthouse's scope (hehe), so i will close this for now.
@GregPeden Good day.
I'm trying to implement your scope
projects(
search: String @search,
scope: ProjectScope @builder(method: "App\\GraphQL\\Builders\\QueryScopes@projects"),
orderBy: [OrderByClause!] @orderBy,
): [Project!]! @paginate
but whenever i add a scope it throws me
Found arg builder arguments that do not work with @search
I'm using tntsearch with scout
I am actually just returning the builder at the moment
public function projects($builder, string $value)
{
return $builder;
}
@stephenjason89
Yes, this is a new thing that Lighthouse started doing since I previously posted here. At the time it didn't enforce this validation check, then whatever happened just happened. Most search drivers would just ignore the extra query elements not supported while TNT search actually used them.
This has become a blocker for me as well so I am looking at fixing this now, probably extending the native search directive and dropping the validation check.
For now I am proposing this is as a bug (though it's really a design issue) as I think the best solution is to disable this check.
@GregPeden Thank you for the reply. Will wait till it gets fixed :D
@spawnia Hello, I have just tried lighthouse 5.10.0
it is still giving me this error

I am using https://github.com/yabhq/laravel-scout-mysql-driver#mode
which should support where conditions together with @search just like the tntSearch.
Looking forward to your reply.
Thank you
@stephenjason89 directives have to implement \Nuwave\Lighthouse\Scout\ScoutBuilderDirective in order to work with @search. You can create your own custom builder directives that work with it.