mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-22 04:46:04 +03:00
Simplified blog example
This commit is contained in:
parent
e69667c633
commit
360bf39c9b
@ -9,7 +9,7 @@ use GraphQL\Utils;
|
||||
* Class AppContext
|
||||
* Instance available in all GraphQL resolvers as 3rd argument
|
||||
*
|
||||
* @package GraphQL\Examples\Social
|
||||
* @package GraphQL\Examples\Blog
|
||||
*/
|
||||
class AppContext
|
||||
{
|
||||
@ -27,9 +27,4 @@ class AppContext
|
||||
* @var \mixed
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var DataSource
|
||||
*/
|
||||
public $dataSource;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
namespace GraphQL\Examples\Blog\Data;
|
||||
use GraphQL\Utils;
|
||||
|
||||
/**
|
||||
* Class DataSource
|
||||
@ -12,14 +11,17 @@ use GraphQL\Utils;
|
||||
*/
|
||||
class DataSource
|
||||
{
|
||||
private $users = [];
|
||||
private $stories = [];
|
||||
private $storyLikes = [];
|
||||
private $comments = [];
|
||||
private static $users = [];
|
||||
private static $stories = [];
|
||||
private static $storyLikes = [];
|
||||
private static $comments = [];
|
||||
private static $storyComments = [];
|
||||
private static $commentReplies = [];
|
||||
private static $storyMentions = [];
|
||||
|
||||
public function __construct()
|
||||
public static function init()
|
||||
{
|
||||
$this->users = [
|
||||
self::$users = [
|
||||
'1' => new User([
|
||||
'id' => '1',
|
||||
'email' => 'john@example.com',
|
||||
@ -40,19 +42,19 @@ class DataSource
|
||||
]),
|
||||
];
|
||||
|
||||
$this->stories = [
|
||||
self::$stories = [
|
||||
'1' => new Story(['id' => '1', 'authorId' => '1', 'body' => '<h1>GraphQL is awesome!</h1>']),
|
||||
'2' => new Story(['id' => '2', 'authorId' => '1', 'body' => '<a>Test this</a>']),
|
||||
'3' => new Story(['id' => '3', 'authorId' => '3', 'body' => "This\n<br>story\n<br>spans\n<br>newlines"]),
|
||||
];
|
||||
|
||||
$this->storyLikes = [
|
||||
self::$storyLikes = [
|
||||
'1' => ['1', '2', '3'],
|
||||
'2' => [],
|
||||
'3' => ['1']
|
||||
];
|
||||
|
||||
$this->comments = [
|
||||
self::$comments = [
|
||||
// thread #1:
|
||||
'100' => new Comment(['id' => '100', 'authorId' => '3', 'storyId' => '1', 'body' => 'Likes']),
|
||||
'110' => new Comment(['id' =>'110', 'authorId' =>'2', 'storyId' => '1', 'body' => 'Reply <b>#1</b>', 'parentId' => '100']),
|
||||
@ -73,57 +75,69 @@ class DataSource
|
||||
'500' => new Comment(['id' => '500', 'authorId' => '2', 'storyId' => '2', 'body' => 'Nice!']),
|
||||
];
|
||||
|
||||
$this->storyComments = [
|
||||
self::$storyComments = [
|
||||
'1' => ['100', '200', '300'],
|
||||
'2' => ['400', '500']
|
||||
];
|
||||
|
||||
$this->commentReplies = [
|
||||
self::$commentReplies = [
|
||||
'100' => ['110', '120', '130'],
|
||||
'110' => ['111', '112', '113', '114', '115', '116', '117'],
|
||||
];
|
||||
|
||||
$this->storyMentions = [
|
||||
self::$storyMentions = [
|
||||
'1' => [
|
||||
$this->users['2']
|
||||
self::$users['2']
|
||||
],
|
||||
'2' => [
|
||||
$this->stories['1'],
|
||||
$this->users['3']
|
||||
self::$stories['1'],
|
||||
self::$users['3']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function findUser($id)
|
||||
public static function findUser($id)
|
||||
{
|
||||
return isset($this->users[$id]) ? $this->users[$id] : null;
|
||||
return isset(self::$users[$id]) ? self::$users[$id] : null;
|
||||
}
|
||||
|
||||
public function findStory($id)
|
||||
public static function findStory($id)
|
||||
{
|
||||
return isset($this->stories[$id]) ? $this->stories[$id] : null;
|
||||
return isset(self::$stories[$id]) ? self::$stories[$id] : null;
|
||||
}
|
||||
|
||||
public function findComment($id)
|
||||
public static function findComment($id)
|
||||
{
|
||||
return isset($this->comments[$id]) ? $this->comments[$id] : null;
|
||||
return isset(self::$comments[$id]) ? self::$comments[$id] : null;
|
||||
}
|
||||
|
||||
public function findLastStoryFor($authorId)
|
||||
public static function findLastStoryFor($authorId)
|
||||
{
|
||||
$storiesFound = array_filter($this->stories, function(Story $story) use ($authorId) {
|
||||
$storiesFound = array_filter(self::$stories, function(Story $story) use ($authorId) {
|
||||
return $story->authorId == $authorId;
|
||||
});
|
||||
return !empty($storiesFound) ? $storiesFound[count($storiesFound) - 1] : null;
|
||||
}
|
||||
|
||||
public function isLikedBy($storyId, $userId)
|
||||
public static function findLikes($storyId, $limit)
|
||||
{
|
||||
$subscribers = isset($this->storyLikes[$storyId]) ? $this->storyLikes[$storyId] : [];
|
||||
$likes = isset(self::$storyLikes[$storyId]) ? self::$storyLikes[$storyId] : [];
|
||||
$result = array_map(
|
||||
function($userId) {
|
||||
return self::$users[$userId];
|
||||
},
|
||||
$likes
|
||||
);
|
||||
return array_slice($result, 0, $limit);
|
||||
}
|
||||
|
||||
public static function isLikedBy($storyId, $userId)
|
||||
{
|
||||
$subscribers = isset(self::$storyLikes[$storyId]) ? self::$storyLikes[$storyId] : [];
|
||||
return in_array($userId, $subscribers);
|
||||
}
|
||||
|
||||
public function getUserPhoto($userId, $size)
|
||||
public static function getUserPhoto($userId, $size)
|
||||
{
|
||||
return new Image([
|
||||
'id' => $userId,
|
||||
@ -134,59 +148,59 @@ class DataSource
|
||||
]);
|
||||
}
|
||||
|
||||
public function findLatestStory()
|
||||
public static function findLatestStory()
|
||||
{
|
||||
return array_pop($this->stories);
|
||||
return array_pop(self::$stories);
|
||||
}
|
||||
|
||||
public function findStories($limit, $afterId = null)
|
||||
public static function findStories($limit, $afterId = null)
|
||||
{
|
||||
$start = $afterId ? (int) array_search($afterId, array_keys($this->stories)) + 1 : 0;
|
||||
return array_slice(array_values($this->stories), $start, $limit);
|
||||
$start = $afterId ? (int) array_search($afterId, array_keys(self::$stories)) + 1 : 0;
|
||||
return array_slice(array_values(self::$stories), $start, $limit);
|
||||
}
|
||||
|
||||
public function findComments($storyId, $limit = 5, $afterId = null)
|
||||
public static function findComments($storyId, $limit = 5, $afterId = null)
|
||||
{
|
||||
$storyComments = isset($this->storyComments[$storyId]) ? $this->storyComments[$storyId] : [];
|
||||
$storyComments = isset(self::$storyComments[$storyId]) ? self::$storyComments[$storyId] : [];
|
||||
|
||||
$start = isset($after) ? (int) array_search($afterId, $storyComments) + 1 : 0;
|
||||
$storyComments = array_slice($storyComments, $start, $limit);
|
||||
|
||||
return array_map(
|
||||
function($commentId) {
|
||||
return $this->comments[$commentId];
|
||||
return self::$comments[$commentId];
|
||||
},
|
||||
$storyComments
|
||||
);
|
||||
}
|
||||
|
||||
public function findReplies($commentId, $limit = 5, $afterId = null)
|
||||
public static function findReplies($commentId, $limit = 5, $afterId = null)
|
||||
{
|
||||
$commentReplies = isset($this->commentReplies[$commentId]) ? $this->commentReplies[$commentId] : [];
|
||||
$commentReplies = isset(self::$commentReplies[$commentId]) ? self::$commentReplies[$commentId] : [];
|
||||
|
||||
$start = isset($after) ? (int) array_search($afterId, $commentReplies) + 1: 0;
|
||||
$commentReplies = array_slice($commentReplies, $start, $limit);
|
||||
|
||||
return array_map(
|
||||
function($replyId) {
|
||||
return $this->comments[$replyId];
|
||||
return self::$comments[$replyId];
|
||||
},
|
||||
$commentReplies
|
||||
);
|
||||
}
|
||||
|
||||
public function countComments($storyId)
|
||||
public static function countComments($storyId)
|
||||
{
|
||||
return isset($this->storyComments[$storyId]) ? count($this->storyComments[$storyId]) : 0;
|
||||
return isset(self::$storyComments[$storyId]) ? count(self::$storyComments[$storyId]) : 0;
|
||||
}
|
||||
|
||||
public function countReplies($commentId)
|
||||
public static function countReplies($commentId)
|
||||
{
|
||||
return isset($this->commentReplies[$commentId]) ? count($this->commentReplies[$commentId]) : 0;
|
||||
return isset(self::$commentReplies[$commentId]) ? count(self::$commentReplies[$commentId]) : 0;
|
||||
}
|
||||
|
||||
public function findStoryMentions($storyId)
|
||||
public static function findStoryMentions($storyId)
|
||||
{
|
||||
return isset($this->storyMentions[$storyId]) ? $this->storyMentions[$storyId] :[];
|
||||
return isset(self::$storyMentions[$storyId]) ? self::$storyMentions[$storyId] :[];
|
||||
}
|
||||
}
|
||||
|
@ -3,36 +3,37 @@ namespace GraphQL\Examples\Blog\Type;
|
||||
|
||||
use GraphQL\Examples\Blog\AppContext;
|
||||
use GraphQL\Examples\Blog\Data\Comment;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Data\DataSource;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
|
||||
class CommentType extends BaseType
|
||||
{
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #1: using composition over inheritance to define type, see ImageType for inheritance example
|
||||
$this->definition = new ObjectType([
|
||||
'name' => 'Comment',
|
||||
'fields' => function() use ($types) {
|
||||
'fields' => function() {
|
||||
return [
|
||||
'id' => $types->id(),
|
||||
'author' => $types->user(),
|
||||
'parent' => $types->comment(),
|
||||
'isAnonymous' => $types->boolean(),
|
||||
'id' => Types::id(),
|
||||
'author' => Types::user(),
|
||||
'parent' => Types::comment(),
|
||||
'isAnonymous' => Types::boolean(),
|
||||
'replies' => [
|
||||
'type' => $types->listOf($types->comment()),
|
||||
'type' => Types::listOf(Types::comment()),
|
||||
'args' => [
|
||||
'after' => $types->int(),
|
||||
'after' => Types::int(),
|
||||
'limit' => [
|
||||
'type' => $types->int(),
|
||||
'type' => Types::int(),
|
||||
'defaultValue' => 5
|
||||
]
|
||||
]
|
||||
],
|
||||
'totalReplyCount' => $types->int(),
|
||||
'totalReplyCount' => Types::int(),
|
||||
|
||||
$types->htmlField('body')
|
||||
Types::htmlField('body')
|
||||
];
|
||||
},
|
||||
'resolveField' => function($value, $args, $context, ResolveInfo $info) {
|
||||
@ -45,30 +46,30 @@ class CommentType extends BaseType
|
||||
]);
|
||||
}
|
||||
|
||||
public function author(Comment $comment, $args, AppContext $context)
|
||||
public function author(Comment $comment)
|
||||
{
|
||||
if ($comment->isAnonymous) {
|
||||
return null;
|
||||
}
|
||||
return $context->dataSource->findUser($comment->authorId);
|
||||
return DataSource::findUser($comment->authorId);
|
||||
}
|
||||
|
||||
public function parent(Comment $comment, $args, AppContext $context)
|
||||
public function parent(Comment $comment)
|
||||
{
|
||||
if ($comment->parentId) {
|
||||
return $context->dataSource->findComment($comment->parentId);
|
||||
return DataSource::findComment($comment->parentId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function replies(Comment $comment, $args, AppContext $context)
|
||||
public function replies(Comment $comment, $args)
|
||||
{
|
||||
$args += ['after' => null];
|
||||
return $context->dataSource->findReplies($comment->id, $args['limit'], $args['after']);
|
||||
return DataSource::findReplies($comment->id, $args['limit'], $args['after']);
|
||||
}
|
||||
|
||||
public function totalReplyCount(Comment $comment, $args, AppContext $context)
|
||||
public function totalReplyCount(Comment $comment)
|
||||
{
|
||||
return $context->dataSource->countReplies($comment->id);
|
||||
return DataSource::countReplies($comment->id);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
namespace GraphQL\Examples\Blog\Type\Field;
|
||||
|
||||
use GraphQL\Examples\Blog\Type\Enum\ContentFormatEnum;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
|
||||
class HtmlField
|
||||
{
|
||||
public static function build(TypeSystem $types, $name, $objectKey = null)
|
||||
public static function build($name, $objectKey = null)
|
||||
{
|
||||
$objectKey = $objectKey ?: $name;
|
||||
|
||||
@ -15,13 +15,13 @@ class HtmlField
|
||||
// (for example when it is a part of some interface)
|
||||
return [
|
||||
'name' => $name,
|
||||
'type' => $types->string(),
|
||||
'type' => Types::string(),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => $types->contentFormatEnum(),
|
||||
'type' => Types::contentFormatEnum(),
|
||||
'defaultValue' => ContentFormatEnum::FORMAT_HTML
|
||||
],
|
||||
'maxLength' => $types->int()
|
||||
'maxLength' => Types::int()
|
||||
],
|
||||
'resolve' => function($object, $args) use ($objectKey) {
|
||||
$html = $object->{$objectKey};
|
||||
|
@ -3,42 +3,42 @@ namespace GraphQL\Examples\Blog\Type;
|
||||
|
||||
use GraphQL\Examples\Blog\AppContext;
|
||||
use GraphQL\Examples\Blog\Data\Image;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
|
||||
class ImageType extends ObjectType
|
||||
{
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #2: define type using inheritance, see any other object type for compositional example
|
||||
$config = [
|
||||
'name' => 'ImageType',
|
||||
'fields' => [
|
||||
'id' => $types->id(),
|
||||
'id' => Types::id(),
|
||||
'type' => new EnumType([
|
||||
'name' => 'ImageTypeEnum',
|
||||
'values' => [
|
||||
'USERPIC' => Image::TYPE_USERPIC
|
||||
]
|
||||
]),
|
||||
'size' => $types->imageSizeEnum(),
|
||||
'width' => $types->int(),
|
||||
'height' => $types->int(),
|
||||
'size' => Types::imageSizeEnum(),
|
||||
'width' => Types::int(),
|
||||
'height' => Types::int(),
|
||||
'url' => [
|
||||
'type' => $types->url(),
|
||||
'type' => Types::url(),
|
||||
'resolve' => [$this, 'resolveUrl']
|
||||
],
|
||||
|
||||
// Just for the sake of example
|
||||
'fieldWithError' => [
|
||||
'type' => $types->string(),
|
||||
'type' => Types::string(),
|
||||
'resolve' => function() {
|
||||
throw new \Exception("Field with exception");
|
||||
}
|
||||
],
|
||||
'nonNullFieldWithError' => [
|
||||
'type' => $types->nonNull($types->string()),
|
||||
'type' => Types::nonNull(Types::string()),
|
||||
'resolve' => function() {
|
||||
throw new \Exception("Non-null field with exception");
|
||||
}
|
||||
|
@ -4,33 +4,31 @@ namespace GraphQL\Examples\Blog\Type;
|
||||
use GraphQL\Examples\Blog\Data\Story;
|
||||
use GraphQL\Examples\Blog\Data\User;
|
||||
use GraphQL\Examples\Blog\Data\Image;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
|
||||
class NodeType extends BaseType
|
||||
{
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #1: using composition over inheritance to define type, see ImageType for inheritance example
|
||||
$this->definition = new InterfaceType([
|
||||
'name' => 'Node',
|
||||
'fields' => [
|
||||
'id' => $types->id()
|
||||
'id' => Types::id()
|
||||
],
|
||||
'resolveType' => function ($object) use ($types) {
|
||||
return $this->resolveType($object, $types);
|
||||
}
|
||||
'resolveType' => [$this, 'resolveType']
|
||||
]);
|
||||
}
|
||||
|
||||
public function resolveType($object, TypeSystem $types)
|
||||
public function resolveType($object, Types $types)
|
||||
{
|
||||
if ($object instanceof User) {
|
||||
return $types->user();
|
||||
return Types::user();
|
||||
} else if ($object instanceof Image) {
|
||||
return $types->image();
|
||||
return Types::image();
|
||||
} else if ($object instanceof Story) {
|
||||
return $types->story();
|
||||
return Types::story();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,51 +2,52 @@
|
||||
namespace GraphQL\Examples\Blog\Type;
|
||||
|
||||
use GraphQL\Examples\Blog\AppContext;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Data\DataSource;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
|
||||
class QueryType extends BaseType
|
||||
{
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #1: using composition over inheritance to define type, see ImageType for inheritance example
|
||||
$this->definition = new ObjectType([
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'user' => [
|
||||
'type' => $types->user(),
|
||||
'type' => Types::user(),
|
||||
'description' => 'Returns user by id (in range of 1-5)',
|
||||
'args' => [
|
||||
'id' => $types->nonNull($types->id())
|
||||
'id' => Types::nonNull(Types::id())
|
||||
]
|
||||
],
|
||||
'viewer' => [
|
||||
'type' => $types->user(),
|
||||
'type' => Types::user(),
|
||||
'description' => 'Represents currently logged-in user (for the sake of example - simply returns user with id == 1)'
|
||||
],
|
||||
'stories' => [
|
||||
'type' => $types->listOf($types->story()),
|
||||
'type' => Types::listOf(Types::story()),
|
||||
'description' => 'Returns subset of stories posted for this blog',
|
||||
'args' => [
|
||||
'after' => [
|
||||
'type' => $types->id(),
|
||||
'type' => Types::id(),
|
||||
'description' => 'Fetch stories listed after the story with this ID'
|
||||
],
|
||||
'limit' => [
|
||||
'type' => $types->int(),
|
||||
'type' => Types::int(),
|
||||
'description' => 'Number of stories to be returned',
|
||||
'defaultValue' => 10
|
||||
]
|
||||
]
|
||||
],
|
||||
'lastStoryPosted' => [
|
||||
'type' => $types->story(),
|
||||
'type' => Types::story(),
|
||||
'description' => 'Returns last story posted for this blog'
|
||||
],
|
||||
'deprecatedField' => [
|
||||
'type' => $types->string(),
|
||||
'type' => Types::string(),
|
||||
'deprecationReason' => 'This field is deprecated!'
|
||||
],
|
||||
'hello' => Type::string()
|
||||
@ -57,25 +58,25 @@ class QueryType extends BaseType
|
||||
]);
|
||||
}
|
||||
|
||||
public function user($val, $args, AppContext $context)
|
||||
public function user($rootValue, $args)
|
||||
{
|
||||
return $context->dataSource->findUser($args['id']);
|
||||
return DataSource::findUser($args['id']);
|
||||
}
|
||||
|
||||
public function viewer($val, $args, AppContext $context)
|
||||
public function viewer($rootValue, $args, AppContext $context)
|
||||
{
|
||||
return $context->viewer;
|
||||
}
|
||||
|
||||
public function stories($val, $args, AppContext $context)
|
||||
public function stories($rootValue, $args)
|
||||
{
|
||||
$args += ['after' => null];
|
||||
return $context->dataSource->findStories($args['limit'], $args['after']);
|
||||
return DataSource::findStories($args['limit'], $args['after']);
|
||||
}
|
||||
|
||||
public function lastStoryPosted($val, $args, AppContext $context)
|
||||
public function lastStoryPosted()
|
||||
{
|
||||
return $context->dataSource->findLatestStory();
|
||||
return DataSource::findLatestStory();
|
||||
}
|
||||
|
||||
public function hello()
|
||||
|
@ -1,30 +1,29 @@
|
||||
<?php
|
||||
namespace GraphQL\Examples\Blog\Type;
|
||||
|
||||
use GraphQL\Examples\Blog\AppContext;
|
||||
use GraphQL\Examples\Blog\Data\Story;
|
||||
use GraphQL\Examples\Blog\Data\User;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
|
||||
class MentionType extends BaseType
|
||||
class SearchResultType extends BaseType
|
||||
{
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #1: using composition over inheritance to define type, see ImageType for inheritance example
|
||||
$this->definition = new UnionType([
|
||||
'name' => 'Mention',
|
||||
'types' => function() use ($types) {
|
||||
'name' => 'SearchResultType',
|
||||
'types' => function() {
|
||||
return [
|
||||
$types->story(),
|
||||
$types->user()
|
||||
Types::story(),
|
||||
Types::user()
|
||||
];
|
||||
},
|
||||
'resolveType' => function($value) use ($types) {
|
||||
'resolveType' => function($value) {
|
||||
if ($value instanceof Story) {
|
||||
return $types->story();
|
||||
return Types::story();
|
||||
} else if ($value instanceof User) {
|
||||
return $types->user();
|
||||
return Types::user();
|
||||
}
|
||||
}
|
||||
]);
|
@ -2,9 +2,9 @@
|
||||
namespace GraphQL\Examples\Blog\Type;
|
||||
|
||||
use GraphQL\Examples\Blog\AppContext;
|
||||
use GraphQL\Examples\Blog\Data\DataSource;
|
||||
use GraphQL\Examples\Blog\Data\Story;
|
||||
use GraphQL\Examples\Blog\Data\User;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
@ -21,31 +21,44 @@ class StoryType extends BaseType
|
||||
const UNLIKE = 'UNLIKE';
|
||||
const REPLY = 'REPLY';
|
||||
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #1: using composition over inheritance to define type, see ImageType for inheritance example
|
||||
$this->definition = new ObjectType([
|
||||
'name' => 'Story',
|
||||
'fields' => function() use ($types) {
|
||||
'fields' => function() {
|
||||
return [
|
||||
'id' => $types->id(),
|
||||
'author' => $types->user(),
|
||||
'mentions' => $types->listOf($types->mention()),
|
||||
'totalCommentCount' => $types->int(),
|
||||
'id' => Types::id(),
|
||||
'author' => Types::user(),
|
||||
'mentions' => Types::listOf(Types::mention()),
|
||||
'totalCommentCount' => Types::int(),
|
||||
'comments' => [
|
||||
'type' => $types->listOf($types->comment()),
|
||||
'type' => Types::listOf(Types::comment()),
|
||||
'args' => [
|
||||
'after' => [
|
||||
'type' => $types->id(),
|
||||
'type' => Types::id(),
|
||||
'description' => 'Load all comments listed after given comment ID'
|
||||
],
|
||||
'limit' => [
|
||||
'type' => $types->int(),
|
||||
'type' => Types::int(),
|
||||
'defaultValue' => 5
|
||||
]
|
||||
]
|
||||
],
|
||||
'affordances' => $types->listOf(new EnumType([
|
||||
'likes' => [
|
||||
'type' => Types::listOf(Types::user()),
|
||||
'args' => [
|
||||
'limit' => [
|
||||
'type' => Types::int(),
|
||||
'description' => 'Limit the number of recent likes returned',
|
||||
'defaultValue' => 5
|
||||
]
|
||||
]
|
||||
],
|
||||
'likedBy' => [
|
||||
'type' => Types::listOf(Types::user()),
|
||||
],
|
||||
'affordances' => Types::listOf(new EnumType([
|
||||
'name' => 'StoryAffordancesEnum',
|
||||
'values' => [
|
||||
self::EDIT,
|
||||
@ -55,13 +68,13 @@ class StoryType extends BaseType
|
||||
self::REPLY
|
||||
]
|
||||
])),
|
||||
'hasViewerLiked' => $types->boolean(),
|
||||
'hasViewerLiked' => Types::boolean(),
|
||||
|
||||
$types->htmlField('body'),
|
||||
Types::htmlField('body'),
|
||||
];
|
||||
},
|
||||
'interfaces' => [
|
||||
$types->node()
|
||||
Types::node()
|
||||
],
|
||||
'resolveField' => function($value, $args, $context, ResolveInfo $info) {
|
||||
if (method_exists($this, $info->fieldName)) {
|
||||
@ -73,15 +86,15 @@ class StoryType extends BaseType
|
||||
]);
|
||||
}
|
||||
|
||||
public function author(Story $story, $args, AppContext $context)
|
||||
public function author(Story $story)
|
||||
{
|
||||
return $context->dataSource->findUser($story->authorId);
|
||||
return DataSource::findUser($story->authorId);
|
||||
}
|
||||
|
||||
public function affordances(Story $story, $args, AppContext $context)
|
||||
{
|
||||
$isViewer = $context->viewer === $context->dataSource->findUser($story->authorId);
|
||||
$isLiked = $context->dataSource->isLikedBy($story->id, $context->viewer->id);
|
||||
$isViewer = $context->viewer === DataSource::findUser($story->authorId);
|
||||
$isLiked = DataSource::isLikedBy($story->id, $context->viewer->id);
|
||||
|
||||
if ($isViewer) {
|
||||
$affordances[] = self::EDIT;
|
||||
@ -97,17 +110,17 @@ class StoryType extends BaseType
|
||||
|
||||
public function hasViewerLiked(Story $story, $args, AppContext $context)
|
||||
{
|
||||
return $context->dataSource->isLikedBy($story->id, $context->viewer->id);
|
||||
return DataSource::isLikedBy($story->id, $context->viewer->id);
|
||||
}
|
||||
|
||||
public function totalCommentCount(Story $story, $args, AppContext $context)
|
||||
public function totalCommentCount(Story $story)
|
||||
{
|
||||
return $context->dataSource->countComments($story->id);
|
||||
return DataSource::countComments($story->id);
|
||||
}
|
||||
|
||||
public function comments(Story $story, $args, AppContext $context)
|
||||
public function comments(Story $story, $args)
|
||||
{
|
||||
$args += ['after' => null];
|
||||
return $context->dataSource->findComments($story->id, $args['limit'], $args['after']);
|
||||
return DataSource::findComments($story->id, $args['limit'], $args['after']);
|
||||
}
|
||||
}
|
||||
|
@ -2,38 +2,40 @@
|
||||
namespace GraphQL\Examples\Blog\Type;
|
||||
|
||||
use GraphQL\Examples\Blog\AppContext;
|
||||
use GraphQL\Examples\Blog\Data\DataSource;
|
||||
use GraphQL\Examples\Blog\Data\User;
|
||||
use GraphQL\Examples\Blog\TypeSystem;
|
||||
use GraphQL\Examples\Blog\Types;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
|
||||
class UserType extends BaseType
|
||||
{
|
||||
public function __construct(TypeSystem $types)
|
||||
public function __construct()
|
||||
{
|
||||
// Option #1: using composition over inheritance to define type, see ImageType for inheritance example
|
||||
$this->definition = new ObjectType([
|
||||
'name' => 'User',
|
||||
'fields' => function() use ($types) {
|
||||
'description' => 'Our blog authors',
|
||||
'fields' => function() {
|
||||
return [
|
||||
'id' => $types->id(),
|
||||
'email' => $types->email(),
|
||||
'id' => Types::id(),
|
||||
'email' => Types::email(),
|
||||
'photo' => [
|
||||
'type' => $types->image(),
|
||||
'type' => Types::image(),
|
||||
'description' => 'User photo URL',
|
||||
'args' => [
|
||||
'size' => $types->nonNull($types->imageSizeEnum()),
|
||||
'size' => Types::nonNull(Types::imageSizeEnum()),
|
||||
]
|
||||
],
|
||||
'firstName' => [
|
||||
'type' => $types->string(),
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'lastName' => [
|
||||
'type' => $types->string(),
|
||||
'type' => Types::string(),
|
||||
],
|
||||
'lastStoryPosted' => $types->story(),
|
||||
'lastStoryPosted' => Types::story(),
|
||||
'fieldWithError' => [
|
||||
'type' => $types->string(),
|
||||
'type' => Types::string(),
|
||||
'resolve' => function() {
|
||||
throw new \Exception("This is error field");
|
||||
}
|
||||
@ -41,7 +43,7 @@ class UserType extends BaseType
|
||||
];
|
||||
},
|
||||
'interfaces' => [
|
||||
$types->node()
|
||||
Types::node()
|
||||
],
|
||||
'resolveField' => function($value, $args, $context, ResolveInfo $info) {
|
||||
if (method_exists($this, $info->fieldName)) {
|
||||
@ -53,13 +55,13 @@ class UserType extends BaseType
|
||||
]);
|
||||
}
|
||||
|
||||
public function photo(User $user, $args, AppContext $context)
|
||||
public function photo(User $user, $args)
|
||||
{
|
||||
return $context->dataSource->getUserPhoto($user->id, $args['size']);
|
||||
return DataSource::getUserPhoto($user->id, $args['size']);
|
||||
}
|
||||
|
||||
public function lastStoryPosted(User $user, $args, AppContext $context)
|
||||
public function lastStoryPosted(User $user)
|
||||
{
|
||||
return $context->dataSource->findLastStoryFor($user->id);
|
||||
return DataSource::findLastStoryFor($user->id);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use GraphQL\Examples\Blog\Type\CommentType;
|
||||
use GraphQL\Examples\Blog\Type\Enum\ContentFormatEnum;
|
||||
use GraphQL\Examples\Blog\Type\Enum\ImageSizeEnumType;
|
||||
use GraphQL\Examples\Blog\Type\Field\HtmlField;
|
||||
use GraphQL\Examples\Blog\Type\MentionType;
|
||||
use GraphQL\Examples\Blog\Type\SearchResultType;
|
||||
use GraphQL\Examples\Blog\Type\NodeType;
|
||||
use GraphQL\Examples\Blog\Type\QueryType;
|
||||
use GraphQL\Examples\Blog\Type\Scalar\EmailType;
|
||||
@ -19,7 +19,7 @@ use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\DefinitionContainer;
|
||||
|
||||
/**
|
||||
* Class TypeSystem
|
||||
* Class Types
|
||||
*
|
||||
* Acts as a registry and factory for your types.
|
||||
*
|
||||
@ -28,115 +28,115 @@ use GraphQL\Type\DefinitionContainer;
|
||||
*
|
||||
* @package GraphQL\Examples\Blog
|
||||
*/
|
||||
class TypeSystem
|
||||
class Types
|
||||
{
|
||||
// Object types:
|
||||
private $user;
|
||||
private $story;
|
||||
private $comment;
|
||||
private $image;
|
||||
private $query;
|
||||
private static $user;
|
||||
private static $story;
|
||||
private static $comment;
|
||||
private static $image;
|
||||
private static $query;
|
||||
|
||||
/**
|
||||
* @return UserType
|
||||
*/
|
||||
public function user()
|
||||
public static function user()
|
||||
{
|
||||
return $this->user ?: ($this->user = new UserType($this));
|
||||
return self::$user ?: (self::$user = new UserType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StoryType
|
||||
*/
|
||||
public function story()
|
||||
public static function story()
|
||||
{
|
||||
return $this->story ?: ($this->story = new StoryType($this));
|
||||
return self::$story ?: (self::$story = new StoryType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommentType
|
||||
*/
|
||||
public function comment()
|
||||
public static function comment()
|
||||
{
|
||||
return $this->comment ?: ($this->comment = new CommentType($this));
|
||||
return self::$comment ?: (self::$comment = new CommentType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ImageType
|
||||
*/
|
||||
public function image()
|
||||
public static function image()
|
||||
{
|
||||
return $this->image ?: ($this->image = new ImageType($this));
|
||||
return self::$image ?: (self::$image = new ImageType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryType
|
||||
*/
|
||||
public function query()
|
||||
public static function query()
|
||||
{
|
||||
return $this->query ?: ($this->query = new QueryType($this));
|
||||
return self::$query ?: (self::$query = new QueryType());
|
||||
}
|
||||
|
||||
|
||||
// Interface types
|
||||
private $node;
|
||||
private static $node;
|
||||
|
||||
/**
|
||||
* @return NodeType
|
||||
*/
|
||||
public function node()
|
||||
public static function node()
|
||||
{
|
||||
return $this->node ?: ($this->node = new NodeType($this));
|
||||
return self::$node ?: (self::$node = new NodeType());
|
||||
}
|
||||
|
||||
|
||||
// Unions types:
|
||||
private $mention;
|
||||
private static $mention;
|
||||
|
||||
/**
|
||||
* @return MentionType
|
||||
* @return SearchResultType
|
||||
*/
|
||||
public function mention()
|
||||
public static function mention()
|
||||
{
|
||||
return $this->mention ?: ($this->mention = new MentionType($this));
|
||||
return self::$mention ?: (self::$mention = new SearchResultType());
|
||||
}
|
||||
|
||||
|
||||
// Enum types
|
||||
private $imageSizeEnum;
|
||||
private $contentFormatEnum;
|
||||
private static $imageSizeEnum;
|
||||
private static $contentFormatEnum;
|
||||
|
||||
/**
|
||||
* @return ImageSizeEnumType
|
||||
*/
|
||||
public function imageSizeEnum()
|
||||
public static function imageSizeEnum()
|
||||
{
|
||||
return $this->imageSizeEnum ?: ($this->imageSizeEnum = new ImageSizeEnumType());
|
||||
return self::$imageSizeEnum ?: (self::$imageSizeEnum = new ImageSizeEnumType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContentFormatEnum
|
||||
*/
|
||||
public function contentFormatEnum()
|
||||
public static function contentFormatEnum()
|
||||
{
|
||||
return $this->contentFormatEnum ?: ($this->contentFormatEnum = new ContentFormatEnum());
|
||||
return self::$contentFormatEnum ?: (self::$contentFormatEnum = new ContentFormatEnum());
|
||||
}
|
||||
|
||||
// Custom Scalar types:
|
||||
private $urlType;
|
||||
private $emailType;
|
||||
private static $urlType;
|
||||
private static $emailType;
|
||||
|
||||
public function email()
|
||||
public static function email()
|
||||
{
|
||||
return $this->emailType ?: ($this->emailType = new EmailType());
|
||||
return self::$emailType ?: (self::$emailType = new EmailType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UrlType
|
||||
*/
|
||||
public function url()
|
||||
public static function url()
|
||||
{
|
||||
return $this->urlType ?: ($this->urlType = new UrlType());
|
||||
return self::$urlType ?: (self::$urlType = new UrlType());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,16 +144,16 @@ class TypeSystem
|
||||
* @param null $objectKey
|
||||
* @return array
|
||||
*/
|
||||
public function htmlField($name, $objectKey = null)
|
||||
public static function htmlField($name, $objectKey = null)
|
||||
{
|
||||
return HtmlField::build($this, $name, $objectKey);
|
||||
return HtmlField::build($name, $objectKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Let's add internal types as well for consistent experience
|
||||
|
||||
public function boolean()
|
||||
public static function boolean()
|
||||
{
|
||||
return Type::boolean();
|
||||
}
|
||||
@ -161,7 +161,7 @@ class TypeSystem
|
||||
/**
|
||||
* @return \GraphQL\Type\Definition\FloatType
|
||||
*/
|
||||
public function float()
|
||||
public static function float()
|
||||
{
|
||||
return Type::float();
|
||||
}
|
||||
@ -169,7 +169,7 @@ class TypeSystem
|
||||
/**
|
||||
* @return \GraphQL\Type\Definition\IDType
|
||||
*/
|
||||
public function id()
|
||||
public static function id()
|
||||
{
|
||||
return Type::id();
|
||||
}
|
||||
@ -177,7 +177,7 @@ class TypeSystem
|
||||
/**
|
||||
* @return \GraphQL\Type\Definition\IntType
|
||||
*/
|
||||
public function int()
|
||||
public static function int()
|
||||
{
|
||||
return Type::int();
|
||||
}
|
||||
@ -185,7 +185,7 @@ class TypeSystem
|
||||
/**
|
||||
* @return \GraphQL\Type\Definition\StringType
|
||||
*/
|
||||
public function string()
|
||||
public static function string()
|
||||
{
|
||||
return Type::string();
|
||||
}
|
||||
@ -194,7 +194,7 @@ class TypeSystem
|
||||
* @param Type|DefinitionContainer $type
|
||||
* @return ListOfType
|
||||
*/
|
||||
public function listOf($type)
|
||||
public static function listOf($type)
|
||||
{
|
||||
return new ListOfType($type);
|
||||
}
|
||||
@ -203,7 +203,7 @@ class TypeSystem
|
||||
* @param Type|DefinitionContainer $type
|
||||
* @return NonNull
|
||||
*/
|
||||
public function nonNull($type)
|
||||
public static function nonNull($type)
|
||||
{
|
||||
return new NonNull($type);
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
// php -S localhost:8080 ./index.php
|
||||
require_once '../../vendor/autoload.php';
|
||||
|
||||
use \GraphQL\Examples\Blog\TypeSystem;
|
||||
use \GraphQL\Examples\Blog\Types;
|
||||
use \GraphQL\Examples\Blog\AppContext;
|
||||
use \GraphQL\Examples\Blog\Data\DataSource;
|
||||
use \GraphQL\Schema;
|
||||
@ -27,17 +27,12 @@ if (!empty($_GET['debug'])) {
|
||||
}
|
||||
|
||||
try {
|
||||
// Initialize user-land registry/factory of our app types:
|
||||
$typeSystem = new TypeSystem();
|
||||
|
||||
// Init stub data source
|
||||
// (in real-world apps this might be Doctrine's EntityManager for instance, or just DB connection):
|
||||
$dataSource = new DataSource();
|
||||
// Initialize our fake data source
|
||||
DataSource::init();
|
||||
|
||||
// Prepare context that will be available in all field resolvers (as 3rd argument):
|
||||
$appContext = new AppContext();
|
||||
$appContext->viewer = $dataSource->findUser(1); // simulated "currently logged-in user"
|
||||
$appContext->dataSource = $dataSource;
|
||||
$appContext->viewer = DataSource::findUser('1'); // simulated "currently logged-in user"
|
||||
$appContext->rootUrl = 'http://localhost:8080';
|
||||
$appContext->request = $_REQUEST;
|
||||
|
||||
@ -58,7 +53,7 @@ try {
|
||||
|
||||
// GraphQL schema to be passed to query executor:
|
||||
$schema = new Schema([
|
||||
'query' => $typeSystem->query()
|
||||
'query' => Types::query()
|
||||
]);
|
||||
|
||||
$result = GraphQL::execute(
|
||||
|
Loading…
Reference in New Issue
Block a user