thecodingmachine / graphqlite

Use PHP Attributes/Annotations to declare your GraphQL API

Home Page:https://graphqlite.thecodingmachine.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Example on the index page

tasselchof opened this issue · comments

I know it's not a big deal, but on the page about "other frameworks", I faced an issue.

On https://graphqlite.thecodingmachine.io/docs/other-frameworks, using these lines like this:

$factory->addControllerNamespace('App\\Controllers\\')
        ->addTypeNamespace('App\\');

was not working. The correct example is:

$factory->addControllerNamespace('App\\Controllers')
        ->addTypeNamespace('App');

I am not sure if it's linked to a new library version for reading classes, as I never used earlier versions of this library.

@tasselchof You're correct, we recently changed out our autoloading/class-finder library, and the new one doesn't work properly with trailing slashes, apparently.

@oojacoboo, it would be great to have a note about it somewhere, as it took a bit of reverse-engineering to figure this one out =)

By the way, I've managed to fix types with those issues, but it does not read Queries (introspection is returning me a Dummy one).

I've used it in a Mezzio application, and the query has such an attribute:

#[API\Query]
public function getList(array $queryParams): UserCollection
{
   return $this->userService->getUsers($queryParams);
}

And schema is configured like that:

$factory->addControllerNamespace('Api\\User\\Handler');

I am figuring out now what is wrong this way, but if you have any ideas, I would be thankful.

@tasselchof if you're using dev-master in Composer, make sure your query classes are autoloaded by Composer. The new autoloading behavior relies on Composer's autoload. Of course, this can be overridden if necessary using the setFinder on your SchemaFactory. We're now using https://github.com/alekitto/class-finder for class discovery.

I am using 7.0 version: "thecodingmachine/graphqlite": "^7.0", as it's Mezzio (Laminas) the classes are auto-loaded (app is relying on composer to do so).

Well, 7.0 isn't using Composer autoload. That's on master. But the trailing forward-slashes aren't compatible with the new class-finder lib, I know that much. As for your other issue, I don't know without more information. I'd need more insight into your dir structure, your namespace design and error stack-traces.

Well, 7.0 isn't using Composer autoload. That's on master. But the trailing forward-slashes aren't compatible with the new class-finder lib, I know that much. As for your other issue, I don't know without more information. I'd need more insight into your dir structure, your namespace design and error stack-traces.

It's a pretty standard app, and the boilerplate can be found here: https://github.com/dotkernel/api/.

The code I've shown is a method I used to test in one of the handlers, specifically this one: https://github.com/dotkernel/api/blob/4.0/src/User/src/Handler/UserHandler.php.

For the rest of the purposes, I've added the part I mentioned earlier.

If you need me to provide more specifics, I am happy to do so.

I mean, I don't see any obvious organization issues. But I also don't have a lot to go off of. At the least, I need an error/stacktrace.

It was just a syntax error in handler. It's fine now, just need to figure out how to properly annotate this:

public function getList(array $queryParams): UserCollection

As I am getting now:

Message: For parameter $queryParams, in Api\User\Handler\UserHandler::getList, please provide an additional @param in the PHPDoc block to further specify the type of the array. For instance: @param string[] $queryParams.

@tasselchof you can't pass unknown arrays into GraphQLite, and you will not be able to annotate that properly. GraphQL is a strictly typed schema. You'll have to use factories, in this case, or make that array an input object (DTO).

@tasselchof you can't pass unknown arrays into GraphQLite, and you will not be able to annotate that properly. GraphQL is a strictly typed schema. You'll have to use factories, in this case, or make that array an input object (DTO).

Thanks for pointing this out. Can you advise on something else: how I should properly annotate return class if it's a collection of some entities (types)? I am getting this now:

Message: Call to undefined method Api\User\Collection\UserCollection::getIdentity()

So despite having it like that:

    #[API\Query([], 'users', 'User')]
    /**
     * @return User[]
     */
    public function getList(#[API\HideParameter] array $queryParams = []): UserCollection
    {
        return $this->userService->getUsers($queryParams);
    }

I encounter that collection is handled as single object.

What is all that you're passing into the Query attribute? Firstly, you should be using named params - it's just way more clear and a better way to use attributes. So, I'd start with cleaning that up.

Your @return annotation is correct, if you've actually imported with a use statement, that User entity/type.

I was passing it to name query "users" - just one of the experiments, here is how the function looks now:

#[API\Query]
    public function getUsers(#[API\HideParameter] array $queryParams = []): array
    {
        return $this->userService->getUsers($queryParams);
    }

And of course entity User is imported in handler.

After I removed all query attribute arguments it worked flawlessly with UserCollection. Probably the problem was there (in third parameter).

One question: $queryParams is an array of filters: how should I annotate it propertly?

I have no idea what's in that array. That's the issue. You can try string[] maybe, if that works.

@oojacoboo This is an array of filters, something like:

$filters = [
['field' => 'name', 'type' => 'eq', 'value' => 'test', ],
];

@oojacoboo This is an array of filters, something like:

$filters = [
['field' => 'name', 'type' => 'eq', 'value' => 'test', ],
];

This array is awful. Validation on that is a mess. How are you handling that on the REST side? Do you have DTOs and validation logic? GraphQL is a typed schema. You need to redesign something and I don't know what or where. But you need to use DTOs for these filters - none of this list/array mess with such dynamic typing. That cannot be annotated for GraphQLite, only indexed, flat arrays. And you're going to need the key.

Find a way to start using DTOs, either just for GraphQL, or preferably for both. Add a factory middleware to convert your REST into a DTO and feed that to the controller method.

@oojacoboo This is an array of filters, something like:

$filters = [
['field' => 'name', 'type' => 'eq', 'value' => 'test', ],
];

This array is awful. Validation on that is a mess. How are you handling that on the REST side? Do you have DTOs and validation logic? GraphQL is a typed schema. You need to redesign something and I don't know what or where. But you need to use DTOs for these filters - none of this list/array mess with such dynamic typing. That cannot be annotated for GraphQLite, only indexed, flat arrays. And you're going to need the key.

Find a way to start using DTOs, either just for GraphQL, or preferably for both. Add a factory middleware to convert your REST into a DTO and feed that to the controller method.

I understand it might look awful to you, but this one is a standard approach in Laminas Api Tools: https://api-tools.getlaminas.org/documentation/modules/api-tools-doctrine-querybuilder. Filters there are looking something like that:

$_GET = [
    'filter' => [
        [
            'type'  => 'eq',
            'field' => 'name',
            'value' => 'Tom',
        ],
    ],
    'order-by' => [
        [
            'type'      => 'field',
            'field'     => 'startAt',
            'direction' => 'desc',
        ],
    ],
];

As our previous API was running on that I am looking at how to adapt and to run it when we move to GraphQL.

I understand why it's the way it is. I understand that's common in REST design. But GraphQL isn't going to accept unknown input and annotating that isn't possible. I may consider a PR that implements PHPStan's array shapes syntax. But honestly, using DTOs is better.