Lighthouse: Using @search directive with scopes

Created on 13 May 2019  路  9Comments  路  Source: nuwave/lighthouse

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

discussion question

All 9 comments

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.

See: https://github.com/nuwave/lighthouse/blob/d6944386e2020dec767f236025e0d80704638ef2/src/Scout/ScoutEnhancer.php#L70

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.

https://github.com/nuwave/lighthouse/issues/1846

@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

image

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m1guelpf picture m1guelpf  路  3Comments

mikeerickson picture mikeerickson  路  3Comments

mehranabi picture mehranabi  路  3Comments

alexwhb picture alexwhb  路  4Comments

Leuloch picture Leuloch  路  3Comments