nuwave / lighthouse Goto Github PK
View Code? Open in Web Editor NEWA framework for serving GraphQL from Laravel
Home Page: https://lighthouse-php.com
License: MIT License
A framework for serving GraphQL from Laravel
Home Page: https://lighthouse-php.com
License: MIT License
Here you are something thats working for root fields.
this one works to create a Relay type [@]connection:
public function resolve()
{
$data = Car::orderBy('id', 'DESC')->relayConnection($this->args);
$pageInfo = (new ConnectionField)->pageInfoResolver($data,$this->args,$this->context,$this->info);
$page = $data->currentPage();
$edges = $data->values()->map(function ($item, $x) use ($page) {
$cursor = ($x + 1) * $page;
$encodedCursor = $this->encodeGlobalId('Car', $cursor);
$globalId = $this->encodeGlobalId('Car', $item->getKey());
$item->_id = $globalId;
return ['cursor' => $encodedCursor, 'node' => $item];
});
return [
'pageInfo' => $pageInfo,
'edges' => $edges,
];
}
Swap to get the Paginator style.
->paginatorConnection($this->args);
$pageInfo = (new PaginatorField)->paginatorInfoResolver($data,$this->args,$this->context,$this->info);
I believe this is something can go on a directive type for queries? where I'm only responsible to start with the initial query,
$data = Car::orderBy('id', 'DESC')
->active('publicado')
->notActive('vendido')
or a Model::query()
if I got no arguments.
full relay query, (Im working now on the react-native this is why it comes with fragments, full day to build a root query with a QueryRenderer + createPaginationContainer, no docs around)
query carsQuery($count: Int!, $cursor: ID) {
...ItemList
}
fragment ItemList on Query {
cars(first: $count, after: $cursor) {
edges {
cursor
node {
id
...Item_car
__typename
}
}
pageInfo {
hasNextPage
lastPage
}
}
}
fragment Item_car on Car {
id
title
text
updated_at
media {
img_small
id
}
}
params
{"count":3,"cursor":null}
``
Hey everyone,
I was implementing relay modern paginated container when I came across and pagination bug or a misconfiguration from my side.
fragment Tournaments_diceviewer on Dice { competitions(after: null, orderby: "id", hasLeagueTableOnly: true) { pageInfo { count total currentPage hasNextPage endCursor hasPreviousPage startCursor } edges { cursor node { id name short_name full_name image order __typename } } } }
The above fragment queries the connections and returns the first 15 nodes with now problem.
The endCursor returned in the first 2 pages is correct. Then on page 3, the endCursor returned is for the last record in the collection.
I had a look at the PageInfoType definition and came across this:
'endCursor' => [ 'type' => Type::string(), 'description' => 'When paginating forwards, the cursor to continue.', 'resolve' => function ($collection) { if ($collection instanceof LengthAwarePaginator) { return $this->encodeGlobalId( 'arrayconnection', $collection->lastItem() * $collection->currentPage() ); } }, ],
I don't understand line 71 or $collection->lastItem() * $collection->currentPage()
What I did was I removed the "* $collection->currentPage()" and everything works great
As for my connection definition:
public function resolve($parent, array $args, $context, ResolveInfo $info) { return $parent->competitions()->getConnection($args); }
competitions() returns a simple eloquent hasMany
Please advise
Thanks
Support for Apollo standardized cache control extension (https://github.com/apollographql/apollo-cache-control). Probably super easy to implement using directives.
Take the following schema
type Query {
me: User @auth
users(
name: String @where(operator: "like")
id: ID @eq
): [User!]! @paginate(model: "App\\User")
}
I'd expect to be able to query
{
users(name: "Dr%") {
id
name
}
}
To be able to get all the users whose names start with "Dr".
Unfortunately due to id
not being passed the query gets `AND "id" IS NULL" appended, Which obviously isn't what we want! We need to be able to query via just name or just id
Hey, thanks dir the work on this. Would you recommend to use your Old package or do you think this One coukd be ready soon?
I don't mean by any means to Stress, but Just out of interest.
Starting a new Project in about a month and I am thinking about using relay.
This is exactly the kind of library that should be used on a small framework. All you really need is some routing and GraphQL magic.
With that said, is there any particular reason you are using Laravel only features like the router?
If something has already registered a function called schema()
ours won't be registered so we need to know the alternative syntax.
GraphQL::schema()->group(['namespace' => 'App\\Http\\GraphQL', 'middleware' => ['auth']], function () {
GraphQL::schema()->group(['namespace' => 'Types'], function () {
GraphQL::schema()->type('user', 'UserType');
GraphQL::schema()->type('viewer', 'ViewerType');
GraphQL::schema()->type('task', 'TaskType');
});
GraphQL::schema()->group(['namespace' => 'Queries'], function () {
GraphQL::schema()->query('viewer', 'ViewerQuery');
});
GraphQL::schema()->group(['namespace' => 'Mutations'], function () {
GraphQL::schema()->mutation('createUser', 'CreateUserMutation');
});
});
Possibly also need something in there about this format instead of the namespaces as well since IDEs can understand what we're on about in this form
GraphQL::schema()->group(['middleware' => ['auth']], function () {
//Types
GraphQL::schema()->type('user', \App\GraphQL\Types\UserType::class);
GraphQL::schema()->type('viewer', \App\GraphQL\Types\ViewerType::class);
GraphQL::schema()->type('task', \App\GraphQL\Types\TaskType::class);
//Queries
GraphQL::schema()->query('viewer', \App\GraphQL\Queries\ViewerQuery::class);
//Mutations
GraphQL::schema()->mutation('createUser', \App\GraphQL\Mutations\CreateUserMutation::class);
});
Or if you prefer the short syntax this works too
use App\GraphQL\Mutations\CreateUserMutation;
use App\GraphQL\Queries\ViewerQuery;
use App\GraphQL\Types\TaskType;
use App\GraphQL\Types\UserType;
use App\GraphQL\Types\ViewerType;
GraphQL::schema()->group(['middleware' => ['auth']], function () {
//Types
GraphQL::schema()->type('user', UserType::class);
GraphQL::schema()->type('viewer', ViewerType::class);
GraphQL::schema()->type('task', TaskType::class);
//Queries
GraphQL::schema()->query('viewer', ViewerQuery::class);
//Mutations
GraphQL::schema()->mutation('createUser', CreateUserMutation::class);
});
So first off lighthouse v2 is awesome!!!! great work! It's so much more simple than v1. I'm currently working on upgrading a project to using lighthouse v2 and I'm getting stuck updating to Relay Modern, because it requires either the schema.graphql or schema.json file to compile the static files from, but it does not recognize the fields when you do things like this someFeild: [Type!]! @paginate(type:"relay" model: "SomeModel")
because it does not know that translates to someFeild(first: Int! after: String):
So am I missing something obvious? Or is this something that's not currently supported?
Helo @chrissm79,
Im wondering how can I filter/order results on a pagination directive.
Like filter results with a query or exclude items by a field.
right now if I try to mix a pagination with a field gives me following waring:
Fields can only have 1 assigned resolver directive. cars has 2 resolver directives [paginate, field]
Am I missing something?
The scope registered at
nuwave/lighthouse:/src/Support/Traits/RelayConnection.php#L18
conflicts with a built in laravel method at
laravel/framework:/src/Illuminate/Database/Eloquent/Model.php#L1005
so it looks like we need to change the scope name;
Might I suggest we go with scopeGetGraphQLConnection()
.
For consistency we should also rename scopeLoadConnection()
to scopeLoadGraphQLConnection()
.
These names are fairly precise but should avoid conflicts in the future as anything to do with connection
seems to be quite dangerous grounds in Laravel.
Thoughts?
[Symfony\Component\Debug\Exception\FatalThrowableError]
Type error: Too few arguments to function App\User::scopeGetConnection(), 1 passed in /var
/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 930 and
exactly 2 expected
Note that the error is actually triggered from the factory(...)->create()
method, however these are normally only used in seeders.
Hey @chrissm79,
Any chance you could provide an example of how to query top level collections?
I.e. We've got examples in the docs of how to get all jobs a user is connected to, but how can we write a query that returns a collection that is not a connection
e.g. assuming we have a user type defined simply returning all users in the system, or we have a job type and returning all jobs in the system that have their status set to "open".
Cheers,
Thanks for supplying query to args directives, really helps alot!
So I am creating an Arg directive called @search
for adding support for Laravel scout (fulltext search).
Is this something you would want in your package or should I make an extension for it?
I'll fill this section out when time permits but I'm currently working on v2.0 of Lighthouse. Spoiler alert, it's a large breaking change that (at the moment) doesn't have an upgrade path. That being said, with the new approach (schema first), the benefits that come with these breaking changes along w/ how much more the end-user can do with it (i.e., plugins) make this a requirement. I want Lighthouse to be approachable to anyone, doesn't require anything but a schema to get started, and provides the user w/ enough flexibility to extend Lighthouse to fit just about any project regardless of complexity.
By defining the schema above, Lighthouse can:
User
and Task
typesUser
hasMany task relationshipTask
belongsTo User relationshipme
query that requires and returns the authenticated usercreateUser
mutation that will autofill and create a new User
w/ rule validation on the email
argumentcreateTask
mutation that will inject the authenticated user's id
into the user_id
argument (currently trying to figure out how to remove this argument from the schema the client sees). Mutation requires that the user can
create tasks via a policyThat's a lot it can do for you w/ only 31 lines and no code!!!
graphql-php
built in Deferred
class to handle relationships and prevent N+1For those who want to check out the example project that the Walkthrough will be based off of, here it is! I'm going to try and work in as many built-in directives as possible to highlight what Lighthouse can do "out of the box" as I prep for the v2.0 release.
@model
directive)Right now if we're doing a query for multiple models we have the fantastic @paginate
directive.
Unfortunately I cannot find anything similar for a single model.
Do we need to add a new @find(model: "App\\User")
directive to allow the following
type Query {
users(name: String @where(operator: "like"), id: ID @in): [User!]! @paginate(model: "App\\User")
user(id: ID @eq, email: String @eq): User @find(model: "App\\User")
}
Hi,
I went over Walkthrough section in the documentation and couldn't continue after the subsection 6.3.5 (Connections) because critical information is missing about adding jobs field to the UserType... I found out about this after watching youtube tutorial....
https://chrissm79.gitbooks.io/lighthouse/content/635-adding-a-connection.html
Hey,
I have started building my first project with Lighthouse.
It's amazingly fast way to building apps. Still, I'm running into an issue with retrieving a string Date representation. I have inserted a new enrty into the database through a mutation.
This is the error that gets returned:
"String cannot represent non scalar value: instance of Illuminate\\Support\\Carbon"
Here is the schema file I'm using:
# schema.graphql
type Project {
id: ID!
parent_id: Parent @belongsTo
name: String!
created_at: String
updated_at: String
deleted_at: String
}
type Query {
projects: [Project!]! @field(resolver: "App\\Http\\GraphQL\\ProjectQueries@index")
}
As can be seen from artisan tinker
, the date is present on the data instance:
And the screenshot from GraphQL Playground:
Am I doing it wrong or is that an issue that needs additional work on your end?
Thank you again for this package & the work you're doing.
I'm making a little test app where i have this schema
type NewsArticle {
id: ID
headline: String
slug: String
article: String
author: User @belongsTo
comments: [NewsArticleComment!] @hasMany
}
type NewsArticleComment {
id: ID
comment: String
author: User @belongsTo
news_article: NewsArticle @belongsTo
}
type User {
id: ID
username: String
newsArticles: [NewsArticle!] @hasMany @rename(attribute: "news_articles")
}
type Query {
users: [User!]! @field(resolver: "App\\Http\\GraphQL\\Query@users")
news_articles: [NewsArticle!]! @field(resolver: "App\\Http\\GraphQL\\Query@newsArticles")
}
The problem i'm having is the "newsArticle" field on the User type. I want to rename and tell that it is a hasMany relationship. Is there any way to do this?
Hi!
Great work on Lighthouse so far! I've found a few things that I would like to submit PR's for.
I tried to clone the repository and running composer install
to get setup but got greeted with a bunch of errors (Wrong phpunit version, ModelFactory not working, redeclaring of classes).
I'm probably doing something wrong.
Do I need a Laravel installation to contribute? Or should the tests work without it?
Thank you!
I think we need to revisit the pagination and connections as a whole in lighthouse, there's been a few changes in Laravel 5.4 that makes things easier, such as the Relation becoming macroable so we're able to add the toConnection()
macro to that as well.
Additionally there is now a new method on Eloquent - forPageAfterId
which should work well as it's effectively cursor pagination in a nutshell.
Now the current cursor generation doesn't seem right to me, I've found things like if I ask for the first 1, and keep incrementing the cursor then I get items 1, 2, 3, 5 and it goes out of whack from there (note this is contiguous data, IDs 1-200 are there).
This seems to come from a disconnect between how we generate the cursor for pagination e.g. here https://github.com/nuwave/lighthouse/blob/master/src/Support/Definition/PageInfoType.php#L73
And how we decode the cursor e.g. here https://github.com/nuwave/lighthouse/blob/master/src/Support/Traits/GlobalIdTrait.php, which seem to expect just type:id causing the effect above.
I'm proposing that we standardize the cursors to be graphqlType:cursorColumn:cursorColumnValue
So the 5th item from a graphql type if user
would look like user:id:5
This means that to get the items we can just do e.g.
function resolve($args){
return User::orderBy('created_at', 'desc')->toConnection($args);
//or if we didn't want to alter the query...
return User::getQuery()->toConnection($args);
}
//this is the macro definition
function toConnection($args){
list($type, $column, $value) = $this->decodeCursor($args);
$first = isset($args['first']) ? $args['first'] : 15;
$items = $this->forPageAfterId($first, $column, $value)->get();
$total = $this->count();
return new LengthAwarePaginator($items, $total, $first);
}
Thoughts?
Could be nice if we changed graphql()->execute($query)
to return a Response object instead and setting the original property on the response to the original model.
By doing this we would have the opportunity for making tests which aren't dependent on our data structure, but instead do something like this
$user = factory(User::class)->create();
$this->be($user);
$query = "{me{email}}";
$response = graphql()->execute($query);
$this->assertTrue($user->is($response->getOriginalContent()));
This test will check if the endpoint me has gotten the correct user model, but it totally ignores what data is actually selected as we don't care about this in this test, just that it is the correct model.
This issue exists as a bit of a dumping ground of "How would I do this" questions that we could potentially answer and add to the docs in a specific section.
As any answers are posted the original asker should add them to their question to keep things easy to read. I've provided a few below to give us some structure.
Be cool if we could have a messages
array that can go with rules
on mutation args so we can define what we want the message to be for that validation.
Thank you for the amazing package :)
I have an issue with the edge command, not sure if its related to my env.
php artisan lighthouse:edge Competition
The command runs out of memory (1024 MB)
PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 45056 bytes) in /Users/sherief/Development/git/dice/backend/vendor/laravel/framework/src/Illuminate/Console/GeneratorCommand.php on line 92
PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 32768 bytes) in /Users/sherief/Development/git/dice/backend/vendor/symfony/debug/Exception/FatalErrorException.php on line 1
Any one facing the same issue?
Thanks
Was thinking if we should add a directive for soft deleted models?
So we have an arg directive called @trashes
or @softdeletes
or something better than that, this arg directive would work in the following way.
type Query{
companies(trashed: Trash @trashes): [Company!]! @paginate(model: "Company", type: "relay")
}
enum Trash {
ONLY @enum(value: "only")
WITH @enum(value: "with")
WITHOUT @enum(value: "without")
}
{
companies(first: 10, trashed: ONLY ) {
edges {
node {
id,
vat,
name
}
}
}
}
Of course we would store the Trash enum, so users don't have to define that themself.
It would be amazing if we could add support for the Apollo tracing extension (https://github.com/apollographql/apollo-tracing).
I can't figure out how we can set a field to be filterable.
For example i have some users and I would like to make the name something i can filter by.
Currently, this is just a placeholder for discussions. But as a quick progress report, I have gotten started on subscriptions this weekend and it's coming along quite nicely! Right now I have 2 variations in mind:
Websockets (lighthouse-subscriptions-ws
) - The main goal of this package is to keep everything in PHP land. The WS server will be built with Ratchet. I've tested some things out and I'm able to get Playground to connect/disconnect to a Ratchet server successfully (using the same methods as subscriptions-transport-ws). The next step is to start pushing down messages from the server. Since a subscriber can subscribe to multiple subscriptions (lol), I need storage. So it will launch w/ InMemory and Redis storage out of the box, but it can also be extended (i.e., MySQL storage). Because of how far along I am, this will likely be the first package released.
Pusher (lighthouse-subscriptions-pusher
) - Maintaining an websocket server for a massive project doesn't sound fun to me... so I want to include at least one third-party solution. graphql-ruby has a great solution for this, however, it does rely on a custom Link for Apollo to work with it. Relay Modern is mentioned too, but I haven't dived into the JS parts of that yet.
The coolest thing (I think) is the fact that I can create these packages completely separate from the main lighthouse
repo by leveraging directives! So hopefully, the community will be able to create some great alternatives if you use a different 3rd party WS provider or if you have a solution that relies on something other than Ratchet (i.e., Node).
I currently don't have a timeline for this, it just being done after work hours and on the weekends but this weekend was pretty productive so I'm really hoping it won't be too long before I can get this in everyone's hands!
Initial Beta Release of lighthouse-ws
I'll make this one quick!
Cheers
On our objects (Types/Mutations/Queries) we have a name key in the $attributes
.
I'm proposing that if we call GraphQL::schema()->type($namespace)
(or any of the other related functions) then we instantiate an instance of the passed class name and use $instance->name
as the name.
Likewise with my open pull request it would be e.g.
GraphQL::schema()->types([
\App\GraphQL\Types\UserType::class,
\App\GraphQL\Types\ViewerType::class,
\App\GraphQL\Types\TaskType::class
]);
I've opened this as an issue rather than a pull request as I have not yet delved too deep into how your group()
function works and what may need to be changed to make it work with that if anything.
Thoughts?
This is more a question than a issue.
I have the following mutation. I want to return the edge from the server to added it to the connection, but i don't know how to returned.
Thanks.
CreateComment.js
class CreateComment extends Relay.Mutation {
getConfigs() {
return [{
connectionName: 'replies',
edgeName: 'commentEdge',
parentID: this.props.replyToComment.id,
parentName: 'game',
rangeBehaviors: {
'': 'append',
},
type: 'RANGE_ADD',
}];
}
getFatQuery() {
return Relay.QL`
fragment on CreateCommentPayload {
commentEdge
}
`;
}
getMutation() {
return Relay.QL`mutation {createComment}`;
}
getVariables() {
return {
body: this.props.body,
parentCommentId: this.props.replyToComment.id,
subjectId: this.props.subject.id,
subjectType: this.props.subject.type,
};
}
}
CreateCommentMutation.php
use GraphQL;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Hub\Game;
use Nuwave\Lighthouse\Support\Definition\GraphQLMutation;
use Nuwave\Lighthouse\Support\Interfaces\RelayMutation;
class CreateCommentMutation extends GraphQLMutation implements RelayMutation
{
/**
* Attributes of mutation.
*
* @var array
*/
protected $attributes = [
'description' => '',
'name' => 'createComment',
];
/**
* Available arguments on mutation.
*
* @return array
*/
public function args()
{
return [
'body' => [
'name' => 'body',
'type' => Type::nonNull(Type::string()),
'rules' => ['required'],
],
'parentCommentId' => [
'name' => 'parentCommentId',
'type' => Type::ID(),
],
'subjectId' => [
'name' => 'subjectId',
'type' => Type::nonNull(Type::ID()),
'rules' => ['required'],
],
'subjectType' => [
'name' => 'subjectType',
'type' => Type::nonNull(Type::string()),
'rules' => ['required'],
],
];
}
/**
* List of output fields.
*
* @return array
*/
public function outputFields()
{
return [
'commentEdge' => [
'type' => function () {
return GraphQL::edge('comment');
},
'resolve' => function ($payload) {
return $payload;
},
],
];
}
/**
* Resolve the mutation.
*
* @param array $args
* @param mixed $context
* @param ResolveInfo $info
* @return mixed
*/
public function mutateAndGetPayload(array $args, $context, ResolveInfo $info)
{
if ($args['subjectType'] == 'Game') {
return $this->createGameComment($args);
}
}
private function createGameComment($args)
{
$subjectId = $this->decodeRelayId($args['subjectId']);
$parentCommentId = $args['parentCommentId'] ? $this->decodeRelayId($args['parentCommentId']) : null;
return Game::findOrFail($subjectId)->comments()->create([
'body' => $args['body'],
'parent_id' => $parentCommentId,
'provider_id' => auth('providers')->id(),
'user_id' => auth('users')->id(),
]);
}
}
Hi there,
Anyone using with relay connection?
Just discovered that I got a jump from offset 10, to offset 40, having the pagination on $first:10
.
its so rare...
This isn't the worst, what I'm getting more confuse its with the fact that to be able to query relay connections I must change type to connection
, if I change it to type:"relay"
I must use count
instead of first
whats not the natural way to query relay connections.
Have downloaded the https://github.com/nuwave/lighthouse-example and seems to get same behaviour.
composer its using version 2.0.3, have even cleared composer cache, fresh install of packages, and not going forward.
@chrissm79 I need your help on this: have done following:
jobs: [Job!]! @paginate(type: "connection", model: "App\\Job")
jobs: [Job!]! @paginate(type: "relay", model: "App\\Job")
query QueryJobs{
jobs(first:3){
edges{
node{
title
}
}
}
}
While asking one of my "How do I..." questions - specifically this one I realised this likely warrants some external discussion.
Right now I'm thinking that the ideal way to do this if we have some additional meta data is going to be manually setting up a link model e.g. UserCompanyLink
and then rather than using belongsToMany
use hasMany
/belongsTo
relations so a query would then look like
{
users {
data {
id
name
companies {
id #this is the relationship id
role #this is one of the "pivot" fields
company {
id #this is the company id
name
}
}
}
}
}
For mutations this gives you a lot of options, You could either do a mutation with the input like
{
"id": 5,
"name": "Donald Trump",
"companies": [
{"id": 8, "role": "owner"},
{"id": 12, "role": "employee"}
]
}
And then assume that if the companies
key is present it always represents the full list of companies the user is linked to.
The issue with this method is that a) it requires a custom resolver, b) you need to always pass across the full list of companies. The good thing is you can manage the relationships in bulk.
Alternatively you could treat the UserCompanyLink
as a full entity and add createUserCompanyLink
, deleteUserCompanyLink
, and updateUserCompanyLink
mutations.
This second method is likely what I'll go for, although I feel there might be a better way than skipping the belongsToMany
relations in laravel and also it becomes a bit of a message in relay as your query looks like
{
users {
edges {
cursor
node {
id
name
companies {
edges {
cursor
node {
id #this is the relationship id
role #this is one of the "pivot" fields
company {
id #this is the company id
name
}
}
}
}
}
}
}
}
When really the ideal output in my mind would be this
(note I haven't actually thought this through from the caching side of frontend clients)
{
users {
edges {
cursor
node {
id
name
companies {
edges {
cursor
role #this is one of the "pivot" fields
id #this is the relationship id
node {
id #this is the company id
name
}
}
}
}
}
}
}
What's our thoughts around this?
webonyx/graphql-php has support for query complexity analysis. It would be helpful if there was a @complexity
directive that allowed you to set a individual field, and for the paginators to set the complexity to a function that returns the number of results its going to return multiplied by the child complexity, as shown in the example at https://webonyx.github.io/graphql-php/security/
@chrissm79 , seems that if you just use the composer require nuwave/lighthouse
the versions there are not properly up to date with master branch.
by default you will get today the 2.0.3 and this version as you can see comes with this fix we had several commits ago.
See picture? should be 'relay' instead of 'connection', and this is whats causing this strange behaviour on production builds.
A workaround will be to use the composer require nuwave/lighthouse:dev-master
or master branch for now, I was wondering how you was doing the releases when no merges are carried, as you see in sourcetree graphical the branch never get merged.
I didn't notice till today because I was using dev-master
.
Hope it helps-
When nesting connections I'm experiencing a problem with the PageInfo field.
When i try to execute this query:
query{
users(first: 1){
pageInfo{
hasNextPage
hasPreviousPage
total
endCursor
}
edges{
node{
id
username
news_articles(first: 2){
pageInfo{
hasNextPage
hasPreviousPage
total
}
edges{
node{
id
headline
}
}
}
}
}
}
}
I get this response
{
"data": {
"users": {
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": false,
"total": 10,
"endCursor": "YXJyYXljb25uZWN0aW9uOjE="
},
"edges": [
{
"node": {
"id": "1",
"username": "tspinka",
"news_articles": {
"pageInfo": {
"hasNextPage": false,
"hasPreviousPage": false,
"total": null
},
"edges": [
{
"node": {
"id": "1",
"headline": "Dignissimos voluptas eum facilis quas."
}
},
{
"node": {
"id": "2",
"headline": "Et architecto voluptatem vitae dolorem dolor."
}
}
]
}
}
}
]
}
}
}
The first level is working but the second isn't.
Here is my schema file
type NewsArticle {
id: ID
headline: String
slug: String
article: String
author: User @belongsTo
comments: [NewsArticleComment!]! @hasMany(type: "relay")
}
type NewsArticleComment {
id: ID
comment: String
author: User @belongsTo
news_article: NewsArticle @belongsTo
}
type User {
id: ID
username: String
email: String
runescape_name: String
member: Boolean
news_articles: [NewsArticle!]! @hasMany(type: "connection", relation: "newsArticles")
}
type Query {
users: [User!]! @paginate(type: "relay", model: "\\App\\User")
news_articles: [NewsArticle!]! @paginate(type:"relay", model: "\\App\\NewsArticle")
}
Hi there,
How we getting on this project, seems its been left aside from here.
Looking for Relay, as the Folkloreatelier/laravel-graphql seems very active and I have been using it, Relay does not seem to be their focus.
I know that some of you are moving to Typescript and the lovely async functions, probably looking for the subscriptions, but ill like to continue using Laravel.
Any comments will be gratefully appreciated.
This is an opinionated issue, but I would like to test the waters.
I really like the Service Container and the dependency injection Laravel offers. Lighthouse currently resolves classes that inherits from either GraphQLField
and GraphQLType
via the service container, but both of these classes extends Illuminate\Support\Fluent
, which in turn takes the argument $attributes in the constructor.
I've gone through the code, and can't really see why we are extending Fluent in the above mentioned classes. There's a few others that need it, but not those two specifically. If we remove the parent, we don't have to call the parent constructor if we need to use the dependency injection (we don't actually have to call the parent-constructor as it is right now, but my code inspector complains if I leave it out).
class GameType {
protected $gameRepository;
public function __construct(GameRepositoryInterface $gameRepository) {
$this->gameRepository = $gameRepository;
}
...
Instead of
class GameType {
protected $gameRepository;
public function __construct(GameRepositoryInterface $gameRepository) {
parent::__construct();
$this->gameRepository = $gameRepository;
}
...
Thoughts? I'd be happy to provide a PR doing this.
Hey guys, I love that lucasmichot and chris are doing so much work on the package right now.
Thanks guys.
Since Relay Modern is coming out soon, I wanted to ask if you plan on making it work with it or if it maybe works with it already?
I am going to start a new project and I'm not sure if I should start it with laravel because I would love to start with Relay modern right away.
What do you guys think about the roadmap for the next couple of months?
Could be nice with an interface to define custom rendering of exceptions just like Laravels Illuminate\Contracts\Support\Responsable
interface.
Hey @chrissm79,
I'm curious about your thoughts on file uploads?
Right now I'm quite happy handling the uploads through a separate controller that simply accepts the file, marks it as transient and returns an ID.
Then for a mutation we simply send across a list of these transient file ids, and the mutation connects the file to the resource and marks it as a completed file, any files that are not marked as completed get deleted after 30 minutes.
For me this has the advantage of being able to use pretty much any standard file uploader on the frontend.
But I know there are some who like to keep everything within graphql, there's been some discussion going on at folkloreinc/laravel-graphql#125 regarding this.
What are your thoughts here?
Hi all,
I'm trying to get relations working following the video tutorial (and any other info I can find), but I keep getting the following error: Call to undefined function Nuwave\Lighthouse\resolve()
I have the following configuration in place (simple Room -> Comments relation):
App\GraphQL\Schemas\pms.graphql
type Room {
id: ID! @globalId
name: String
available: String
reference: String
details: String
location: String
comments: [Comment!]! @hasMany
}
type Comment {
id: ID! @globalId
reference: String
title: String
content: String
created_at: DateTime
room: Room! @belongsTo
}
App\GraphQL\Queries\Rooms.php
<?php
namespace App\GraphQL\Queries;
use Nuwave\Lighthouse\Support\Schema\GraphQLResolver;
use Illuminate\Auth\Access\AuthorizationException;
use App\Models\Room\Room;
class Rooms extends GraphQLResolver
{
public function resolve()
{
if(!$this->context || !$this->context->user){
throw new AuthorizationException('Unauthorized');
return null;
}
// query
$roomsQuery = Room::query();
return $roomsQuery
->where('reference', 'NOT LIKE', '%9999')
->orderBy('lft')
->get()
;
}
}
App\GraphQL\schema.graphql
#import ./Schemas/pms.graphql
scalar DateTime @scalar(class: "DateTime")
type ResponsesPayload {
success: Boolean!
msg: String
payload: String
}
type Query @group(middleware: ["auth:api", "scopes:graphql"]) {
rooms: [Room!]!
}
As soon as I try the following query, I get the forementioned error
query getRooms {
rooms {
id
name
available
comments {
reference
title
content
}
}
}
I'm probably overlooking something obvious here..
I still have some other questions related to this:
type Comment @model(model: "App\\Models\\Room\\Comment") { ... }
Query
entries when I try to separate them into different files@model
directiveGraphQLResolver
or GraphQLQuery
Thanks again for this great library, I hope I can get rolling with it very soon!
Kind regards,
Erik
Hey @chrissm79 ,
Any chance you could delete the old branches for V1, and add a quick note to the readme regarding the other branches.
E.g. what's the difference between
master
v2-beta
v2.0.x
Cheers!
So this is not really an issue, but I think it's worth discussing and I want it documented so that other people might find a use for it.
The background to this discussion is this issue. The current version of Relay (0.10) doesn't support root fields/queries to be arrays. In the linked issue above they suggest a workaround until Relay 2 is released. An updated version of the workaround has been suggested in this issue: lucasbento/graphql-pokemon#1
I don't think we should build support for this, since Relay 2 is coming, but the easiest way to add this to a project would be to extend GraphQL - since it's registered through Laravel's service container, and override the buildSchema()-method:
Nuwave\Lighthouse\GraphQL
public function buildSchema() {
...
$fields = $queryType->config['fields'];
$fields['query'] = [
'args' => [],
'type' => $queryType,
'resolve' => function() {
return true; // we need to have a closure that returns something
}
];
$queryType->config['fields'] = $fields;
...
return new Schema([
'query' => $queryType,
'mutation' => $mutationType,
'types' => $this->typesWithInterfaces->all(),
]);
This is a hack(!) and probably shouldn't be used in production.
One could get away with doing something similar in their extension if they first call the parent on the overridden method (buildSchema()) and then get the mutations, queries and types from the generated Schema, and then instantiate a new Schema.
This is not recommended, since this package will most likely change.
If anyone has any suggestions on how (and why) support for this should be implemented for real, please, leave a comment!
Love it!, so simple!
Got to the point where I'm getting the schema generated into a React-Native project using Relay Modern, retrieving the schema form the server with the graphql get-schema -I
introspection method, hardest part was figure out the mutation. (working example bellow)
Need a bit of advise how you handle or its up to us the auth, resolve, middleware, thing. Like for example: @inject(context: "auth.id"))
Add the route point to the crsss thing exclude? I mean, to resolve the context, maybe using passport, if you can share your approach I'm sure it will be very inspirational. ๐ค [@]middleware() directive probably?
For a custom Directive, like example bellow, Is it class name or the name attributed whats important?
<?php
namespace App\GraphQL\Directives;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
class Context implements FieldResolver
{
use HandlesDirectives;
/**
* Name of the directive.
*
* @return string
*/
public function name()
{
return 'context';
}
/**
* Resolve the field directive.
*
* @param FieldValue $value
*
* @return FieldValue
*/
public function handle(FieldValue $value)
{
return auth()->id();
}
}
type User @model {
id: ID
name: String!
email: String!
created_at: String!
updated_at: String!
avisos: [Aviso!]! @hasMany
}
type Aviso @model {
id: ID
uuid: String!
name: String!
user: User!
}
type Query {
viewer: User @auth
}
type Mutation {
createUser(
name: String!
email: String! @validate(rules: ["email", "unique:users,email"])
): User @create(model: "App\\Models\\User")
createAviso(name: String, user_id: Int @inject(context: "auth.id")): Aviso
@create(model: "App\\Models\\Aviso")
}
This is how I'm doing the middleware,
'directives' => [__DIR__.'/../app/GraphQL/Directives'],
'route' => [
'prefix' => 'kapi',
// 'middleware' => ['loghttp']
],
/*
|--------------------------------------------------------------------------
| Namespace registry
|--------------------------------------------------------------------------
|
| This package provides a set of commands to make it easy for you to
| create new parts in your GraphQL schema. Change these values to
| match the namespaces you'd like each piece to be created in.
|
*/
'namespaces' => [
'models' => 'App\Models',
'mutations' => 'App\GraphQL\Mutations',
'queries' => 'App\GraphQL\Queries',
'types' => 'App\GraphQL\Types',
'fields' => 'App\GraphQL\Fields',
'scalars' => 'App\GraphQL\Scalars',
'connections' => 'App\GraphQL\Connections',
'dataloaders' => 'App\\GraphQL\\DataLoaders',
],
best solution so far for Laravel from a personal perspective.
Happy coding!
Hello,
I was following the walkthrough for lighthouse on youtube, but I am running onto SQLSTATE error when I want to use relationships.
This is my schema.graphql
file:
enum Type {
ADMIN @enum(value: "admin")
CUSTOMER @enum(value: "customer")
}
type User {
id: ID!
name: String!
email: String!
type: Type!
created_at: String!
updated_at: String
}
type Customer {
id: ID!
email: String!
phone: String
user: User @belongsTo
}
type Query {
customers: [Customer!]! @field(resolver: "App\\Http\\GraphQL\\Query@customers")
}
When I call this query:
{
customers {
id
user {
name
}
}
}
The return values are:
{
"data": {
"customers": [
{
"id": "1",
"user": null
}
]
},
"errors": [
{
"message": "SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near \".\"\nLINE 1: SELECT `users`.* FROM ((select * from \"users\" where \"users\"....\n ^ (SQL: SELECT `users`.* FROM ((select * from \"users\" where \"users\".\"id\" in (1))) AS `users`)",
"locations": [
{
"line": 4,
"column": 5
}
]
}
]
}
And laravel.log
contains the following info:
[2018-04-24 02:54:08] local.INFO: GraphQL Error: {"code":0,"message":"SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near \".\"
LINE 1: SELECT `users`.* FROM ((select * from \"users\" where \"users\"....
^ (SQL: SELECT `users`.* FROM ((select * from \"users\" where \"users\".\"id\" in (1))) AS `users`)","trace":"#0 /home/vagrant/code/kalsha/vendor/webonyx/graphql-php/src/Executor/Executor.php(850): GraphQL\\Error\\Error::createLocatedError(Object(Illuminate\\Database\\QueryException), Object(ArrayObject), Array)
#1 /home/vagrant/code/kalsha/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromise.php(144): GraphQL\\Executor\\Executor->GraphQL\\Executor\\{closure}(Object(Illuminate\\Database\\QueryException))
#2 /home/vagrant/code/kalsha/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromise.php(35): GraphQL\\Executor\\Promise\\Adapter\\SyncPromise->GraphQL\\Executor\\Promise\\Adapter\\{closure}()
#3 /home/vagrant/code/kalsha/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromiseAdapter.php(140): GraphQL\\Executor\\Promise\\Adapter\\SyncPromise::runQueue()
#4 /home/vagrant/code/kalsha/vendor/webonyx/graphql-php/src/GraphQL.php(236): GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter->wait(Object(GraphQL\\Executor\\Promise\\Promise))
#5 /home/vagrant/code/kalsha/vendor/nuwave/lighthouse/src/GraphQL.php(122): GraphQL\\GraphQL::executeAndReturnResult(Object(GraphQL\\Type\\Schema), '{\
customers {...', NULL, Object(Nuwave\\Lighthouse\\Schema\\Context), NULL)
#6 /home/vagrant/code/kalsha/vendor/nuwave/lighthouse/src/GraphQL.php(81): Nuwave\\Lighthouse\\GraphQL->queryAndReturnResult('{\
customers {...', Object(Nuwave\\Lighthouse\\Schema\\Context), NULL, NULL)
#7 /home/vagrant/code/kalsha/vendor/nuwave/lighthouse/src/Support/Http/Controllers/GraphQLController.php(44): Nuwave\\Lighthouse\\GraphQL->execute('{\
customers {...', Object(Nuwave\\Lighthouse\\Schema\\Context), NULL)
#8 [internal function]: Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController->query(Object(Illuminate\\Http\\Request))
#9 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array(Array, Array)
#10 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('query', Array)
#11 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Route.php(212): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController), 'query')
#12 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Route.php(169): Illuminate\\Routing\\Route->runController()
#13 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Router.php(659): Illuminate\\Routing\\Route->run()
#14 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#15 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#16 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Router.php(661): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#17 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Router.php(636): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#18 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Router.php(602): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#19 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Router.php(591): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#20 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#21 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#22 /home/vagrant/code/kalsha/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#23 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Fideloper\\Proxy\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#24 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#25 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(30): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#26 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#27 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#28 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(30): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#29 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#30 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#31 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#32 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#33 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#34 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php(46): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#35 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(149): Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#36 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#37 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#38 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#39 /home/vagrant/code/kalsha/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#40 /home/vagrant/code/kalsha/public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#41 {main}"}
I am using PostgreSQL and running PHP 7.2.
How to get the relationships to work? I can't figure it out. Thanks for any help
If I create a new connection and providing a FQC, It would be awesome if the ConnectionRegistrar
checked if it was a class (class_exists) and tried to resolve it via the service container, before the instance is created.
public function fields() {
return [
'maps' => GraphQL::connection(MapConnection::class)->field()
];
}
It would make it possible for us to utilize the Dependency Injection in the Connection, instead of using the helper-function app
to pull in necessary services and such.
Again, I would be happy to provide a PR for this.
Okay first off im running a windows machine. I installed this library for graphiql-ui https://github.com/noh4ck/laravel-graphiql however when i got to the route localhost/graphiql-ui i get an error in the window where the queries should run. Am i doing something wrong? How would i configure lighthouse to work with that library?
Here is the error
Hello Maintainers,
We have been working on a new PHP implementation of the GraphQL specification and we are closing in on a version 1.0 release. I just wanted to stop by and mention this to see if you're interested in teaming up with us. There is a Slack for the project where we can invite you.
The repository can be found here:
https://github.com/digiaonline/graphql-php
Best,
Christoffer
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.