계속 지켜보셨다면 제가 개발과 관련하여 일종의 메가 시리즈를 쓰고 있다는 것을 알고 계실 것입니다. GraphQL 및 카우치베이스. 지난 튜토리얼에서 살펴본 내용은 다음과 같습니다. Java에서 GraphQL을 사용하는 방법, Node.js에서 GraphQL을 사용하는 방법및 Golang에서 GraphQL을 사용하는 방법.
이 모든 언어에 대한 콘텐츠를 제작하고 나서 팀원 중 누군가가 PHP가 어디에 있는지 물어왔습니다. 이 질문에서 GraphQL과 PHP를 사용하여 API를 만드는 방법에 관한 이 튜토리얼이 탄생했습니다. 카우치베이스 NoSQL을 데이터 계층.
이 GraphQL PHP 및 NoSQL 튜토리얼에 너무 많은 시간을 투자하기 전에 충족해야 할 몇 가지 가정이 있습니다:
- Couchbase가 이미 설치 및 구성되어 있어야 합니다.
- PHP가 설치 및 구성되어 있어야 합니다.
- PHP 프로젝트 종속성을 다운로드하려면 Composer 및 PECL을 사용할 수 있어야 합니다.
위의 조건이 충족되었다고 가정하면 애플리케이션 개발을 진행할 수 있습니다.
PHP용 GraphQL 및 Couchbase 종속성을 사용하여 새 프로젝트 만들기
프로젝트의 성공을 위한 첫 번째 단계는 프로젝트를 생성하고 필요한 종속 요소를 다운로드하는 것입니다. 컴퓨터의 어딘가에 새 디렉터리를 생성하고 graphql.php 파일을 만듭니다. 이 디렉터리 내에서 명령 프롬프트를 사용하여 다음을 실행합니다:
1 |
작곡가 require 위보닉스/그래프 쿼리-php |
위의 명령은 API의 모든 작업을 수행하는 GraphQL 종속성을 설치합니다. 그런 다음 명령 프롬프트에서 다음 명령을 실행합니다:
1 |
pecl 설치 카우치베이스 |
위의 명령은 Couchbase PHP SDK를 설치합니다. 이전 가정을 반복해서 말하자면, 다음이 필요합니다. 작곡가 그리고 PECL 를 사용할 수 있습니다.
에 대한 자세한 정보 카우치베이스 PHP SDK 및 GraphQL 패키지 에 대한 자세한 내용은 각 문서에서 확인할 수 있습니다.
GraphQL 및 Couchbase 개발을 위한 프로젝트 구성하기
프로젝트를 만들고 종속 요소를 다운로드했으면 이제 애플리케이션 부트스트랩 작업을 시작할 수 있습니다. 프로젝트의 graphql.php 파일을 열고 다음을 포함하세요:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php require_once __DIR__ . '/vendor/autoload.php'; 사용 GraphQL\유형\정의\객체 유형; 사용 GraphQL\유형\정의\유형; 사용 GraphQL\GraphQL; 사용 GraphQL\유형\스키마; $클러스터 = new 카우치베이스클러스터("couchbase://localhost"); 1TP4인증자 = new \카우치베이스\비밀번호 인증기(); 1TP4인증자->사용자 이름('example')->비밀번호('123456'); $클러스터->인증(1TP4인증자); $버킷 = $클러스터->오픈버킷('example'); 헤더('콘텐츠 유형: 애플리케이션/json'); ?> |
위 코드에서는 Composer 종속성을 참조하고 사용하려는 GraphQL 클래스 몇 가지를 포함하고 있습니다. 실제로 GraphQL을 구성하거나 데이터베이스를 사용하지 않고도 데이터베이스에 대한 연결을 설정해야 합니다.
제 PHP 애플리케이션은 Couchbase 인스턴스와 함께 로컬에서 실행되고 있었습니다. 자신의 Couchbase 연결 정보와 관련하여 부족한 부분을 채워야 합니다.
데이터 관리를 위한 GraphQL 개체 정의
이제 API 내에서 GraphQL 객체라고도 하는 데이터 모델을 정의하는 데 집중할 수 있습니다. 이 특정 예제에서는 포켓몬 데이터를 참조하겠습니다. 따라서 특정 포켓몬에 대한 정보, 포켓몬이 어떤 동작을 하는지, 어떤 비디오 게임에서 발견되는지에 대한 정보가 있을 수 있습니다. 이것이 데이터 모델의 기초가 될 것입니다.
다음부터 시작하겠습니다. graphql.php 파일을 사용합니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$이동 유형 = new 객체 유형([ 'name' => '이동', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], 'type' => [ 'type' => 유형::문자열() ], 'power' => [ 'type' => 유형::int() ], ], ]); $포켓몬 유형 = new 객체 유형([ 'name' => '포켓몬', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], 'weight' => [ 'type' => 유형::int() ], 'height' => [ 'type' => 유형::int() ], '속성' => [ 'type' => 유형::listOf(유형::문자열()) ], 'moves' => [ 'type' => 유형::listOf($이동 유형) ], ], ]); |
위 코드에는 필드라고도 하는 가능한 JSON 속성을 가진 두 개의 객체가 있습니다. 하나는 $포켓몬타입
객체에는 다른 객체를 참조하는 필드가 있습니다. 다른 객체를 참조하는 listOf
유형은 데이터 배열이 반환될 것으로 예상할 수 있음을 의미합니다.
우리가 가지고 있는 두 가지 객체는 복잡하지는 않지만 현실적인 예시입니다. 하지만 현재로서는 모델 청사진에 불과합니다. 데이터베이스의 실제 데이터로 채울 수 있어야 합니다.
GraphQL 객체를 채우는 쿼리 개발하기
다음 단계는 데이터베이스에 대해 실행할 쿼리를 만들어 GraphQL 객체를 채우는 것입니다. 이 작업은 쿼리를 통해 이루어지며, 쿼리는 결국 로직을 포함하는 또 다른 GraphQL 객체에 지나지 않습니다.
다음 코드를 예로 들어 보겠습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$쿼리 유형 = new 객체 유형([ 'name' => '쿼리', 'fields' => [ '포켓몬' => [ 'type' => 유형::listOf($포켓몬 유형), 'resolve' => 함수 ($root, $args) { 글로벌 $버킷; $쿼리 = CouchbaseN1qlQuery::fromString("SELECT example.* FROM example WHERE type = 'pokemon'"); $결과 = $버킷->쿼리($쿼리); 반환 $결과->행; } ], '포켓몬' => [ 'type' => $포켓몬 유형, 'args' => [ 'id' => 유형::비Null(유형::문자열()), ], 'resolve' => 함수 ($root, $args) { 글로벌 $버킷; $결과 = $버킷->get("포켓몬-" . $args['id']); 반환 $결과->값; } ], ], ]); |
위의 코드는 결국 여러분의 graphql.php 파일에는 두 가지 가능한 쿼리가 있습니다. 첫 번째 쿼리는 포켓몬
는 $포켓몬타입
모델입니다. 모델 해결
함수는 실제로 로직을 실행하는 함수입니다. 다음에 대한 로직은 포켓몬
는 Couchbase N1QL 쿼리를 실행하고 결과를 반환합니다. N1QL 쿼리의 결과는 GraphQL 객체에 정의된 각 필드에 매핑됩니다.
실행하기 포켓몬
쿼리는 다음과 같이 보일 수 있습니다:
1 |
curl http://localhost:8080 -d '{"쿼리": "쿼리 { 포켓몬{ 이름, 키, 속성 } }" }' |
그리고 포켓몬
쿼리는 비슷하지만 동일하지는 않습니다. 쿼리에서 포켓몬
쿼리에는 id
를 전달합니다. 이 id
값은 NoSQL 쿼리가 아닌 특정 NoSQL 문서를 직접 조회하는 데 사용됩니다.
그리고 포켓몬
쿼리는 다음과 같이 보일 수 있습니다:
1 |
curl http://localhost:8080 -d '{"query": "쿼리 { 포켓몬(id: \"25\"){ 이름, 높이, 속성 } }" }' |
이 쿼리의 결과는 배열이 아닌 단일 객체가 됩니다.
단순한 방식으로 모델 관계 처리하기
이제 어떤 종류의 데이터 관계를 원한다고 가정해 봅시다. 지금까지는 이동 데이터와 포켓몬 데이터가 같은 문서에 존재한다고 가정했지만, 분리되어 있지만 서로 연관된 데이터가 있다면 어떨까요?
여기에는 두 가지 접근 방식이 있습니다:
- JOIN 쿼리를 수행하면 관리하기 더 복잡한 쿼리를 만들 수 있습니다.
- 필드별로 쿼리를 모듈화하여 작고 매끄럽게 유지할 수 있습니다.
제 팟캐스트를 들어보신 적이 있으신가요? API 개발을 위한 GraphQL 에 GraphQL 공동창시자 Lee Byron이 출연한 것을 보면 어느 쪽도 틀린 것이 아니며 선호도에 따라 결정된다는 것을 알 수 있습니다. 쿼리와 GraphQL 객체를 모듈화하는 후자를 살펴보겠습니다.
이제 게임 데이터를 포함시키고자 하는데 게임 데이터가 이와 같은 별도의 객체라고 가정해 보겠습니다:
1 2 3 4 5 6 |
$게임 유형 = new 객체 유형([ 'name' => '게임', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], ], ]); |
이제 데이터베이스의 포켓몬 문서에 게임 ID는 있지만 실제 데이터는 포함되어 있지 않다고 가정해 보겠습니다. 기본 키와 외래 키 관계와 비슷하지만, 이것은 NoSQL이기 때문에 실제로는 그렇지 않습니다.
지금 우리가 할 수 있는 일은 다음과 같습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$포켓몬 유형 = new 객체 유형([ 'name' => '포켓몬', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], 'weight' => [ 'type' => 유형::int() ], 'height' => [ 'type' => 유형::int() ], '속성' => [ 'type' => 유형::listOf(유형::문자열()) ], 'moves' => [ 'type' => 유형::listOf($이동 유형) ], 'game' => [ 'type' => $게임 유형, 'resolve' => 함수 ($root, $args) { 글로벌 $버킷; $결과 = $버킷->get($root->게임); 반환 $결과->값; } ], ], ]); |
저희의 $포켓몬타입
를 사용하여 게임 필드를 포함할 수 있지만 이 필드에는 고유한 해결
함수입니다. 이 내부 해결
함수에 액세스하여 부모 또는 $root
데이터에 게임 ID가 포함되어 있기 때문입니다. 포켓몬
그리고 포켓몬
쿼리가 제공했습니다. ID를 사용하여 별도의 문서에 존재하는 게임 데이터를 가져와서 반환할 수 있습니다.
이제 우리의 오리지널 포켓몬
쿼리는 JOIN 문에서 제외됩니다. JOIN 문이 나쁘다는 말은 아니지만, 50줄의 쿼리가 필요한 거친 데이터 모델이 있다고 상상해 보세요. 이러한 시나리오에서는 대규모 쿼리를 유지하려고 노력하기보다는 쿼리를 분할하는 것이 합리적일 수 있습니다. 재미있는 사실로, 데이터베이스의 게임 연산은 게임 필드가 요청되지 않는 한 호출되지 않습니다. 그래프QL 쿼리 프런트엔드에서
애플리케이션 통합
이제 애플리케이션을 통합할 차례입니다. 지금까지는 데이터 모델과 쿼리만 보았지만 GraphQL과 함께 사용하도록 구성하지 않았습니다.
이제 완성된 코드를 살펴보세요:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
<?php require_once __DIR__ . '/vendor/autoload.php'; 사용 GraphQL\유형\정의\객체 유형; 사용 GraphQL\유형\정의\유형; 사용 GraphQL\GraphQL; 사용 GraphQL\유형\스키마; $클러스터 = new 카우치베이스클러스터("couchbase://localhost"); 1TP4인증자 = new \카우치베이스\비밀번호 인증기(); 1TP4인증자->사용자 이름('example')->비밀번호('123456'); $클러스터->인증(1TP4인증자); $버킷 = $클러스터->오픈버킷('example'); $게임 유형 = new 객체 유형([ 'name' => '게임', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], ], ]); $이동 유형 = new 객체 유형([ 'name' => '이동', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], 'type' => [ 'type' => 유형::문자열() ], 'power' => [ 'type' => 유형::int() ], ], ]); $포켓몬타입 = new 객체 유형([ 'name' => '포켓몬', 'fields' => [ 'name' => [ 'type' => 유형::문자열() ], 'weight' => [ 'type' => 유형::int() ], 'height' => [ 'type' => 유형::int() ], '속성' => [ 'type' => 유형::listOf(유형::문자열()) ], 'moves' => [ 'type' => 유형::listOf($이동 유형) ], 'game' => [ 'type' => $게임 유형, 'resolve' => 함수 ($root, $args) { 글로벌 $버킷; 1TP4결과 = $버킷->get($root->게임); 반환 1TP4결과->값; } ], ], ]); $쿼리 유형 = new 객체 유형([ 'name' => '쿼리', 'fields' => [ '포켓몬' => [ 'type' => 유형::listOf($포켓몬타입), 'resolve' => 함수 ($root, $args) { 글로벌 $버킷; $쿼리 = CouchbaseN1qlQuery::fromString("SELECT example.* FROM example WHERE type = 'pokemon'"); 1TP4결과 = $버킷->쿼리($쿼리); 반환 1TP4결과->행; } ], '포켓몬' => [ 'type' => $포켓몬타입, 'args' => [ 'id' => 유형::비Null(유형::문자열()), ], 'resolve' => 함수 ($root, $args) { 글로벌 $버킷; 1TP4결과 = $버킷->get("포켓몬-" . $args['id']); 반환 1TP4결과->값; } ], ], ]); $schema = new 스키마([ 'query' => $쿼리 유형 ]); $rawInput = file_get_contents('php://input'); 1TP4입력 = json_decode($rawInput, true); $쿼리 = 1TP4입력['query']; $변수값 = 발행(1TP4입력['변수']) ? 1TP4입력['변수'] : null; 시도 { 1TP4결과 = GraphQL::실행 쿼리($schema, $쿼리, null, null, $변수값); 1TP4출력 = 1TP4결과->toArray(); } catch (\예외 $e) { 1TP4출력 = [ 'errors' => [ [ '메시지' => $e->getMessage() ] ] ]; } 헤더('콘텐츠 유형: 애플리케이션/json'); echo json_encode(1TP4출력); ?> |
코드의 아래쪽 절반은 PHP GraphQL 서버 공식 문서. 기본적으로 쿼리를 스키마로 정의하고 최종 사용자로부터 모든 입력을 받습니다. 이 입력은 스키마와 결합하여 사용자에게 반환되는 결과를 생성하는 데 사용됩니다.
결론
방금 PHP로 GraphQL API를 만드는 방법과 카우치베이스 를 NoSQL 데이터베이스로 사용할 수 있습니다. GraphQL은 프론트엔드를 통해 쿼리할 수 있는 데이터 모델을 적절히 유지 관리할 수 있으므로 RESTful API 작성에 대한 훌륭한 대안이 될 수 있습니다. 따라서 HTTP 요청의 양과 모든 요청에서 반환되는 페이로드가 줄어듭니다.
PHP와 함께 Couchbase를 사용하는 방법에 대해 자세히 알아보려면 제가 이전에 작성한 튜토리얼을 참조하세요, 카우치베이스 서버와 PHP를 사용하여 NoSQL 시작하기.