
æè¿ãGraphQLã«ã€ããŠããè³ã«ããŸãã ãŸããã€ã³ã¿ãŒãããäžã«ã¯ãGraphQLãµãŒããŒã®äœææ¹æ³ã«é¢ããå€ãã®èšäºãæ¢ã«ãããŸãã ãããããããã®èšäºã®ã»ãšãã©ãã¹ãŠã§ãNode.jsã¯ããã¯ãšã³ããšããŠäœ¿çšãããŠããŸãã
ç§ã¯Node.jsã«å察ããããšã¯ãªããç§èªèº«ãåãã§äœ¿çšããŠããŸãããããã§ãã»ãšãã©ã®ãããžã§ã¯ãã¯PHPã§è¡ã£ãŠããŸãã ããã«ãPHPãšMySQLã䜿çšãããã¹ãã£ã³ã°ã¯ãNode.jsã䜿çšãããã¹ãã£ã³ã°ãããã¯ããã«å®äŸ¡ã§æé ãªäŸ¡æ Œã§ãã ãã®ãããã€ã³ã¿ãŒãããäžã®PHPã§GraphQLã䜿çšããããšã«ã€ããŠäºå®äžäœã®èšèããªãããšã¯ãç§ã«ã¯å ¬å¹³ã«æããŸããã
ãã®èšäºã§ã¯ã graphql-phpã©ã€ãã©ãªã䜿çšããŠPHPã§GraphQLãµãŒããŒãäœæããæ¹æ³ãšãããã䜿çšããŠMySQLããããŒã¿ãååŸããããã®ç°¡åãªAPIãå®è£ ããæ¹æ³ã«ã€ããŠèª¬æããŸãã
ãã®èšäºã§ã¯ç¹å®ã®PHPãã¬ãŒã ã¯ãŒã¯ã®äœ¿çšãæŸæ£ããããšã«ããŸãããããããèªãã åŸããã®ç¥èãã¢ããªã±ãŒã·ã§ã³ã«é©çšããããšã¯é£ãããããŸããã ããã«ãäžéšã®ãã¬ãŒã ã¯ãŒã¯ã«ã¯ãäœæ¥ã容æã«ããgraphql-phpã«åºã¥ãã©ã€ãã©ãªãæ¢ã«ãããŸãã
æºåãã
ãã®èšäºã§ã¯ããã³ããšã³ããå®è¡ããŸããããããã£ãŠãGraphQLãµãŒããŒãžã®ã¯ãšãªãç°¡åã«ãã¹ãããããã«ããã©ãŠã¶ãŒã«GraphiQLæ¡åŒµæ©èœãã€ã³ã¹ããŒã«ããããšããå§ãããŸãã
Chromeã®å ŽåïŒ
ãŸããããŒã¿ããŒã¹ã«ããŒãã«ãäœæãããã¹ãããŒã¿ã»ãããå ¥åããå¿ èŠããããŸãã
ãusersãããŒãã«ã«ããŠãŒã¶ãŒã®ãªã¹ããä¿åããŸãã

ãŸããåæ ã®è¡šã«ã¯ããŠãŒã¶ãŒéã®åæ ã瀺ãå€å¯Ÿå€ã®é¢ä¿ããããŸãã

ããŒã¿ããŒã¹ãã³ãã¯ãä»ã®ãã¹ãŠã®ã³ãŒããšåæ§ã«ãGithubã®ãã®èšäºã®ãªããžããªããååŸã§ããŸãã
ããã«ã¡ã¯ãGraphQLïŒ
ãŸãããããžã§ã¯ãã«graphql-phpãã€ã³ã¹ããŒã«ããå¿ èŠããããŸãã ã³ã³ããŒã¶ãŒã§ãããè¡ãããšãã§ããŸãïŒ
composer require webonyx/graphql-php
ä»ãäŒçµ±ã«åŸã£ãŠããHelloãWorldããšæžããŸãã
ãããè¡ãã«ã¯ãã«ãŒãã«graphql.phpãã¡ã€ã«ãäœæããŸããããã¯ãGraphQLãµãŒããŒã®ãšã³ããã€ã³ããšããŠæ©èœããŸãã
ãã®äžã§ãcomposerãªãŒãããŒããŒãæ¥ç¶ããŸãã
require_once __DIR__ . '/vendor/autoload.php';
GraphQLãæ¥ç¶ããŸãã
use GraphQL\GraphQL;
GraphQLã«ã¯ãšãªãå®è¡ãããã«ã¯ãã¯ãšãªèªäœãšããŒã¿ã¹ããŒããæž¡ãå¿ èŠããããŸãã
ãªã¯ãšã¹ããåä¿¡ããã«ã¯ã次ã®ã³ãŒããèšè¿°ããŸãã
$rawInput = file_get_contents('php://input'); $input = json_decode($rawInput, true); $query = $input['query'];
ã¹ããŒããäœæããã«ã¯ããŸãGraphQL \ Schemaãæ¥ç¶ããŸãã
use GraphQL\Schema;
ã¹ããŒãã³ã³ã¹ãã©ã¯ã¿ãŒã¯ãã¯ãšãªã«ãŒãããŒã¿åãæå®ããå¿ èŠã®ããé åãåãå ¥ããŸãããã®é åã¯ãAPIã®ããŒã¿ãèªã¿åãã®ã«åœ¹ç«ã€ãããæåã«ãã®åãäœæããŸãã
ã泚æ
ã¹ããŒãã«ã¯ãããŒã¿ãæžã蟌ãããã®APIãæäŸãããªãã·ã§ã³ã®ã«ãŒãMutationããŒã¿åãå«ããããšãã§ããŸããããã®èšäºã§ã¯èª¬æããŸããã
æãåçŽãªã±ãŒã¹ã§ã¯ãQueryããŒã¿åã¯ObjectTypeã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ã§ããããã®ãã£ãŒã«ãã¯åçŽåïŒããšãã°ãintãŸãã¯stringïŒã§ããå¿ èŠãããããããããã®ããŒã¿åãæäŸããã¯ã©ã¹ãGraphQLã§æ¥ç¶ããŸãã
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\ObjectType;
ãããŠãQueryããŒã¿åãäœæããŸãã
$queryType = new ObjectType([ 'name' => 'Query', 'fields' => [ 'hello' => [ 'type' => Type::string(), 'description' => ' ', 'resolve' => function () { return ', GraphQL!'; } ] ] ]);
ã芧ã®ãšãããããŒã¿åã«ã¯ååãšãã£ãŒã«ãã®é åãå«ãŸããŠããå¿ èŠãããããªãã·ã§ã³ã®èª¬æãæå®ããããšãã§ããŸãã
ã泚æ
ãŸããããŒã¿åã«ã¯ãinterfacesãããisTypeOfããããã³ãresolveFieldãããããã£ãå«ãŸããå ŽåããããŸããããã®èšäºã§ã¯ããããèæ
®ããŸããã
ããŒã¿åã®ãã£ãŒã«ãã«ã¯ãå¿ èŠãªããããã£ãnameããšãtypeããå¿ èŠã§ãã ãnameãããããã£ãèšå®ãããŠããªãå Žåããã£ãŒã«ãããŒãååãšããŠäœ¿çšãããŸãïŒãã®å Žåã¯ãhelloãïŒã ãŸãããã®äŸã§ã¯ããhelloããã£ãŒã«ãã¯ãªãã·ã§ã³ã®ããããã£ãdescriptionãïŒèª¬æãšãresolveãïŒã«èšå®ãããçµæãè¿ãé¢æ°ã§ãã ãã®å Žåãã解決ãé¢æ°ã¯åã«æååã HelloãGraphQLïŒ ããè¿ããŸãããããå°é£ãªç¶æ³ã§ã¯ãAPIãŸãã¯ããŒã¿ããŒã¹ããæ å ±ãåä¿¡ããŠââåŠçã§ããŸãã
ã泚æ
ãã£ãŒã«ãã«ã¯ããã®èšäºã§åŸè¿°ããargsããããã£ãšããã®èšäºã§ã¯èæ
®ãããªãdeprecationReasonããããã£ãå«ããããšãã§ããŸãã
ãããã£ãŠã1ã€ã®helloãã£ãŒã«ãã®ã¿ãå«ãã«ãŒãQueryããŒã¿åãäœæããåçŽãªããã¹ãæååãè¿ããŸãã ãããããŒã¿ã¹ããŒãã«è¿œå ããŸãããã
$schema = new Schema([ 'query' => $queryType ]);
ãããŠãGraphQLã¯ãšãªãå®è¡ããŠçµæãååŸããŸãã
$result = GraphQL::execute($schema, $query);
çµæãJSON圢åŒã§åºåããã ãã§ãã¢ããªã±ãŒã·ã§ã³ã®æºåã¯å®äºã§ãã
header('Content-Type: application/json; charset=UTF-8'); echo json_encode($result);
try-catchãããã¯ã§ã³ãŒããã©ããããŠãšã©ãŒãåŠçãããšãgraphql.phpãã¡ã€ã«ã®ã³ãŒãã¯æ¬¡ã®ããã«ãªããŸãã
graphql.php
<?php require_once __DIR__ . '/vendor/autoload.php'; use GraphQL\GraphQL; use GraphQL\Schema; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\ObjectType; try { // $rawInput = file_get_contents('php://input'); $input = json_decode($rawInput, true); $query = $input['query']; // "" $queryType = new ObjectType([ 'name' => 'Query', 'fields' => [ 'hello' => [ 'type' => Type::string(), 'description' => ' ', 'resolve' => function () { return ', GraphQL!'; } ] ] ]); // $schema = new Schema([ 'query' => $queryType ]); // $result = GraphQL::execute($schema, $query); } catch (\Exception $e) { $result = [ 'error' => [ 'message' => $e->getMessage() ] ]; } // header('Content-Type: application/json; charset=UTF-8'); echo json_encode($result);
GraphQLã®åäœã確èªããŸãã ãããè¡ãã«ã¯ãGraphiQLã®æ¡åŒµæ©èœãå®è¡ãããšã³ããã€ã³ãïŒç§ã®å Žåã¯ã localhost / graphql.php ãïŒãã€ã³ã¹ããŒã«ããŠãã¯ãšãªãå®è¡ããŸãã

DBããã®ãŠãŒã¶ãŒã®çµè«
ããã§ã¯ãã¿ã¹ã¯ãè€éã«ããŸãããã MySQLããŒã¿ããŒã¹ã®ãŠãŒã¶ãŒããªã¹ãããŸãã
ãããè¡ãã«ã¯ãObjectTypeã¯ã©ã¹ã®å¥ã®ããŒã¿åãäœæããå¿ èŠããããŸãã graphql.phpã®ã³ãŒããç©ã¿äžããªãããã«ããã¹ãŠã®ããŒã¿åãåå¥ã®ãã¡ã€ã«ã«ãšã¯ã¹ããŒãããŸãã ãããŠãç§ãã¡èªèº«ã®äžã§ããŒã¿åã䜿çšããæ©äŒãããããã«ãããããã¯ã©ã¹ã®åœ¢ã«æŽçããŸãã ããšãã°ãããŒã¿ã¿ã€ããuserãã«ãã£ãŒã«ããfriendsããè¿œå ã§ããŸããããã¯ãåãã¿ã€ããuserãã®ãŠãŒã¶ãŒã®é åã«ãªããŸãã
ããŒã¿åãã¯ã©ã¹ãšããŠãã©ãŒãããããå Žåãããã©ã«ãã§ã¯ã©ã¹åããååŸãããããããnameãããããã£ãæå®ããå¿ èŠã¯ãããŸããïŒããšãã°ãQueryTypeã¯ã©ã¹ã®ååã¯Queryã«ãªããŸãïŒã
ããã§ãgraphql.phpã«ãã£ãã«ãŒãã¯ãšãªããŒã¿ã¿ã€ãïŒ
$queryType = new ObjectType([ 'name' => 'Query', 'fields' => [ 'hello' => [ 'type' => Type::string(), 'description' => ' ', 'resolve' => function () { return ', GraphQL!'; } ] ] ]);
å¥ã®QueryType.phpãã¡ã€ã«ã«é 眮ããã次ã®ããã«ãªããŸãã
class QueryType extends ObjectType { public function __construct() { $config = [ 'fields' => [ 'hello' => [ 'type' => Type::string(), 'description' => ' ', 'resolve' => function () { return ', GraphQL!'; } ] ] ]; parent::__construct($config); } }
ãŸããåãå®çŸ©ãããšãã«ããã«ç¡éã®ååž°ãé¿ããããã«ããfieldsãããããã£ã§ãã£ãŒã«ãã®é åã§ã¯ãªãããã£ãŒã«ãã®é åãè¿ãå¿åé¢æ°ãåžžã«ç€ºãããšããå§ãããŸãã
class QueryType extends ObjectType { public function __construct() { $config = [ 'fields' => function() { return [ 'hello' => [ 'type' => Type::string(), 'description' => ' ', 'resolve' => function () { return ', GraphQL!'; } ] ]; } ]; parent::__construct($config); } }
ãããžã§ã¯ããéçºãããšãå€ãã®ããŒã¿åã衚瀺ãããå Žåãããããããããžã§ã¯ãã§äœ¿çšãããåºæ¬çãªããŒã¿åãå«ãããã¹ãŠã®ããŒã¿åã®ãã¡ã¯ããªãšããŠæ©èœããåå¥ã®ã¬ãžã¹ããªãäœæããããšããå§ãããŸãã Appãã©ã«ããŒãšããã®äžã«ãã¡ã€ã«ãTypes.phpãäœæããŸããããããã¯ããã¹ãŠã®ã¿ã€ãã®ãããžã§ã¯ãããŒã¿ã®ãŸãã«ã¬ãžã¹ããªãŒã§ãã
ãŸããAppãã©ã«ããŒã§ãTypeã®ãµããã©ã«ããŒãäœæããŸãããã®ãµããã©ã«ããŒã«ã¯ããã¹ãŠã®ããŒã¿åãä¿åããQueryType.phpã転éããŸãã
ããã§ãåå空éãè¿œå ããTypes.phpã¬ãžã¹ããªã«å¿ èŠãªã¿ã€ããå ¥åããŸãã
App / Types.php
<?php namespace App; use App\Type\QueryType; use GraphQL\Type\Definition\Type; class Types { private static $query; public static function query() { return self::$query ?: (self::$query = new QueryType()); } public static function string() { return Type::string(); } }
ã¬ãžã¹ããªã«ã¯2ã€ã®ããŒã¿åã®ã¿ããããŸãïŒ1ã€ã®åçŽãªïŒæååïŒãš1ã€ã®è€åïŒã¯ãšãªïŒã§ãã
代ããã«ä»ã®ãã¹ãŠã®ãã¡ã€ã«ã§ïŒ
use GraphQL\Type\Definition\Type;
ã¿ã€ãã¬ãžã¹ããªãæ¥ç¶ããŸãã
use App\Types;
ãããŠãåè¿°ã®ãã¹ãŠã®åãã¬ãžã¹ããªã®åã«çœ®ãæããŸãã
代ããã«QueryType.phpã§ïŒ
Type::string()
次ã®ããã«ãªããŸãã
Types::string()
ãããŠãgraphql.phpã®å³ã¯æ¬¡ã®ããã«ãªããŸãã
$schema = new Schema([ 'query' => Types::query() ]);
ããŒã¿ããŒã¹ãããŠãŒã¶ãŒãååŸããã«ã¯ãããã«ã¢ã¯ã»ã¹ããããã®ã€ã³ã¿ãŒãã§ã€ã¹ãæäŸããå¿ èŠããããŸãã ä»»æã®æ¹æ³ã§ããŒã¿ããŒã¹ããããŒã¿ãååŸã§ããŸãã åãã¬ãŒã ã¯ãŒã¯ã«ã¯ããã®ããã®ç¬èªã®ããŒã«ããããŸãã ãã®èšäºã§ã¯ãMySQLããŒã¿ããŒã¹ã«æ¥ç¶ããŠã¯ãšãªãå®è¡ã§ããæãåçŽãªã€ã³ã¿ãŒãã§ã€ã¹ãäœæããŸããã ããã¯GraphQLã«ã¯é©çšãããªãããããã®ã¯ã©ã¹ã§ã¡ãœãããã©ã®ããã«å®è£ ããããã«ã€ããŠã¯èª¬æãããåã«ãã®ã³ãŒãã瀺ããŸãã
ã¢ããª/DB.php
<?php namespace App; class DB { private static $pdo; public static function init($config) { self::$pdo = new PDO("mysql:host={$config['host']};dbname={$config['database']}", $config['username'], $config['password']); self::$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); } public static function selectOne($query) { $records = self::select($query); return array_shift($records); } public static function select($query) { $statement = self::$pdo->query($query); return $statement->fetchAll(); } public static function affectingStatement($query) { $statement = self::$pdo->query($query); return $statement->rowCount(); } }
graphql.phpãã¡ã€ã«ã§ãããŒã¿ããŒã¹ãžã®æ¥ç¶ãåæåããã³ãŒããè¿œå ããŸãã
// $config = [ 'host' => 'localhost', 'database' => 'gql', 'username' => 'root', 'password' => 'root' ]; // DB::init($config);
次ã«ãTypeãã©ã«ããŒã§ããŠãŒã¶ãŒããŒã¿ã衚瀺ãããŠãŒã¶ãŒããŒã¿ã¿ã€ããäœæããŸãã UserType.phpãã¡ã€ã«ã®ã³ãŒãã¯æ¬¡ã®ããã«ãªããŸãã
ã¢ããª/ã¿ã€ã/ UserType.php
<?php namespace App\Type; use App\DB; use App\Types; use GraphQL\Type\Definition\ObjectType; class UserType extends ObjectType { public function __construct() { $config = [ 'description' => '', 'fields' => function() { return [ 'id' => [ 'type' => Types::string(), 'description' => ' ' ], 'name' => [ 'type' => Types::string(), 'description' => ' ' ], 'email' => [ 'type' => Types::string(), 'description' => 'E-mail ' ], 'friends' => [ 'type' => Types::listOf(Types::user()), 'description' => ' ', 'resolve' => function ($root) { return DB::select("SELECT u.* from friendships f JOIN users u ON u.id = f.friend_id WHERE f.user_id = {$root->id}"); } ], 'countFriends' => [ 'type' => Types::int(), 'description' => ' ', 'resolve' => function ($root) { return DB::affectingStatement("SELECT u.* from friendships f JOIN users u ON u.id = f.friend_id WHERE f.user_id = {$root->id}"); } ] ]; } ]; parent::__construct($config); } }
ãã£ãŒã«ãã®æå³ã¯ãã説æãããããã£ããç解ã§ããŸãã ããããã£ãidãããnameãããemailããããã³ãcountFriendsãã«ã¯åçŽãªã¿ã€ããããããfriendsãããããã£ã¯å人ã®ãªã¹ãã§ã-åããŠãŒã¶ãŒã§ãããããã¿ã€ãããããŸãïŒ
Types::listOf(Types::user())
ãŸãã以åã«äœ¿çšããããšã®ãªãããã€ãã®åºæ¬ã¿ã€ããã¬ãžã¹ããªã«è¿œå ããå¿ èŠããããŸãã
public static function int() { return Type::int(); } public static function listOf($type) { return Type::listOf($type); }
ãããŠããŠãŒã¶ãŒã¿ã€ããäœæããŸããã
private static $user; public static function user() { return self::$user ?: (self::$user = new UserType()); }
ãfriendsãããã³ãcountFriendsãããããã£ã®æ»ãå€ïŒè§£æ±ºïŒã¯ãããŒã¿ããŒã¹ããååŸãããŸãã â resolveâã®å¿åé¢æ°ã¯ãçŸåšã®ãã£ãŒã«ãïŒ$ã«ãŒãïŒã®å€ãæåã®åŒæ°ãšããŠåãåããŸãããã®åŒæ°ããããã¬ã³ããªã¹ããªã¯ãšã¹ãã«æ¿å ¥ãããŠãŒã¶ãŒIDãèŠã€ããããšãã§ããŸãã
ã泚æ
ãã®ã³ãŒãã¯ãã¬ãŒãã³ã°ãç®çãšããŠäœæãããããããªã¯ãšã¹ãã®ãšã¹ã±ãŒãã«ã¯æ³šæãæããŸããã§ãããããã¡ããå®éã®ãããžã§ã¯ãã§ãããè¡ãããšã¯ã§ããŸããã
æåŸã«ãQueryType.phpã³ãŒããå€æŽããŠãç¹å®ã®ãŠãŒã¶ãŒã«é¢ããæ å ±ãèå¥åã§ååŸããããã®ãã£ãŒã«ãïŒãuserããã£ãŒã«ãïŒãšãã¹ãŠã®ãŠãŒã¶ãŒããªã¹ãããããã®ãã£ãŒã«ãïŒãallUsersããã£ãŒã«ãïŒãAPIã«è¿œå ããŸãã
ã¢ããª/ã¿ã€ã/ QueryType.php
<?php namespace App\Type; use App\DB; use App\Types; use GraphQL\Type\Definition\ObjectType; class QueryType extends ObjectType { public function __construct() { $config = [ 'fields' => function() { return [ 'user' => [ 'type' => Types::user(), 'description' => ' id', 'args' => [ 'id' => Types::int() ], 'resolve' => function ($root, $args) { return DB::selectOne("SELECT * from users WHERE id = {$args['id']}"); } ], 'allUsers' => [ 'type' => Types::listOf(Types::user()), 'description' => ' ', 'resolve' => function () { return DB::select('SELECT * from users'); } ] ]; } ]; parent::__construct($config); } }
ããã§ã¯ãããŒã¿ãååŸããå¿ èŠããããŠãŒã¶ãŒã®èå¥åãèŠã€ããããã«ãåŒæ°ã®é åãå«ããuserããã£ãŒã«ãã®ãargsãããããã£ã䜿çšããŸãã é åãargsãã¯ã2çªç®ã®åŒæ°ã«ãã£ãŠå¿åé¢æ°ãresolveãã«æž¡ãããŸããããã䜿çšããŠãã¿ãŒã²ãããŠãŒã¶ãŒã®IDãããããŸãã
ã泚æ
åŒæ°ã¯ç¬èªã®ããããã£ãæã€ããšãã§ããŸããããã®å ŽåãåŒæ°ã®é
åãèšè¿°ããããã«ç°¡ç¥åããã圢åŒã䜿çšããŸããé
åã®ããŒã¯ååã§ãå€ã¯åŒæ°ã®ã¿ã€ãã§ãã
代ããã«ïŒ
'args' => [ 'id' => Types::int() ]
代ããã«ïŒ
'args' => [ 'id' => [ 'type' => Types::int() ] ]
ããã§ãGraphQLãµãŒããŒãèµ·åãã次ã®ã¯ãšãªã§ãã®åäœã確èªã§ããŸãã

ãŸãã¯ãã®ããã«ïŒ

ãŸãã¯ãã®ä»ã
ãããã«
以äžã§ãã ããã¥ã¡ã³ããèªãã§ãã ããã ã³ã¡ã³ãã§è³ªåããŠãã ããã æ¹å€ããã
ãŸããGithubã«é¢ããã³ã¡ã³ãä»ãã®ãœãŒã¹ã³ãŒããèªãããšããå§ãããŸã ã
ãã®èšäºã®ä»ã®éšåïŒ
- ã€ã³ã¹ããŒã«ãåè·¯å³ãã¯ãšãª
- çªç¶å€ç°ãå€æ°ãæ€èšŒãå®å šæ§
- N + 1ã¯ãšãªã®åé¡ã解決ãã