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

"smallest working example using no framework" doesn't work

shish opened this issue · comments

commented
// $cache is a PSR-16 compatible cache.
// $container is a PSR-11 compatible container.
$factory = new SchemaFactory($cache, $container);

$cache and $container aren't defined anywhere in the example code :(

I've created an empty in-memory cache object to be used as $cache, and an empty container object to be used as $container, and then my schema comes out empty.

I'm guessing that somehow I'm supposed to be supplying a $container that is pre-populated full of Types? (Does addTypeNamespace not automatically load all the types from the namespace?)

Good question. The service container is certainly used for instantiation of various services/classes. Your types, operations and factories should be callable through your container. So yes, "pre-populated", if you'd like to say it like that. Although most containers don't actually "pre-populate", but instead handle instantiation and caching. If they weren't called in this fashion, you wouldn't be able to make use of your container service.

commented

So I'm trying to reverse-engineer graphqlite's expectations by providing a PSR-11 compatible container which just logs calls:

class C implements ContainerInterface
{
    public function get(string $id): mixed {
        echo("get($id)\n");
    }
    public function has(string $id): bool {
        echo("has($id)\n");
        return false;
    }
}

... but apparently it never gets called at all?

Then I wonder if I'm doing something wrong somewhere else:

<?php

namespace Shimmie2;

require_once("vendor/autoload.php");

use TheCodingMachine\GraphQLite\SchemaFactory;
use TheCodingMachine\GraphQLite\Annotations\Type;
use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Query;
use TheCodingMachine\GraphQLite\Annotations\Mutation;
use GraphQL\Utils\SchemaPrinter;

#[Type]
class TagUsage
{
    #[Field]
    public string $tag;
    #[Field]
    public int $uses;

    /**
     * @return TagUsage[]
     */
    #[Query]
    public static function tags(string $search, int $limit=10): array {
        return []; // get from database
    }
}

class C implements \Psr\Container\ContainerInterface
{
    public function get(string $id): mixed {
        echo("get($id)\n");
    }
    public function has(string $id): bool {
        echo("has($id)\n");
        return false;
    }
}

$cache = new \Sabre\Cache\Memory();
$container = new C();
$factory = new SchemaFactory($cache, $container);
$factory->addControllerNamespace('Shimmie2\\')
        ->addTypeNamespace('Shimmie2\\');
$factory->devMode(); // vs prodMode
$schema = $factory->createSchema();
echo(SchemaPrinter::doPrint($schema));
$ php test.php
type Mutation {
  """
  A placeholder query used by thecodingmachine/graphqlite when there are no declared mutations.
  """
  dummyMutation: String
}

type Query {
  """
  A placeholder query used by thecodingmachine/graphqlite when there are no declared queries.
  """
  dummyQuery: String
}

It seems like it's not recognizing your Query attribute, probably since it's defined on a Type. Try adding that to a different class. And yea, it's possible that since there aren't any defined operations the container never gets called?

commented

Tried putting my query in a separate class, even a separate class in a separate namespace (so that addControllerNamespace and addTypeNamespace are separate), no luck :(

Have you tried the namespace without the trailing \? The error makes it clear that there isn't any defined mutations or queries. So, it's not finding them being defined in the controller namespace.

commented

Tried without the trailing slash, still doesn't work

@shish It's worth mentioning that the container is also used for AutoWire.

https://graphqlite.thecodingmachine.io/docs/annotations-reference#autowire

commented

Dropping a note for anybody who stumbles across this thread in the future - I never did figure out how to use graphqlite without a framework, I ended up building my own system from scratch instead - that is very much a minimalist stop-gap though, I'd still rather use the full-featured graphqlite if anybody figures out how ^^;;

@shish We use GraphQLite with our custom "framework", if you want to call it a framework. It's really just a custom application with it's own bootstrapping, request and error handling, and more. I'm not entirely sure what issues you're running into. You've been discussing things at a rather macro level.

commented

I'm literally copy-pasting the "smallest working example using no framework" code into a file called test.php and running php test.php, then trying to fix the issues I run into (some variables aren't defined, and when I define them according to the standards documents referenced in the comments, my output is empty - my attempt to make things work is the code above)

A step-by-step walkthrough of my process from "blank folder" to "try creating a new minimal project according to the minimal-working-example guide" to "running into problems":

$ mkdir example
$ cd example
$ composer require thecodingmachine/graphqlite
$ composer require sabre/cache
$ cat > test.php
<?php

namespace Shimmie2;

require_once("vendor/autoload.php");

use TheCodingMachine\GraphQLite\SchemaFactory;
use TheCodingMachine\GraphQLite\Annotations\Type;
use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Query;
use TheCodingMachine\GraphQLite\Annotations\Mutation;
use GraphQL\Utils\SchemaPrinter;

#[Type]
class TagUsage
{
    #[Field]
    public string $tag;
    #[Field]
    public int $uses;

    /**
     * @return TagUsage[]
     */
    #[Query]
    public static function tags(string $search, int $limit=10): array {
        return []; // get from database
    }
}

class C implements \Psr\Container\ContainerInterface
{
    public function get(string $id): mixed {
        echo("get($id)\n");
    }
    public function has(string $id): bool {
        echo("has($id)\n");
        return false;
    }
}

$cache = new \Sabre\Cache\Memory();
$container = new C();
$factory = new SchemaFactory($cache, $container);
$factory->addControllerNamespace('Shimmie2\\')
        ->addTypeNamespace('Shimmie2\\');
$factory->devMode(); // vs prodMode
$schema = $factory->createSchema();
echo(SchemaPrinter::doPrint($schema));
^D
$ php test.php
type Mutation {
  """
  A placeholder query used by thecodingmachine/graphqlite when there are no declared mutations.
  """
  dummyMutation: String
}

type Query {
  """
  A placeholder query used by thecodingmachine/graphqlite when there are no declared queries.
  """
  dummyQuery: String
}

It looks like there's now some extra examples at the bottom of the no-framework guide, so maybe something in there will give clues as to what I'm missing ^^

What's the error you're getting? You say the output is empty. Can you query the __schema?

It'd be great to get the docs updated on this if you can document the changes and submit a PR upon getting a minimal version running.

commented

When I say the output is empty I mean there are no mutations or queries, just "A placeholder query used by thecodingmachine/graphqlite when there are no declared queries.", even though I have attached #[Query] to a function within the namespace specified by $factory->addControllerNamespace() -- there's no error message, just a lack of output (see the output of running test.php at the bottom )

Okay, played around with your script.

You need to add your classes to the autoloader in your composer.json.

composer --autoload

Which means you need to re-organize your files a bit. GraphQLite is looking in the autoload for the mentioned namespaces.

{
    "require": {
        "thecodingmachine/graphqlite": "^6.1",
        "sabre/cache": "^2.0"
    },

    "autoload": {
        "psr-4": {
            "Shimmie2\\": "src/"
        }
    }
}

Put your controller/type in the src dir in another file.

composer dump-autoload

Again, a PR update on this would be welcomed.

commented

Aha, that is a hugely useful hint, now I at least know where to start looking, thank you :D

Alas, I'm working on adding graphql support to an application that predates psr-4 by around 10 years, and has its whole own module loading stuff...

Maybe as well as updating the docs to include a complete working end-to-end example, I can also submit a PR to allow it to load from existing in-memory classes instead of assuming the presence of an autoloader? 👀

Composer has support for pre PSR-4 autoloading - check the docs.

A PR would be great on the minimal example and docs - maybe including a bit on Composer autoload requirements. There isn't any need to mention pre PSR-4 autoloading. Few people are doing that these days and most have already moved their autoloading to Composer anyway.

For reference, we used to handle autoloading outside Composer and migrated it all to Composer. We even have some non-PSR-4 autoloading in our Composer config - still.