Un cliente GraphQL escrito en PHP que proporciona clases generadoras de consultas muy simples pero poderosas que hacen que el proceso de interacción con un servidor GraphQL sea muy simple.
Hay 3 formas principales de utilizar este paquete para generar sus consultas GraphQL:
Query
de forma dinámica. Está diseñado para usarse en casos en los que una consulta se construye de forma dinámica.Ejecute el siguiente comando para instalar el paquete usando Composer:
$ composer require gmostafa/php-graphql-client
Para evitar la molestia de tener que escribir consultas y simplemente interactuar con objetos PHP generados a partir de su esquema API, visite el repositorio PHP GraphQL OQM.
$ gql = ( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Esta sencilla consulta recuperará todas las empresas que muestran sus nombres y números de serie.
La consulta proporcionada en el ejemplo anterior se representa en forma abreviada. La forma abreviada implica escribir un número reducido de líneas de código, lo que acelera el proceso de redacción de consultas. A continuación se muestra un ejemplo del formulario completo para exactamente la misma consulta escrita en el ejemplo anterior.
$ gql = ( new Query ())
-> setSelectionSet (
[
( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
)
]
);
Como se ve en el ejemplo, la forma abreviada es más sencilla de leer y escribir; generalmente se prefiere usarla en comparación con la forma completa.
El formulario completo no debe usarse a menos que la consulta no se pueda representar en el formulario abreviado, que tiene solo un caso, cuando queremos ejecutar múltiples consultas en el mismo objeto.
$ gql = ( new Query ())
-> setSelectionSet (
[
( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
),
( new Query ( ' countries ' ))
-> setSelectionSet (
[
' name ' ,
' code ' ,
]
)
]
);
Esta consulta recupera todas las empresas y países y muestra algunos campos de datos para cada uno. Básicamente, ejecuta dos (o más si es necesario) consultas independientes en un sobre de objeto de consulta.
Escribir varias consultas requiere escribir el objeto de consulta en formato completo para representar cada consulta como un subcampo debajo del objeto de consulta principal.
$ gql = ( new Query ( ' companies ' ))
-> setSelectionSet (
[
' name ' ,
' serialNumber ' ,
( new Query ( ' branches ' ))
-> setSelectionSet (
[
' address ' ,
( new Query ( ' contracts ' ))
-> setSelectionSet ([ ' date ' ])
]
)
]
);
Esta consulta es más compleja y recupera no solo campos escalares, sino también campos de objetos. Esta consulta devuelve todas las empresas, muestra sus nombres, números de serie y, para cada empresa, todas sus sucursales, muestra la dirección de la sucursal, y para cada dirección, recupera todos los contratos vinculados a esta dirección, mostrando sus fechas.
$ gql = ( new Query ( ' companies ' ))
-> setArguments ([ ' name ' => ' Tech Co. ' , ' first ' => 3 ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Esta consulta no recupera todas las empresas agregando argumentos. Esta consulta recuperará las primeras 3 empresas con el nombre "Tech Co.", mostrando sus nombres y números de serie.
$ gql = ( new Query ( ' companies ' ))
-> setArguments ([ ' serialNumbers ' => [ 159 , 260 , 371 ]])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Esta consulta es un caso especial de la consulta de argumentos. En este ejemplo, la consulta recuperará solo las empresas con un número de serie entre 159, 260 y 371, mostrando el nombre y el número de serie.
$ gql = ( new Query ( ' companies ' ))
-> setArguments ([ ' filter ' => new RawObject ( ' {name_starts_with: "Face"} ' )])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Esta consulta es otro caso especial de la consulta de argumentos. En este ejemplo, configuramos un "filtro" de objeto de entrada personalizado con algunos valores para limitar las empresas que se devuelven. Estamos configurando el filtro "name_starts_with" con el valor "Cara". Esta consulta recuperará sólo las empresas cuyos nombres comiencen con la frase "Face".
La clase RawObject que se está construyendo se utiliza para inyectar la cadena en la consulta tal como está. Cualquier cadena que se ingrese en el constructor RawObject se colocará en la consulta tal como está, sin ningún formato personalizado que normalmente realiza la clase de consulta.
$ gql = ( new Query ( ' companies ' ))
-> setVariables (
[
new Variable ( ' name ' , ' String ' , true ),
new Variable ( ' limit ' , ' Int ' , false , 5 )
]
)
-> setArguments ([ ' name ' => ' $name ' , ' first ' => ' $limit ' ])
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
Esta consulta muestra cómo se pueden usar variables en este paquete para permitir solicitudes dinámicas habilitadas por los estándares GraphQL.
La clase Variable es una clase inmutable que representa una variable en los estándares GraphQL. Su constructor recibe 4 argumentos:
$ 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 '
]
)
]
);
Se puede establecer un alias en el segundo argumento del constructor de consultas para ocasiones en las que es necesario recuperar el mismo objeto varias veces con diferentes argumentos.
$ gql = ( new Query ( ' companies ' ))
-> setAlias ( ' CompanyAlias ' )
-> setSelectionSet (
[
' name ' ,
' serialNumber '
]
);
El alias también se puede configurar mediante el método setter.
Al consultar un campo que devuelve un tipo de interfaz, es posible que necesite utilizar fragmentos en línea para acceder a datos sobre el tipo concreto subyacente.
Este ejemplo muestra cómo generar fragmentos en línea usando este paquete:
$ gql = new Query ( ' companies ' );
$ gql -> setSelectionSet (
[
' serialNumber ' ,
' name ' ,
( new InlineFragment ( ' PrivateCompany ' ))
-> setSelectionSet (
[
' boardMembers ' ,
' shareholders ' ,
]
),
]
);
La clase QueryBuilder se puede utilizar para construir objetos Query de forma dinámica, lo que puede resultar útil en algunos casos. Funciona de manera muy similar a la clase Query, pero el edificio Query está dividido en pasos.
Así es como se puede crear el ejemplo "Consulta con argumento de objeto de entrada" utilizando QueryBuilder:
$ builder = ( new QueryBuilder ( ' companies ' ))
-> setVariable ( ' namePrefix ' , ' String ' , true )
-> setArgument ( ' filter ' , new RawObject ( ' {name_starts_with: $namePrefix} ' ))
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' );
$ gql = $ builder -> getQuery ();
Al igual que con la clase Query, se puede establecer un alias utilizando el segundo argumento del constructor.
$ builder = ( new QueryBuilder ( ' companies ' , ' CompanyAlias ' ))
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' );
$ gql = $ builder -> getQuery ();
O mediante el método de establecimiento
$ builder = ( new QueryBuilder ( ' companies ' ))
-> setAlias ( ' CompanyAlias ' )
-> selectField ( ' name ' )
-> selectField ( ' serialNumber ' );
$ gql = $ builder -> getQuery ();
Al igual que la clase Query, la clase QueryBuilder se puede escribir en formato completo para permitir escribir varias consultas en un objeto de creación de consultas. A continuación se muestra un ejemplo de cómo se puede utilizar el formulario completo con 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 ();
Esta consulta es una extensión de la consulta del ejemplo anterior. Devuelve todas las empresas que comienzan con un prefijo de nombre y devuelve la empresa con el serialNumber
de valor 123, ambas en la misma respuesta.
Se puede crear una instancia de un objeto Cliente fácilmente proporcionando la URL del punto final GraphQL.
El constructor del Cliente también recibe una matriz opcional "authorizationHeaders", que se puede usar para agregar encabezados de autorización a todas las solicitudes que se envían al servidor GraphQL.
Ejemplo:
$ client = new Client (
' http://api.graphql.com ' ,
[ ' Authorization ' => ' Basic xyz ' ]
);
El constructor del Cliente también recibe una matriz opcional "httpOptions", que anula los "authorizationHeaders" y se puede utilizar para agregar opciones personalizadas de solicitud del Cliente HTTP Guzzle.
Ejemplo:
$ 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 ' ]
. . .
]
);
Es posible utilizar su propio cliente HTTP preconfigurado que implemente la interfaz PSR-18.
Ejemplo:
$ client = new Client (
' http://api.graphql.com ' ,
[],
[],
$ myHttpClient
);
Ejecutar consulta con el cliente GraphQL y obtener los resultados en la estructura del objeto:
$ results = $ client -> runQuery ( $ gql );
$ results -> getData ()-> companies [ 0 ]-> branches ;
O obtener resultados en estructura de matriz:
$ results = $ client -> runQuery ( $ gql , true );
$ results -> getData ()[ ' companies ' ][ 1 ][ ' branches ' ][ ' address ' ];
La ejecución de consultas que contienen variables requiere pasar una matriz asociativa que asigna nombres de variables (claves) a valores de variables (valores) al método runQuery
. He aquí un ejemplo:
$ 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 );
Las mutaciones siguen las mismas reglas de las consultas en GraphQL, seleccionan campos en los objetos devueltos, reciben argumentos y pueden tener subcampos.
Aquí hay un ejemplo de muestra sobre cómo construir y ejecutar mutaciones:
$ mutation = ( new Mutation ( ' createCompany ' ))
-> setArguments ([ ' companyObject ' => new RawObject ( ' {name: "Trial Company", employees: 200} ' )])
-> setSelectionSet (
[
' _id ' ,
' name ' ,
' serialNumber ' ,
]
);
$ results = $ client -> runQuery ( $ mutation );
El cliente puede ejecutar las mutaciones de la misma manera que se ejecutan las consultas.
Las mutaciones pueden utilizar las variables de la misma manera que las consultas. Aquí hay un ejemplo sobre cómo usar las variables para pasar objetos de entrada al servidor GraphQL dinámicamente:
$ 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
);
Estas son la mutación resultante y las variables que se pasan con ella:
mutation( $ company : CompanyInputObject!) {
createCompany (companyObject: $ company )
}
{"company":{"name":"Tech Company", " type " :"Testing", " size " :"Medium"}}
GraphQL Pokemon es una API GraphQL pública muy interesante disponible para recuperar datos de Pokemon. La API está disponible públicamente en la web; la usaremos para demostrar las capacidades de este cliente.
Enlace al repositorio de Github: https://github.com/lucasbento/graphql-pokemon
Enlace API: https://graphql-pokemon.now.sh/
Esta consulta recupera las evoluciones de cualquier Pokémon y sus ataques:
query( $ name : String!) {
pokemon (name: $ name ) {
id
number
name
evolutions {
id
number
name
weight {
minimum
maximum
}
attacks {
fast {
name
type
damage
}
}
}
}
}
Así es como esta consulta se puede escribir usando la clase de consulta y ejecutar usando el cliente:
$ 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 ' ]);
O alternativamente, así es como se puede generar esta consulta usando la clase 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 ' ]);
Aunque no es el objetivo principal de este paquete, admite la ejecución de consultas de cadenas sin formato, como cualquier otro cliente que utilice el método runRawQuery
en la clase Client
. Aquí hay un ejemplo de cómo usarlo:
$ gql = <<<QUERY
query {
pokemon(name: "Pikachu") {
id
number
name
attacks {
special {
name
type
damage
}
}
}
}
QUERY ;
$ results = $ client -> runRawQuery ( $ gql );