Skip to content

Commit

Permalink
include morph type key in returned model
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Nielsen committed Nov 4, 2019
1 parent c747b38 commit 3cf3c8d
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 13 deletions.
31 changes: 18 additions & 13 deletions src/Support/SelectFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
namespace Rebing\GraphQL\Support;

use Closure;
use RuntimeException;
use GraphQL\Error\InvariantViolation;
use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\WrappingType;
use Illuminate\Database\Query\Expression;
use GraphQL\Type\Definition\FieldDefinition;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type as GraphqlType;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Definition\WrappingType;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Query\Expression;
use RuntimeException;

class SelectFields
{
Expand All @@ -27,7 +27,7 @@ class SelectFields
/** @var array */
private $relations = [];

const FOREIGN_KEY = 'foreignKey';
const ALWAYS_RELATION_KEY = 'ALWAYS_RELATION_KEY';

/**
* @param ResolveInfo $info
Expand Down Expand Up @@ -142,7 +142,7 @@ protected static function handleFields(
}

// Always select foreign key
if ($field === self::FOREIGN_KEY) {
if ($field === self::ALWAYS_RELATION_KEY) {
self::addFieldToSelect($key, $select, $parentTable, false);
continue;
}
Expand Down Expand Up @@ -212,7 +212,11 @@ protected static function handleFields(
$segments = explode('.', $foreignKey);
$foreignKey = end($segments);
if (! array_key_exists($foreignKey, $field)) {
$field['fields'][$foreignKey] = self::FOREIGN_KEY;
$field['fields'][$foreignKey] = self::ALWAYS_RELATION_KEY;
}

if (is_a($relation, MorphMany::class) || is_a($relation, MorphOne::class)) {
$field['fields'][$relation->getMorphType()] = self::ALWAYS_RELATION_KEY;
}
}

Expand Down Expand Up @@ -295,7 +299,8 @@ protected static function validateField(FieldDefinition $fieldObject, array $que

default:
throw new RuntimeException(
sprintf("Unsupported use of 'privacy' configuration on field '%s'.",
sprintf(
"Unsupported use of 'privacy' configuration on field '%s'.",
$fieldObject->name
)
);
Expand Down
46 changes: 46 additions & 0 deletions tests/Database/SelectFields/MorphRelationshipTests/CommentType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Rebing\GraphQL\Tests\Database\SelectFields\MorphRelationshipTests;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
use Rebing\GraphQL\Tests\Support\Models\Comment;

class CommentType extends GraphQLType
{
protected $attributes = [
'name' => 'Comment',
'model' => Comment::class,
];

public function fields(): array
{
return [
'body' => [
'type' => Type::string(),
],
'id' => [
'type' => Type::nonNull(Type::ID()),
],
'likes' => [
'type' => Type::listOf(GraphQL::Type('Like')),
],
'post' => [
'type' => Type::nonNull(GraphQL::type('Post')),
],
'title' => [
'type' => Type::nonNull(Type::string()),
],
];
}

public function interfaces(): array
{
return [
GraphQL::type('LikableInterface'),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Rebing\GraphQL\Tests\Database\SelectFields\MorphRelationshipTests;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\InterfaceType;
use Rebing\GraphQL\Tests\Support\Models\Comment;
use Rebing\GraphQL\Tests\Support\Models\Post;

class LikableInterfaceType extends InterfaceType
{
protected $attributes = [
'name' => 'LikableInterface',
];

public function fields(): array
{
return [
'id' => [
'type' => Type::nonNull(Type::id()),
],
];
}

public function resolveType($root)
{
if ($root instanceof Post) {
return GraphQL::type('Post');
} elseif ($root instanceof Comment) {
return GraphQL::type('Comment');
}
}
}
33 changes: 33 additions & 0 deletions tests/Database/SelectFields/MorphRelationshipTests/LikeType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Rebing\GraphQL\Tests\Database\SelectFields\MorphRelationshipTests;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
use Rebing\GraphQL\Tests\Support\Models\Like;

class LikeType extends GraphQLType
{
protected $attributes = [
'name' => 'Like',
'model' => Like::class,
];

public function fields(): array
{
return [
'id' => [
'type' => Type::nonNull(Type::id()),
],
'likable' => [
'type' => Type::nonNull(GraphQL::type('LikableInterface')),
],
'user' => [
'type' => Type::nonNull(GraphQL::type('User')),
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

declare(strict_types=1);

namespace Rebing\GraphQL\Tests\Database\SelectFields\MorphRelationshipTests;

use Rebing\GraphQL\Tests\Support\Models\Like;
use Rebing\GraphQL\Tests\Support\Models\Post;
use Rebing\GraphQL\Tests\Support\Models\User;
use Rebing\GraphQL\Tests\Support\Traits\SqlAssertionTrait;
use Rebing\GraphQL\Tests\TestCaseDatabase;

class MorphRelationshipTest extends TestCaseDatabase
{
use SqlAssertionTrait;

public function testMorphRelationship(): void
{
$user = factory(User::class)->create([
'name' => 'User Name',
]);

$otherUser = factory(User::class)->create();

$post = factory(Post::class)->create([
'user_id' => $user->id,
]);

$like = factory(Like::class)->make([
'user_id' => $otherUser->id,
]);

$post->likes()->save($like);

$this->assertNotNull($user->posts[0]->likes[0]->user);

$query = <<<'GRAQPHQL'
{
users {
id
posts {
id
title
likes {
id
user {
id
}
}
}
}
}
GRAQPHQL;

$this->sqlCounterReset();

$result = $this->graphql($query, [
'expectErrors' => true,
]);

$this->assertSqlQueries(
<<<'SQL'
select "users"."id" from "users";
select "posts"."id", "posts"."title", "posts"."user_id" from "posts" where "posts"."user_id" in (?, ?) order by "posts"."id" asc;
select "likes"."id", "likes"."user_id", "likes"."likable_id", "likes"."likable_type" from "likes" where "likes"."likable_id" in (?) and "likes"."likable_type" = ?;
select "users"."id" from "users" where "users"."id" in (?);
select * from "posts" where "posts"."id" = ? limit 1;
SQL
);

$expectedResult = [
'data' => [
'users' => [
[
'id' => $user->id,
'posts' => [
[
'id' => $post->id,
'title' => $post->title,
'likes' => [
[
'id' => $like->id,
'user' => [
'id' => $otherUser->id,
],
],
],
],
],
],
[
'id' => $otherUser->id,
'posts' => [],
],
],
],
];
$this->assertEquals($expectedResult, $result);
}

protected function getEnvironmentSetUp($app)
{
parent::getEnvironmentSetUp($app);

$app['config']->set('graphql.schemas.default', [
'query' => [
UsersQuery::class,
],
]);

$app['config']->set('graphql.schemas.custom', null);

$app['config']->set('graphql.types', [
LikableInterfaceType::class,
CommentType::class,
LikeType::class,
PostType::class,
UserType::class,
]);
}
}
49 changes: 49 additions & 0 deletions tests/Database/SelectFields/MorphRelationshipTests/PostType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Rebing\GraphQL\Tests\Database\SelectFields\MorphRelationshipTests;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
use Rebing\GraphQL\Tests\Support\Models\Post;

class PostType extends GraphQLType
{
protected $attributes = [
'name' => 'Post',
'model' => Post::class,
];

public function fields(): array
{
return [
'body' => [
'type' => Type::nonNull(Type::string()),
],
'comments' => [
'type' => Type::listOf(GraphQL::type('Comment')),
],
'id' => [
'type' => Type::nonNull(Type::id()),
],
'likes' => [
'type' => Type::listOf(GraphQL::Type('Like')),
],
'title' => [
'type' => Type::nonNull(Type::string()),
],
'user' => [
'type' => GraphQL::type('User'),
],
];
}

public function interfaces(): array
{
return [
GraphQL::type('LikableInterface'),
];
}
}
36 changes: 36 additions & 0 deletions tests/Database/SelectFields/MorphRelationshipTests/UserType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Rebing\GraphQL\Tests\Database\SelectFields\MorphRelationshipTests;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
use Rebing\GraphQL\Tests\Support\Models\User;

class UserType extends GraphQLType
{
protected $attributes = [
'name' => 'User',
'model' => User::class,
];

public function fields(): array
{
return [
'id' => [
'type' => Type::nonNull(Type::id()),
],
'likes' => [
'type' => Type::listOf(GraphQL::Type('Like')),
],
'name' => [
'type' => Type::nonNull(Type::string()),
],
'posts' => [
'type' => Type::listOf(GraphQL::type('Post')),
],
];
}
}
Loading

0 comments on commit 3cf3c8d

Please sign in to comment.