Клиент GraphQL, написанный на PHP, который предоставляет очень простые, но мощные классы генератора запросов, которые делают процесс взаимодействия с сервером GraphQL очень простым.
Существует 3 основных способа использования этого пакета для генерации запросов GraphQL:
Query
. Его конструкция предназначена для использования в случаях, когда запрос строится динамически.Запустите следующую команду, чтобы установить пакет с помощью композитора:
$ composer require gmostafa/php-graphql-client
Чтобы избежать хлопот, связанных с написанием каких-либо запросов и просто взаимодействовать с объектами PHP, сгенерированными из вашей схемы API, посетите репозиторий PHP GraphQL OQM.
$ gql = ( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Этот простой запрос позволит получить все компании, отображающие их названия и серийные номера.
Запрос, представленный в предыдущем примере, представлен в «сокращенной форме». Сокращенная форма предполагает написание уменьшенного количества строк кода, что ускоряет процесс написания запросов. Ниже приведен пример полной формы для того же запроса, написанного в предыдущем примере.
$ gql = ( new Query ())
-> setSelectionSet (
[
( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
)
]
);
Как видно из примера, сокращенную форму легче читать и писать, ее обычно предпочтительнее использовать по сравнению с полной формой.
Полную форму не следует использовать, если запрос не может быть представлен в сокращенной форме, которая имеет только один случай, когда мы хотим выполнить несколько запросов в одном и том же объекте.
$ gql = ( new Query ())
-> setSelectionSet (
[
( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
),
( new Query ( ' countries ' ))
-> setSelectionSet (
[
' name ' ,
' code ' ,
]
)
]
);
Этот запрос извлекает все компании и страны, отображая некоторые поля данных для каждой. По сути, он выполняет два (или более, если необходимо) независимых запроса в одном конверте объекта запроса.
Для написания нескольких запросов необходимо написать объект запроса в полной форме, чтобы представить каждый запрос как подполе родительского объекта запроса.
$ gql = ( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber ' ,
( new Query ( ' branches ' ))
-> setSelectionSet (
[
' address ' ,
( new Query ( ' contracts ' ))
-> setSelectionSet ([ ' date ' ])
]
)
]
);
Этот запрос более сложный: он извлекает не только скалярные поля, но и поля объектов. Этот запрос возвращает все компании, отображая их названия, серийные номера, а также для каждой компании, все ее филиалы, отображая адрес филиала, а для каждого адреса он извлекает все контракты, привязанные к этому адресу, отображая их даты.
$ gql = ( new Query ( ' companies ' ))
-> setArguments ([ ' name ' => ' Tech Co. ' , ' first ' => 3 ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Этот запрос не извлекает все компании путем добавления аргументов. Этот запрос вернет первые три компании с названием «Tech Co.», отображая их названия и серийные номера.
$ gql = ( new Query ( ' companies ' ))
-> setArguments ([ ' serialNumbers ' => [ 159 , 260 , 371 ]])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Этот запрос является частным случаем запроса аргументов. В этом примере запрос получит только компании с серийным номером в одном из 159, 260 и 371, отображая название и серийный номер.
$ gql = ( new Query ( ' companies ' ))
-> setArguments ([ ' filter ' => new RawObject ( ' {name_starts_with: "Face"} ' )])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Этот запрос является еще одним частным случаем запроса аргументов. В этом примере мы устанавливаем пользовательский объект ввода «фильтр» с некоторыми значениями, чтобы ограничить возвращаемые компании. Мы устанавливаем фильтр «name_starts_with» со значением «Лицо». Этот запрос вернет только те компании, названия которых начинаются с фразы «Face».
Создаваемый класс RawObject используется для внедрения строки в запрос в том виде, в каком она есть. Какая бы строка ни вводилась в конструктор RawObject, она будет помещена в запрос в том виде, в каком она есть, без какого-либо специального форматирования, обычно выполняемого классом запроса.
$ gql = ( new Query ( ' companies ' ))
-> setVariables (
[
new Variable ( ' name ' , ' String ' , true ),
new Variable ( ' limit ' , ' Int ' , false , 5 )
]
)
-> setArguments ([ ' name ' => ' $name ' , ' first ' => ' $limit ' ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Этот запрос показывает, как переменные можно использовать в этом пакете для обеспечения динамических запросов, предусмотренных стандартами GraphQL.
Класс Variable — это неизменяемый класс, который представляет переменную в стандартах GraphQL. Его конструктор получает 4 аргумента:
$ gql = ( new Query ())
-> setSelectionSet (
[
( new Query ( ' companies ' , ' TechCo ' ))
-> setArguments ([ ' name ' => ' Tech Co. ' ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
),
( new Query ( ' companies ' , ' AnotherTechCo ' ))
-> setArguments ([ ' name ' => ' A.N. Other Tech Co. ' ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
)
]
);
Псевдоним можно установить во втором аргументе конструктора Query в тех случаях, когда один и тот же объект необходимо получить несколько раз с разными аргументами.
$ gql = ( new Query ( ' companies ' ))
-> setAlias ( ' CompanyAlias ' )
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Псевдоним также можно установить с помощью метода установки.
При запросе поля, возвращающего тип интерфейса, вам может потребоваться использовать встроенные фрагменты для доступа к данным базового конкретного типа.
В этом примере показано, как генерировать встроенные фрагменты с помощью этого пакета:
$ gql = new Query ( ' companies ' );
$ gql -> setSelectionSet (
[
' serialNumber ' ,
' name ' ,
( new InlineFragment ( ' PrivateCompany ' ))
-> setSelectionSet (
[
' boardMembers ' ,
' shareholders ' ,
]
),
]
);
Класс QueryBuilder можно использовать для динамического создания объектов Query, что может быть полезно в некоторых случаях. Он работает очень похоже на класс Query, но построение Query разделено на этапы.
Вот как можно создать пример «Запрос с аргументом входного объекта» с помощью QueryBuilder:
$ builder = ( new QueryBuilder ( ' companies ' ))
-> setVariable ( ' namePrefix ' , ' String ' , true )
-> setArgument ( ' filter ' , new RawObject ( ' {name_starts_with: $namePrefix} ' ))
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' );
$ gql = $ builder -> getQuery ();
Как и в случае с классом Query, псевдоним можно установить с помощью второго аргумента конструктора.
$ builder = ( new QueryBuilder ( ' companies ' , ' CompanyAlias ' ))
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' );
$ gql = $ builder -> getQuery ();
Или через метод установки
$ builder = ( new QueryBuilder ( ' companies ' ))
-> setAlias ( ' CompanyAlias ' )
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' );
$ gql = $ builder -> getQuery ();
Как и класс Query, класс QueryBuilder можно написать в полной форме, чтобы можно было писать несколько запросов в одном объекте построителя запросов. Ниже приведен пример использования полной формы с QueryBuilder:
$ builder = ( new QueryBuilder ())
-> setVariable ( ' namePrefix ' , ' String ' , true )
-> selectField (
( new QueryBuilder ( ' companies ' ))
-> setArgument ( ' filter ' , new RawObject ( ' {name_starts_with: $namePrefix} ' ))
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' )
)
-> selectField (
( new QueryBuilder ( ' company ' ))
-> setArgument ( ' serialNumber ' , 123 )
-> selectField ( ' name ' )
);
$ gql = $ builder -> getQuery ();
Этот запрос является расширением запроса из предыдущего примера. Он возвращает все компании, начинающиеся с префикса имени, и возвращает компанию со значением serialNumber
123, оба в одном ответе.
Объект Client можно легко создать, указав URL-адрес конечной точки GraphQL.
Конструктор клиента также получает дополнительный массив «authorizationHeaders», который можно использовать для добавления заголовков авторизации ко всем запросам, отправляемым на сервер GraphQL.
Пример:
$ client = new Client (
' http://api.graphql.com ' ,
[ ' Authorization ' => ' Basic xyz ' ]
);
Конструктор клиента также получает дополнительный массив «httpOptions», который переопределяет «authorizationHeaders» и может использоваться для добавления пользовательских параметров запроса HTTP-клиента Guzzle.
Пример:
$ client = new Client (
' http://api.graphql.com ' ,
[],
[
' connect_timeout ' => 5 ,
' timeout ' => 5 ,
' headers ' => [
' Authorization ' => ' Basic xyz '
'User-Agent' => ' testing/1.0 ' ,
],
' proxy ' => [
' http ' => ' tcp://localhost:8125 ' , // Use this proxy with "http"
' https ' => ' tcp://localhost:9124 ' , // Use this proxy with "https",
' no ' => [ ' .mit.edu ' , ' foo.com ' ] // Don't use a proxy with these
],
' cert ' => [ ' /path/server.pem ' , ' password ' ]
. . .
]
);
Можно использовать собственный предварительно настроенный HTTP-клиент, реализующий интерфейс PSR-18.
Пример:
$ client = new Client (
' http://api.graphql.com ' ,
[],
[],
$ myHttpClient
);
Запуск запроса с помощью клиента GraphQL и получение результатов в структуре объекта:
$ results = $ client -> runQuery ( $ gql );
$ results -> getData ()-> companies [ 0 ]-> branches ;
Или получение результатов в структуре массива:
$ results = $ client -> runQuery ( $ gql , true );
$ results -> getData ()[ ' companies ' ][ 1 ][ ' branches ' ][ ' address ' ];
Выполнение запросов, содержащих переменные, требует передачи в метод runQuery
ассоциативного массива, который сопоставляет имена переменных (ключи) со значениями переменных (значениями). Вот пример:
$ gql = ( new Query ( ' companies ' ))
-> setVariables (
[
new Variable ( ' name ' , ' String ' , true ),
new Variable ( ' limit ' , ' Int ' , false , 5 )
]
)
-> setArguments ([ ' name ' => ' $name ' , ' first ' => ' $limit ' ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
$ variablesArray = [ ' name ' => ' Tech Co. ' , ' first ' => 5 ];
$ results = $ client -> runQuery ( $ gql , true , $ variablesArray );
Мутации следуют тем же правилам запросов в GraphQL: они выбирают поля возвращаемых объектов, получают аргументы и могут иметь подполя.
Вот пример того, как создавать и запускать мутации:
$ mutation = ( new Mutation ( ' createCompany ' ))
-> setArguments ([ ' companyObject ' => new RawObject ( ' {name: "Trial Company", employees: 200} ' )])
-> setSelectionSet (
[
' _id ' ,
' name ' ,
' serialNumber ' ,
]
);
$ results = $ client -> runQuery ( $ mutation );
Мутации могут выполняться клиентом так же, как выполняются запросы.
Мутации могут использовать переменные так же, как запросы. Вот пример использования переменных для динамической передачи входных объектов на сервер GraphQL:
$ mutation = ( new Mutation ( ' createCompany ' ))
-> setVariables ([ new Variable ( ' company ' , ' CompanyInputObject ' , true )])
-> setArguments ([ ' companyObject ' => ' $company ' ]);
$ variables = [ ' company ' => [ ' name ' => ' Tech Company ' , ' type ' => ' Testing ' , ' size ' => ' Medium ' ]];
$ client -> runQuery (
$ mutation , true , $ variables
);
Это полученная мутация и переданные вместе с ней переменные:
mutation( $ company : CompanyInputObject!) {
createCompany (companyObject: $ company )
}
{"company":{"name":"Tech Company", " type " :"Testing", " size " :"Medium"}}
GraphQL Pokemon — это очень крутой общедоступный API GraphQL, доступный для получения данных о покемонах. API доступен публично в Интернете, мы будем использовать его для демонстрации возможностей этого клиента.
Ссылка на репозиторий Github: https://github.com/lucasbento/graphql-pokemon
Ссылка на API: https://graphql-pokemon.now.sh/
Этот запрос извлекает эволюцию любых покемонов и их атаки:
query( $ name : String!) {
pokemon (name: $ name ) {
id
number
name
evolutions {
id
number
name
weight {
minimum
maximum
}
attacks {
fast {
name
type
damage
}
}
}
}
}
Вот как этот запрос можно написать с использованием класса запроса и запустить с помощью клиента:
$ client = new Client (
' https://graphql-pokemon.now.sh/ '
);
$ gql = ( new Query ( ' pokemon ' ))
-> setVariables ([ new Variable ( ' name ' , ' String ' , true )])
-> setArguments ([ ' name ' => ' $name ' ])
-> setSelectionSet (
[
' id ' ,
' number ' ,
' name ' ,
( new Query ( ' evolutions ' ))
-> setSelectionSet (
[
' id ' ,
' number ' ,
' name ' ,
( new Query ( ' attacks ' ))
-> setSelectionSet (
[
( new Query ( ' fast ' ))
-> setSelectionSet (
[
' name ' ,
' type ' ,
' damage ' ,
]
)
]
)
]
)
]
);
try {
$ name = readline ( ' Enter pokemon name: ' );
$ results = $ client -> runQuery ( $ gql , true , [ ' name ' => $ name ]);
}
catch ( QueryError $ exception ) {
print_r ( $ exception -> getErrorDetails ());
exit ;
}
print_r ( $ results -> getData ()[ ' pokemon ' ]);
Или, альтернативно, вот как этот запрос можно сгенерировать с помощью класса QueryBuilder:
$ client = new Client (
' https://graphql-pokemon.now.sh/ '
);
$ builder = ( new QueryBuilder ( ' pokemon ' ))
-> setVariable ( ' name ' , ' String ' , true )
-> setArgument ( ' name ' , ' $name ' )
-> selectField ( ' id ' )
-> selectField ( ' number ' )
-> selectField ( ' name ' )
-> selectField (
( new QueryBuilder ( ' evolutions ' ))
-> selectField ( ' id ' )
-> selectField ( ' name ' )
-> selectField ( ' number ' )
-> selectField (
( new QueryBuilder ( ' attacks ' ))
-> selectField (
( new QueryBuilder ( ' fast ' ))
-> selectField ( ' name ' )
-> selectField ( ' type ' )
-> selectField ( ' damage ' )
)
)
);
try {
$ name = readline ( ' Enter pokemon name: ' );
$ results = $ client -> runQuery ( $ builder , true , [ ' name ' => $ name ]);
}
catch ( QueryError $ exception ) {
print_r ( $ exception -> getErrorDetails ());
exit ;
}
print_r ( $ results -> getData ()[ ' pokemon ' ]);
Хотя это и не является основной целью этого пакета, он поддерживает выполнение запросов необработанных строк, как и любой другой клиент, использующий метод runRawQuery
в классе Client
. Вот пример того, как его использовать:
$ gql = <<<QUERY
query {
pokemon(name: "Pikachu") {
id
number
name
attacks {
special {
name
type
damage
}
}
}
}
QUERY ;
$ results = $ client -> runRawQuery ( $ gql );