Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ with following configuration (`hasMany`) :
- "after" : cursor-based navigation
```

I case of relationships and if `StudioNet\GraphQL\Support\Type\Meta` type is
registered, you'll be granted to use field like `_posts_meta { count }` in order
to retrieve global count.

This transformer also converts [mutated fields from model](https://laravel.com/docs/5.4/eloquent-mutators).
Let's show you the convertion mapping (based on supported model cast) :

Expand Down Expand Up @@ -215,6 +219,17 @@ Generate singular query based on `EloquentObjectType`.
- "id" : id-based navigation
```

#### `StudioNet\GraphQL\Generator\Query\MetaEloquentGenerator`

Generate meta query based on `EloquentObjectType`.

```
- Type : "EloquentObjectType"
- Return : [
- "count" : count of objects
]
```

#### `StudioNet\GraphQL\Generator\Query\NodesEloquentGenerator`

Generate pluralized query based on `EloquentObjectType`.
Expand Down Expand Up @@ -455,3 +470,41 @@ query {
}
}
```

#### Using metadata

```php
# config/graphql.php

return [
'type' => [
\StudioNet\GraphQL\Support\Type\Meta::class,
\App\User::class,
\App\Post::class
]
];
```

```graphql
query {
_users_meta {
count
}

users (take: 2) {
id
first_name
last_name

posts (take: 5) {
id
title
content
}

_posts_meta {
count
}
}
}
```
11 changes: 9 additions & 2 deletions resources/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@
],

// Type configuration. You can append any data : a transformer will handle
// them (if exists)
'type' => [],
// them (if exists). Order matter
'type' => [
// Mandatory in order to make working meta data
\StudioNet\GraphQL\Support\Type\Meta::class

// Custom types
// \App\User::class
],

// Scalar field definitions
'scalar' => [
Expand Down Expand Up @@ -57,6 +63,7 @@
// all will be called
'generator' => [
'query' => [
\StudioNet\GraphQL\Generator\Query\MetaEloquentGenerator::class,
\StudioNet\GraphQL\Generator\Query\NodeEloquentGenerator::class,
\StudioNet\GraphQL\Generator\Query\NodesEloquentGenerator::class
],
Expand Down
7 changes: 7 additions & 0 deletions src/Generator/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ abstract class Generator implements GeneratorInterface {
public function __construct(Application $app) {
$this->app = $app;
}

/**
* {@inheritDoc}
*/
public function dependsOn() {
return [];
}
}
8 changes: 8 additions & 0 deletions src/Generator/GeneratorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ public function generate($instance);
* @return string
*/
public function getKey($instance);

/**
* Assert a specific type is registered before executing. If not, the
* generator will not be triggered
*
* @return array
*/
public function dependsOn();
}
70 changes: 70 additions & 0 deletions src/Generator/Query/MetaEloquentGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
namespace StudioNet\GraphQL\Generator\Query;

use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type as GraphQLType;
use Illuminate\Database\Eloquent\Model;
use StudioNet\GraphQL\Generator\Generator;
use StudioNet\GraphQL\Type\EloquentObjectType;

/**
* Generate meta query for given EloquentObjectType
*
* @see Generator
*/
class MetaEloquentGenerator extends Generator {
/**
* {@inheritDoc}
*/
public function supports($instance) {
return ($instance instanceof EloquentObjectType);
}

/**
* {@inheritDoc}
*/
public function getKey($instance) {
return sprintf('_%s_meta', strtolower(str_plural($instance->name)));
}

/**
* {@inheritDoc}
*/
public function dependsOn() {
return ['meta'];
}

/**
* {@inheritDoc}
*/
public function generate($instance) {
return [
'type' => $this->app['graphql']->type('meta'),
'resolve' => $this->getResolver($instance->getModel())
];
}

/**
* Return meta resolver
*
* @param Model $model
* @return callable
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function getResolver(Model $model) {
return function($root, array $args, $context, ResolveInfo $info) use ($model) {
// Clone collection in order to not erase existin collection
$collection = $model->newQuery();
$fields = $info->getFieldSelection(3);
$data = [];

foreach (array_keys($fields) as $key) {
switch ($key) {
case 'count' : $data['count'] = $collection->count(); break;
}
}

return $data;
};
}
}
16 changes: 15 additions & 1 deletion src/GraphQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,21 @@ public function registerGenerator($category, $generator) {
throw new Exception\GeneratorException('Unable to find given category');
}

$this->generators[$category][] = $this->app->make($generator);
$generator = $this->app->make($generator);
$dependencies = $generator->dependsOn();

// Assert generator has all is types dependencies
if (!empty($dependencies)) {
foreach ($dependencies as $dependency) {
$dependency = strtolower($dependency);

if (!array_key_exists($dependency, $this->types)) {
return;
}
}
}

$this->generators[$category][] = $generator;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ public function boot() {
// Call external methods to load defined schemas and others things
$this->registerScalars();
$this->registerTransformers();
$this->registerGenerators();
$this->registerSchemas();
$this->registerTypes();
$this->registerGenerators();
}

/**
Expand Down
35 changes: 35 additions & 0 deletions src/Support/Type/Meta.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
namespace StudioNet\GraphQL\Support\Type;

use StudioNet\GraphQL\Support\Type;
use GraphQL\Type\Definition\Type as GraphQLType;

/**
* Meta
*
* @see Type
*/
class Meta extends Type {
/**
* {@inheritDoc}
*/
public function getName() {
return 'Meta';
}

/**
* {@inheritDoc}
*/
public function getDescription() {
return 'Base-type metadata';
}

/**
* {@inheritDoc}
*/
public function getFields() {
return [
'count' => ['type' => GraphQLType::nonNull(GraphQLType::int())],
];
}
}
64 changes: 62 additions & 2 deletions src/Transformer/Type/ModelTransformer.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php
namespace StudioNet\GraphQL\Transformer\Type;

use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type as GraphQLType;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Application;
Expand Down Expand Up @@ -76,8 +78,6 @@ private function getDescription(Model $model) {
}

/**
* TODO
*
* Return corresponding fields. We're prefer using callable here because of
* recursive models. As this method handles relationships, we have to manage
* all depths cases
Expand Down Expand Up @@ -123,6 +123,18 @@ private function getFields(Model $model) {
// like `BelongsTo` doesn't handle arguments (we can't
// lookup throw a single entry, even with id)
if ($many) {
if ($this->hasMeta()) {
// Create a field named `_{column}_meta` in order to
// access info from it (like global count)
$fields[] = [
'name' => "_{$column}_meta",
'description' => "{$column} metadata",
'type' => $this->getMeta(),
'resolve' => $this->getMetaResolver($relation)
];
}

// Continue throw process
$type = GraphQLType::listOf($type);
$field = array_merge([
'args' => $this->getArguments(),
Expand All @@ -147,6 +159,28 @@ private function getFields(Model $model) {
};
}

/**
* Check if meta is global registered
*
* @return bool
*/
private function hasMeta() {
try {
return (bool) $this->app['graphql']->type('meta');
} catch (\Exception $e) {}

return false;
}

/**
* Return meta type
*
* @return ObjectType
*/
private function getMeta() {
return $this->app['graphql']->type('meta');
}

/**
* Resolve a relationship field
*
Expand Down Expand Up @@ -180,6 +214,32 @@ private function getResolver(array $relation) {
};
}

/**
* Resolve a meta field
*
* @param array $relation
* @return callable
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
private function getMetaResolver(array $relation) {
$method = $relation['field'];

return function($root, array $args, $context, ResolveInfo $info) use ($method) {
// Clone collection in order to not erase existin collection
$collection = clone $root->{$method}();
$fields = $info->getFieldSelection(3);
$data = [];

foreach (array_keys($fields) as $key) {
switch ($key) {
case 'count' : $data['count'] = $collection->count(); break;
}
}

return $data;
};
}

/**
* Return available arguments (many because there's no argument for single
* element). Order matter
Expand Down
6 changes: 3 additions & 3 deletions src/Transformer/TypeTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public function transform($instance) {
// Merge all attributes within attributes var
$attributes = array_merge($attributes, [
'fields' => $fields,
'name' => $this->getName(),
'description' => $this->getDescription()
'name' => $instance->getName(),
'description' => $instance->getDescription()
]);

if (!empty($interfaces)) {
Expand All @@ -64,7 +64,7 @@ private function getFieldResolver(TypeInterface $type, $name, array $field) {
return $field['resolve'];
}

$method = studly_case(sprintf('resolve-%s-%field', $name));
$method = studly_case(sprintf('resolve-%s-field', $name));

if (method_exists($type, $method)) {
return function() use ($type, $method) { return [$type, $method]; };
Expand Down