GraphQL APIはPHPずMySQLで䜜成したす。 パヌト1むンストヌル、図、ク゚リ

画像



最近、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 」をむンストヌルしお、ク゚リを実行したす。



GraphQLのテスト



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サヌバヌを起動し、次のク゚リでその動䜜を確認できたす。



GraphQLの怜蚌ナヌザヌデヌタの取埗



たたはこのように



GraphQLのテストナヌザヌデヌタの取埗



たたはその他。



おわりに



以䞊です。 ドキュメントを読んでください。 コメントで質問しおください。 批刀する。



たた、Githubに関するコメント付きの゜ヌスコヌドを読むこずをお勧めしたす 。



この蚘事の他の郚分

  1. むンストヌル、回路図、ク゚リ
  2. 突然倉異、倉数、怜蚌、安党性
  3. N + 1ク゚リの問題を解決する



All Articles