{"id":24030254,"url":"https://github.com/getpop/field-query","last_synced_at":"2026-03-02T13:36:50.990Z","repository":{"id":45189563,"uuid":"215467527","full_name":"getpop/field-query","owner":"getpop","description":"[READ ONLY] Syntax to query GraphQL through URL params","archived":false,"fork":false,"pushed_at":"2022-07-15T03:56:50.000Z","size":236,"stargazers_count":4,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-26T09:20:07.591Z","etag":null,"topics":["api","api-server","graphql","grapql-server","php","query","syntax"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/getpop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-16T05:51:53.000Z","updated_at":"2022-01-07T13:06:26.000Z","dependencies_parsed_at":"2022-08-04T02:00:14.206Z","dependency_job_id":null,"html_url":"https://github.com/getpop/field-query","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/getpop/field-query","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getpop%2Ffield-query","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getpop%2Ffield-query/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getpop%2Ffield-query/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getpop%2Ffield-query/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getpop","download_url":"https://codeload.github.com/getpop/field-query/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getpop%2Ffield-query/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273397669,"owners_count":25098233,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-server","graphql","grapql-server","php","query","syntax"],"created_at":"2025-01-08T17:30:57.992Z","updated_at":"2026-03-02T13:36:45.955Z","avatar_url":"https://github.com/getpop.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Field Query\n\n\u003c!--\n[![Build Status][ico-travis]][link-travis]\n[![Quality Score][ico-code-quality]][link-code-quality]\n[![Software License][ico-license]](LICENSE.md)\n[![Latest Version on Packagist][ico-version]][link-packagist]\n[![Coverage Status][ico-scrutinizer]][link-scrutinizer]\n[![Total Downloads][ico-downloads]][link-downloads]\n--\u003e\n\nSyntax to query GraphQL through URL params, which grants a GraphQL API the capability to be cached on the server.\n\n## Install\n\nVia Composer\n\n```php bash\ncomposer require getpop/field-query\n```\n\n## Development\n\nThe source code is hosted on the [PoP monorepo](https://github.com/leoloso/PoP), under [`Engine/packages/field-query`](https://github.com/leoloso/PoP/tree/master/layers/Engine/packages/field-query).\n\n## Usage\n\nInitialize the component:\n\n``` php\n\\PoP\\Root\\App::stockAndInitializeModuleClasses([([\n    \\PoP\\FieldQuery\\Module::class,\n]);\n```\n\nUse it:\n\n```php\nuse PoP\\FieldQuery\\Facades\\Query\\FieldQueryInterpreterFacade;\n\n$fieldQueryInterpreter = FieldQueryInterpreterFacade::getInstance();\n\n// To create a field from its elements\n$field = /* @todo Re-do this code! Left undone */ new Field($fieldName, $fieldArgs, $fieldAlias, $skipOutputIfNull, $fieldDirectives);\n\n// To retrieve the elements from a field\n$fieldName = $fieldQueryInterpreter-\u003egetFieldName($field);\n$fieldArgs = $fieldQueryInterpreter-\u003egetFieldArgs($field);\n\n// All other functions listed in FieldQueryInterpreterInterface\n// ...\n```\n\n## Why\n\nThe GraphQL query generally spans multiple lines, and it is provided through the body of the request instead of through URL params. As a result, it is difficult to cache the results from a GraphQL query in the server. In order to support server-side caching on GraphQL, we can attempt to provide the query through the URL instead, as to use standard mechanisms which cache a page based on the URL as its unique ID.\n\nThe syntax described and implemented in this project is a re-imagining of the GraphQL syntax, supporting all the same elements (field arguments, variables, aliases, fragments, directives, etc), however designed to be easy to write, and easy to read and understand, in a single line, so it can be passed as a URL param.\n\nBeing able to pass the query as a URL param has, in turn, several other advantages:\n\n- It removes the need for a client-side library to convert the GraphQL query into the required format (such as [Relay](https://relay.dev/docs/en/graphql-in-relay)), leading to performance improvements and reduced amount of code to maintain\n- The GraphQL API becomes easier to consume (same as REST), and avoids depending on a special client (such as [GraphiQL](https://github.com/graphql/graphiql)) to visualize the results of the query\n\n## Who uses it\n\n[PoP](https://github.com/leoloso/PoP) uses this syntax natively: To load data in each component within the application itself (as done by the [Component Model](https://github.com/getpop/component-model)), and to load data from an API through URL param `query` (as done by the [PoP API](https://github.com/PoP-PoPAPI/api)).\n\nA GraphQL server can implement this syntax as to support URI-based server-side caching. To achieve this, a service must translate the query from this syntax to the corresponding [GraphQL syntax](https://graphql.org/learn/queries/), and then pass the translated query to the GraphQL engine.\n\n## Syntax\n\n[Similar to GraphQL](https://graphql.org/learn/queries/#fields), the query describes a set of “fields”, where each field can contain the following elements:\n\n- **The field name:** What data to retrieve\n- **Field arguments:** How to filter the data, or format the results\n- **Field alias:** How to name the field in the response\n- **Field directives:** To change the behaviour of how to execute the operation\n\nDifferently than GraphQL, a field can also contain the following elements:\n\n- **Property names in the field arguments may be optional:** To simplify passing arguments to the field\n- **Bookmarks:** To keep loading data from an already-defined field\n- **Operators and Helpers:** Standard operations (`and`, `or`, `if`, `isNull`, etc) and helpers to access environment variables (among other use cases) can be already available as fields\n- **Composable fields:** The response of a field can be used as input to another field, through its arguments or field directives\n- **Skip output if null:** To ignore the output if the value of the field is null\n- **Composable directives:** A directive can modify the behaviour of other, nested directives\n- **Expressions:** To pass values across directives\n\nFrom the composing elements, only the field name is mandatory; all others are optional. A field is composed in this order:\n\n1. The field name\n2. Arguments: `(...)`\n3. Bookmark: `[...]`\n4. Alias: `@...` (if the bookmark is also present, it is placed inside)\n5. Skip output if null: `?`\n6. Directives: directive name and arguments: `\u003cdirectiveName(...)\u003e`\n\nThe field looks like this:\n\n```php\nfieldName(fieldArgs)[@alias]?\u003cfieldDirective(directiveArgs)\u003e\n```\n\nTo make it clearer to visualize, the query can be split into several lines:\n\n```php\nfieldName(\n  fieldArgs\n)[@alias]?\u003c\n  fieldDirective(\n    directiveArgs\n  )\n\u003e\n```\n\n\u003e **Note:**\u003cbr/\u003eFirefox already handles the multi-line query: Copy/pasting it into the URL bar works perfectly. To try it out, copy `https://newapi.getpop.org/api/graphql/` followed by the query presented in each example into Firefox's URL bar, and voilà, the query should execute.\n\u003e \n\u003e Chrome and Safari do not work as well: They require to strip all the whitespaces and line returns before pasting the query into the URL bar.\n\u003e \n\u003e Conclusion: use Firefox!\n\nTo retrieve several fields in the same query, we join them using `,`:\n\n```php\nfieldName1@alias1,\nfieldName2(\n  fieldArgs2\n)[@alias2]?\u003c\n  fieldDirective2\n\u003e\n```\n\n### Retrieving properties from a node\n\nSeparate the properties to fetch using `|`.\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  id\n  fullSchema\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=id|fullSchema)):_\n\n```php\n/?query=\n  id|\n  fullSchema\n```\n\n### Retrieving nested properties\n\nTo fetch relational or nested data, describe the path to the property using `.`.\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    author {\n      id\n    }\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.author.id)):_\n\n```php\n/?query=\n  posts.\n    author.\n      id\n```\n\nWe can use `|` to bring more than one property when reaching the node:\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    author {\n      id\n      name\n      url\n    }\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.author.id|name|url)):_\n\n```php\n/?query=\n  posts.\n    author.\n      id|\n      name|\n      url\n```\n\nSymbols `.` and `|` can be mixed together to also bring properties along the path:\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    id\n    title\n    author {\n      id\n      name\n      url\n    }\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|author.id|name|url)):_\n\n```php\n/?query=\n  posts.\n    id|\n    title|\n    author.\n      id|\n      name|\n      url\n```\n\n### Appending fields\n\nCombine multiple fields by joining them using `,`.\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    author {\n      id\n      name\n      url\n    }\n    comments {\n      id\n      content\n    }\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.author.id|name|url,posts.comments.id|content)):_\n\n```php\n/?query=\n  posts.\n    author.\n      id|\n      name|\n      url,\n  posts.\n    comments.\n      id|\n      content\n```\n\n### Appending fields with strict execution order\n\n_This is a syntax + functionality feature._ Combine multiple fields by joining them using `;`, telling the data-loading engine to resolve the fields on the right side of the `;` only after resolving all the fields on the left side.\n\nThe closest equivalent in GraphQL is the same as the previous case with `,`, however this syntax does not modify the behavior of the server.\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    author {\n      id\n      name\n      url\n    }\n    comments {\n      id\n      content\n    }\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.author.id|name|url;posts.comments.id|content)):_\n\n```php\n/?query=\n  posts.\n    author.\n      id|\n      name|\n      url;\n      \n  posts.\n    comments.\n      id|\n      content\n```\n\nIn the GraphQL server, the previous query is resolved as this one (with `self` being used to delay when a field is resolved):\n\n```php\n/?query=\n  posts.\n    author.\n      id|\n      name|\n      url,\n  self.\n    self.\n      posts.\n        comments.\n        id|\n        content\n```\n\n### Field arguments\n\nField arguments is an array of properties, to filter the results (when applied to a property along a path) or modify the output (when applied to a property on a leaf node) from the field. These are enclosed using `()`, defined using `:` to separate the property name from the value (becoming `name:value`), and separated using `,`.\n\nValues do not need be enclosed using quotes `\"...\"`.\n\n_Filtering results **in GraphQL**:_\n\n```graphql\nquery {\n  posts(filter:{ search: \"template\" }) {\n    id\n    title\n    date\n  }\n}\n```\n\n_Filtering results **in PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts(filter:{search:template}).id|title|date)):_\n\n```php\n/?query=\n  posts(filter: { search: template }).\n    id|\n    title|\n    date\n```\n\n_Formatting output **in GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    id\n    title\n    dateStr(format: \"d/m/Y\")\n  }\n}\n```\n\n_Formatting output **in PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|dateStr(format:d/m/Y))):_\n\n```php\n/?query=\n  posts.\n    id|\n    title|\n    dateStr(format:d/m/Y)\n```\n\n### Optional property name in field arguments\n\nDefining the argument name can be ignored if it can be deduced from the schema (for instance, the name can be deduced from the position of the property within the arguments in the schema definition).\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|dateStr(d/m/Y))):_\n\n```php\n/?query=\n  posts.\n    id|\n    title|\n    dateStr(d/m/Y)\n```\n\n### Aliases\n\nAn alias defines under what name to output the field. The alias name must be prepended with `@`:\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    id\n    title\n    formattedDate: dateStr(format: \"d/m/Y\")\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|dateStr(d/m/Y)@formattedDate)):_\n\n```php\n/?query=\n  posts.\n    id|\n    title|\n    dateStr(d/m/Y)@formattedDate\n```\n\nPlease notice that aliases are optional, differently than in GraphQL. [In GraphQL](https://graphql.org/learn/queries/#aliases), because the field arguments are not part of the field in the response, when querying the same field with different arguments it is required to use an alias to differentiate them. In PoP, however, field arguments are part of the field in the response, which already differentiates the fields.\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    id\n    title\n    date: date\n    formattedDate: dateStr(format: \"d/m/Y\")\n  }\n}\n```\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|dateStr|dateStr(d/m/Y))):_\n\n```php\n/?query=posts.\n  id|\n  title|\n  dateStr|\n  dateStr(d/m/Y)\n```\n\n### Bookmarks\n\nWhen iterating down a field path, loading data from different sub-branches is visually appealing in GraphQL:\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  users {\n    posts {\n      author {\n        id\n        name\n      }\n      comments {\n        id\n        content\n      }\n    }\n  }\n}\n```\n\nIn PoP, however, the query can become very verbose, because when combining fields with `,` it starts iterating the path again all the way from the root:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=users.posts.author.id|name,users.posts.comments.id|content)):_\n\n```php\n/?query=\n  users.\n    posts.\n      author.\n        id|\n        name,\n  users.\n    posts.\n      comments.\n        id|\n        content\n```\n\nBookmarks help address this problem by creating a shortcut to a path, so we can conveniently keep loading data from that point on. To define the bookmark, its name is enclosed with `[...]` when iterating down the path, and to use it, its name is similarly enclosed with `[...]`:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=users.posts[userposts].author.id|name,[userposts].comments.id|content)):_\n\n```php\n/?query=\n  users.\n    posts[userposts].\n      author.\n        id|\n        name,\n    [userposts].\n      comments.\n        id|\n        content\n```\n\n### Bookmark with Alias\n\nWhen we need to define both a bookmark to a path, and an alias to output the field, these 2 must be combined: The alias, prepended with `@`, is placed within the bookmark delimiters `[...]`.\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=users.posts[@userposts].author.id|name,[userposts].comments.id|content)):_\n\n```php\n/?query=\n  users.\n    posts[@userposts].\n      author.\n        id|\n        name,\n    [userposts].\n      comments.id|\n      content\n```\n\n### Variables\n\nVariables can be used to input values to field arguments. While [in GraphQL](https://graphql.org/learn/queries/#variables) the values to resolve to are defined within the body (in a separate dictionary than the query), in PoP these are retrieved from the request (`$_GET` or `$_POST`). \n\nThe variable name must be prepended with `$`, and its value in the request can be defined either directly under the variable name, or under entry `variables` and then the variable name. \n\n_API call **in GraphQL**:_\n\n```json\n{\n  \"query\":\"query ($format: String) {\n    posts {\n      id\n      title\n      dateStr(format: $format)\n    }\n  }\",\n  \"variables\":\"{\n    \\\"format\\\":\\\"d/m/Y\\\"\n  }\"\n}\n```\n\n_**In PoP** (View in browser: [query 1](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|dateStr($format)\u0026format=d/m/Y), [query 2](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|dateStr($format)\u0026variables[format]=d/m/Y)):_\n\n```php\n1. /?\n  format=d/m/Y\u0026\n  query=\n    posts.\n      id|\n      title|\n      dateStr($format)\n\n2. /?\n  variables[format]=d/m/Y\u0026\n  query=\n    posts.\n      id|\n      title|\n      dateStr($format)\n```\n\n### Fragments\n\nFragments enable to re-use query sections. Similar to variables, their resolution is defined in the request (`$_GET` or `$_POST`). Unlike [in GraphQL](https://graphql.org/learn/queries/#fragments), the fragment does not need to indicate on which schema type it operates.\n\nThe fragment name must be prepended with `--`, and the query they resolve to can be defined either directly under the fragment name, or under entry `fragments` and then the fragment name. \n\nWhile a fragment can contain `|` (to split fields), it cannot contain `;` (to split operations) or `,` (to split queries), to avoid confusion (since these are computed from the root of the query, not the fragment).\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  users {\n    ...userData\n    posts {\n      comments {\n        author {\n          ...userData\n        }\n      }\n    }\n  }\n}\n\nfragment userData on User {\n  id\n  name\n  url\n}\n```\n\n_**In PoP** (View in browser: [query 1](https://nextapi.getpop.org/api/graphql/?query=users.--userData|posts.comments.author.--userData\u0026userData=id|name|url), [query 2](https://nextapi.getpop.org/api/graphql/?query=users.--userData|posts.comments.author.--userData\u0026fragments[userData]=id|name|url)):_\n\n```php\n1. /?\nuserData=\n  id|\n  name|\n  url\u0026\nquery=\n  users.\n    --userData|\n    posts.\n      comments.\n        author.\n          --userData\n\n2. /?\nfragments[userData]=\n  id|\n  name|\n  url\u0026\nquery=\n  users.\n    --userData|\n    posts.\n      comments.\n        author.\n          --userData\n```\n\n### Directives\n\nA directive enables to modify if/how the operation to fetch data is executed. Each field accepts many directives, each of them receiving its own arguments to customize its behaviour. The set of directives is surrounded by `\u003c...\u003e`, the directives within must be separated by `,`, and their arguments follows the same syntax as field arguments: they are surrounded by `(...)`, and its pairs of `name:value` are separated by `,`.\n\n_**In GraphQL**:_\n\n```graphql\nquery {\n  posts {\n    id\n    title\n    featuredImage @include(if: $addFeaturedImage) {\n      id\n      src\n    }\n  }\n}\n```\n\n_**In PoP** (View in browser: [query 1](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage\u003cinclude(if:$include)\u003e.id|src\u0026include=true), [query 2](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage\u003cinclude(if:$include)\u003e.id|src\u0026include=false), [query 3](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage\u003cskip(if:$skip)\u003e.id|src\u0026skip=true), [query 4](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage\u003cskip(if:$skip)\u003e.id|src\u0026skip=false)):_\n\n```php\n1. /?\ninclude=true\u0026\nquery=\n  posts.\n    id|\n    title|\n    featuredImage\u003c\n      include(if:$include)\n    \u003e.\n      id|\n      src\n\n2. /?\ninclude=false\u0026\nquery=\n  posts.\n    id|\n    title|\n    featuredImage\u003c\n      include(if:$include)\n    \u003e.\n      id|\n      src\n\n3. /?\nskip=true\u0026\nquery=\n  posts.\n    id|\n    title|\n    featuredImage\u003c\n      skip(if:$skip)\n    \u003e.\n      id|\n      src\n\n4. /?\nskip=false\u0026\nquery=\n  posts.\n    id|\n    title|\n    featuredImage\u003c\n      skip(if:$skip)\n    \u003e.\n      id|\n      src\n```\n\n### Operators and Helpers\n\nStandard operations, such as `and`, `or`, `if`, `isNull`, `contains`, `sprintf` and many others, can be made available on the API as fields. Then, the operator name stands for the field name, and it can accept all the other elements in the same format (arguments, aliases, etc). \n\nTo pass an argument value as an array, we enclose it between `[]` and split its items with `,`. The format can be just `value` (numeric array) or `key:value` (indexed array).\n\n_**In PoP** (View in browser: \u003ca href=\"https://nextapi.getpop.org/api/graphql?query=not(true)\"\u003equery 1\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql?query=or([1,0])\"\u003equery 2\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql?query=and([1,0])\"\u003equery 3\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=if(true,%20Show%20this%20text,%20Hide%20this%20text)\"\u003equery 4\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=equals(first%20text,%20second%20text)\"\u003equery 5\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql?query=isNull(),isNull(something)\"\u003equery 6\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=sprintf(%s%20is%20%s,%20[PoP%20API,cool])\"\u003equery 7\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=echo([name:%20PoP%20API,status:%20cool])\"\u003equery 8\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=arrayValues([name:%20PoP%20API,status:%20cool])\"\u003equery 9\u003c/a\u003e):_\n\n```php\n1. /?query=not(true)\n\n2. /?query=or([1, 0])\n\n3. /?query=and([1, 0])\n\n4. /?query=if(true, Show this text, Hide this text)\n\n5. /?query=equals(first text, second text)\n\n6. /?query=isNull(),isNull(something)\n\n7. /?query=sprintf(\n  %s is %s, [\n    PoP API, \n    cool\n  ])\n\n8. /?query=echo([\n  name: PoP API,\n  status: cool\n])\n\n9. /?query=arrayValues([\n  name: PoP API,\n  status: cool\n])\n```\n\nIn the same fashion, helper functions can provide any required information, also behaving as fields. For instance, helper `context` provides the values in the system's state, and helper `var` can retrieve any specific variable from the system's state.\n\n_**In PoP** (View in browser: \u003ca href=\"https://nextapi.getpop.org/api/graphql?query=context\"\u003equery 1\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql?query=var(route)|var(target)@target|var(datastructure)\"\u003equery 2\u003c/a\u003e):_\n\n```php\n1. /?query=context\n\n2. /?query=\n  var(route)|\n  var(target)@target|\n  var(datastructure)\n```\n\n### Composable fields\n\nThe real benefit from having operators comes when they can receive the output from a field as their input. Since an operator is a field by itself, this can be generalized into “composable fields”: Passing the result of any field as an argument value to another field. \n\nIn order to distinguish if the input to the field is a string or the name of a field, the field must have field arguments brackets `(...)` (if no arguments, then simply `()`). For instance, `\"id\"` means the string \"id\", and `\"id()\"` means to execute and pass the result from field \"id\".\n\n_**In PoP** (View in browser: \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=posts.hasComments|not(hasComments())\"\u003equery 1\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=posts.hasComments|hasFeaturedImage|or([hasComments(),hasFeaturedImage()])\"\u003equery 2\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=var(fetching-site),posts.hasFeaturedImage|and([hasFeaturedImage(), var(fetching-site)])\"\u003equery 3\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=posts.if(hasComments(),sprintf(Post with title '%s' has %s comments,[title(), commentCount()]),sprintf(Post with ID %s was created on %s, [id(),dateStr(d/m/Y)]))@postDesc\"\u003equery 4\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=users.name|equals(name(), leo)\"\u003equery 5\u003c/a\u003e, \u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=posts.featuredImage|isNull(featuredImage())\"\u003equery 6\u003c/a\u003e):_\n\n```php\n1. /?query=\n  posts.\n    hasComments|\n    not(hasComments())\n\n2. /?query=\n  posts.\n    hasComments|\n    hasFeaturedImage|\n    or([\n      hasComments(),\n      hasFeaturedImage()\n    ])\n\n3. /?query=\n  var(fetching-site)|\n  posts.\n    hasFeaturedImage|\n    and([\n      hasFeaturedImage(),\n      var(fetching-site)\n    ])\n\n4. /?query=\n  posts.\n  if (\n    hasComments(),\n    sprintf(\n      Post with title '%s' has %s comments, [\n      title(), \n      commentCount()\n    ]),\n    sprintf(\n      Post with ID %s was created on %s, [\n      id(),\n      dateStr(d/m/Y)\n    ])\n  )@postDesc\n\n5. /?query=users.\n  name|\n  equals(\n    name(), \n    leo\n  )\n\n6. /?query=\n  posts.\n    featuredImage|\n    isNull(featuredImage())\n```\n\nIn order to include characters `(` and `)` as part of the query string, and avoid treating the string as a field to be executed, we must enclose it using quotes: `\"...\"`.\n\n_**In PoP** (\u003ca href='https://nextapi.getpop.org/api/graphql/?query=posts.sprintf(\"This post has %s comment(s)\",[commentCount()])@postDesc'\u003eView query in browser\u003c/a\u003e):_\n\n```php\n/?query=\n  posts.\n    sprintf(\n      \"This post has %s comment(s)\", [\n      commentCount()\n    ])@postDesc\n```\n\n### Composable fields with directives\n\nComposable fields enable to execute an operation against the queried object itself. Making use of this capability, directives in PoP become much more useful, since they can evaluate their conditions against each and every object independently. This feature can give raise to a myriad of new features, such as client-directed content manipulation, fine-grained access control, enhanced validations, and many others.\n\nFor instance, the GraphQL spec [requires](https://graphql.org/learn/queries/#directives) to support directives `include` and `skip`, which receive a parameter `if` with a boolean value. While GraphQL expects this value to be provided through a variable (as shown in section [Directives](#directives) above), in PoP it can be retrieved from the object.\n\n_**In PoP** (View in browser: [query 1](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage\u003cinclude(if:not(isNull(featuredImage())))\u003e.id|src), [query 2](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage\u003cskip(if:isNull(featuredImage()))\u003e.id|src)):_\n\n```php\n1. /?query=\n  posts.\n    id|\n    title|\n    featuredImage\u003c\n      include(if:not(isNull(featuredImage())))\n    \u003e.\n      id|\n      src\n\n2. /?query=\n  posts.\n    id|\n    title|\n    featuredImage\u003c\n      skip(if:isNull(featuredImage()))\n    \u003e.\n      id|\n      src\n```\n\n### Skip output if null\n\nWhenever the value from a field is null, its nested fields will not be retrieved. For instance, consider the following case, in which field `\"featuredImage\"` sometimes is null:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage.id|src)):_\n\n```php\n/?query=\n  posts.\n    id|\n    title|\n    featuredImage.\n      id|\n      src\n```\n\nAs we have seen in section [Composable fields with directives](#composable-fields-with-directives) above, by combining directives `include` and `skip` with composable fields, we can decide to not output a field when its value is null. However, the query to execute this behaviour includes a directive added in the middle of the query path, making it very verbose and less legible. Since this is a very common use case, it makes sense to generalize it and incorporate a simplified version of it into the syntax. \n\nFor this, PoP introduces symbol `?`, to be placed after the field name (and its field arguments, alias and bookmark), to indicate \"if this value is null, do not output it\". \n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|featuredImage?.id|src)):_\n\n```php\n/?query=\n  posts.\n    id|\n    title|\n    featuredImage?.\n      id|\n      src\n```\n\n### Composable directives and Expressions\n\nDirectives can be nested: An outer directive can modify the behaviour of its inner, nested directive(s). It can pass values across to its composed directives through “expressions”, variables surrounded with `%...%` which can be created on runtime (coded as part of the query), or be defined in the directive itself.\n\nIn the example below, directive `\u003cforEach\u003e` iterates through the elements of the array, for its composed directive `\u003capplyFunction\u003e` to do something with each of them. It passes the array item through pre-defined expression `%{value}%` (coded within the directive).\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=echo(%5B%5Bbanana,apple%5D,%5Bstrawberry,grape,melon%5D%5D)@fruitJoin%3CforEach%3CapplyFunction(function:arrayJoin,addArguments:%5Barray:%{value}%,separator:%22---%22%5D)%3E%3E)):_\n\n```php\n/?query=\n  echo([\n    [banana, apple],\n    [strawberry, grape, melon]\n  ])@fruitJoin\u003c\n    forEach\u003c\n      applyFunction(\n        function: arrayJoin,\n        addArguments: [\n          array: %{value}%,\n          separator: \"---\"\n        ]\n      )\n    \u003e\n  \u003e\n```\n\nIn the example below, directive `\u003cadvancePointerInArrayOrObject\u003e` communicates to directive `\u003ctranslate\u003e` the language to translate to through expression `%{toLang}%`, which is defined on-the-fly.\n\n_**In PoP** (\u003ca href=\"https://nextapi.getpop.org/api/graphql/?query=echo([{text:Hello my friends,translateTo:fr},{text:How do you like this software so far?,translateTo:es}])@translated\u003cforEach\u003cadvancePointerInArrayOrObject(path:text,appendExpressions:{toLang:extract(%{value}%,translateTo)})\u003ctranslateMultiple(from:en,to:%{toLang}%,oneLanguagePerField:true)\u003e\u003e\u003e\"\u003eView query in browser\u003c/a\u003e):_\n\n```php\n/?query=\n  echo([\n    {\n      text: Hello my friends,\n      translateTo: fr\n    },\n    {\n      text: How do you like this software so far?,\n      translateTo: es\n    }\n  ])@translated\u003c\n    forEach\u003c\n      advancePointerInArrayOrObject(\n        path: text,\n        appendExpressions: {\n          toLang:extract(%{value}%,translateTo)\n        }\n      )\u003c\n        translateMultiple(\n          from: en,\n          to: %{toLang}%,\n          oneLanguagePerField: true\n        )\n      \u003e\n    \u003e\n  \u003e\n```\n\n### Combining elements\n\nDifferent elements can be combined, such as the following examples. \n\nA fragment can contain nested paths, variables, directives and other fragments:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts(pagination:{limit:$limit}).--postData|author.posts(pagination:{limit:$limit}).--postData\u0026postData=id|title|--nestedPostData|dateStr(format:$format)\u0026nestedPostData=comments\u003cinclude(if:$include)\u003e.id|content\u0026format=d/m/Y\u0026include=true\u0026limit=3)):_\n\n```php\n/?\npostData=\n  id|\n  title|\n  --nestedPostData|\n  dateStr(format:$format)\u0026\nnestedPostData=\n  comments\u003c\n    include(if:$include)\n  \u003e.\n    id|\n    content\u0026\nformat=d/m/Y\u0026\ninclude=true\u0026\nlimit=3\u0026\norder=title\u0026\nquery=\n  posts(\n    pagination: {\n      limit:$limit\n    },\n    sort: {\n      order:$order\n    }\n  ).\n    --postData|\n    author.\n      posts(\n        pagination: {\n          limit:$limit\n        }\n      ).\n        --postData\n```\n\nA fragment can contain directives, which are transferred into the fragment resolution fields:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|--props\u003cinclude(if:hasComments())\u003e\u0026fragments[props]=title|date)):_\n\n```php\n/?\nfragments[props]=\n  title|\n  date\u0026 \nquery=\n  posts.\n    id|\n    --props\u003c\n      include(if:hasComments())\n    \u003e\n```\n\nIf the field in the fragment resolution field already has its own directives, these are applied; otherwise, the directives from the fragment definition are applied:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|--props\u003cinclude(if:hasComments())\u003e\u0026fragments[props]=title|url\u003cinclude(if:not(hasComments()))\u003e)):_\n\n```php\n/?\nfragments[props]=\n  title|\n  url\u003c\n    include(if:not(hasComments()))\n  \u003e\u0026\nquery=\n  posts.\n    id|\n    --props\u003c\n      include(if:hasComments())\n    \u003e\n```\n\nA fragment can contain an alias, which is then transferred to all fragment resolution fields as an enumerated alias (`@aliasName1`, `@aliasName2`, etc):\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|--props@prop\u0026fragments[props]=title|url|featuredImage)):_\n\n```php\n/?\nfragments[props]=\n  title|\n  url|\n  featuredImage\u0026\nquery=\n  posts.\n    id|\n    --props@prop\n```\n\n\u003c!-- Combined with directives, we can query several fields at the same time without having them override their results:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|--props@original|--props@translated\u003ctranslate(from:en,to:es)\u003e\u0026fragments[props]=title|content)):_\n\n```php\n/?query=posts.id|--props@original|--props@translated\u003ctranslate(from:en,to:es)\u003e\u0026fragments[props]=title|content\n``` --\u003e\n\nA fragment can contain the \"Skip output if null\" symbol, which is then transferred to all fragment resolution fields:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|--props?\u0026fragments[props]=title|url|featuredImage)):_\n\n```php\n/?\nfragments[props]=\n  title|\n  url|\n  featuredImage\u0026\nquery=\n  posts.\n    id|\n    --props?\n```\n\nCombining both directives and the skip output if null symbol with fragments:\n\n_**In PoP** ([View query in browser](https://nextapi.getpop.org/api/graphql/?query=posts.id|hasComments|--props?\u003cinclude(if:hasComments())\u003e\u0026fragments[props]=title|url\u003cinclude(if:hasComments())\u003e|featuredImage)):_\n\n```php\n/?\nfragments[props]=\n  title|\n  url\u003c\n    include(if:hasComments())\n  \u003e|\n  featuredImage\u0026\nquery=\n  posts.\n    id|\n    hasComments|\n    --props?\u003c\n      include(if:hasComments())\n    \u003e\n```\n\n\u003c!--\n## Query examples\n\nField arguments:\n\n- Order posts by title: [posts(order:title|asc)](https://nextapi.getpop.org/api/graphql/?query=posts(order:title|asc).id|title|url|date)\n- Search \"template\" and limit it to 3 results: [posts(searchfor:template,limit:3)](https://nextapi.getpop.org/api/graphql/?query=posts(searchfor:template,limit:3).id|title|url|date)\n- Format a date: [posts.date(format:d/m/Y)](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|url|date(format:d/m/Y))\n\nAlias:\n\n- [posts(order:title|asc)@orderedposts](https://nextapi.getpop.org/api/graphql/?query=posts(order:title|asc)@orderedposts.id|title|url|date)\n- [posts.date(format:d/m/Y)@formatteddate](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|url|date(format:d/m/Y)@formatteddate)\n\nBookmark:\n\n- [posts.comments[comments].author.id|name,[comments].post.id|title](https://nextapi.getpop.org/api/graphql/?query=posts.comments[comments].author.id|name,[comments].post.id|title)\n\n\n\nBookmark with alias:\n\n- [posts.comments[@postcomments].author.id|name,[postcomments].post.id|title](https://nextapi.getpop.org/api/graphql/?query=posts.comments[@postcomments].author.id|name,[postcomments].post.id|title)\n\n\n\nVariables:\n\n- [posts(searchfor:$term,limit:$limit).id|title\u0026variables[limit]=3\u0026term=template](https://nextapi.getpop.org/api/graphql/?query=posts(searchfor:$term,limit:$limit).id|title\u0026variables[limit]=3\u0026term=template)\n\nFragments:\n\n- [posts(limit:2).--fr1,users(id:1).posts.--fr1\u0026fragments[fr1]=id|author.posts(limit:1).id|title](https://nextapi.getpop.org/api/graphql/?query=posts(limit:2).--fr1,users(id:1).posts.--fr1\u0026fragments[fr1]=id|author.posts(limit:1).id|title)\n\n\nDirectives:\n\n- [posts.id|title|url\u003cinclude(if:$include)\u003e\u0026variables[include]=true](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|url\u003cinclude(if:$include)\u003e\u0026variables[include]=true)\n- [posts.id|title|url\u003cinclude(if:$include)\u003e\u0026variables[include]=](https://nextapi.getpop.org/api/graphql/?query=posts.id|title|url\u003cinclude(if:$include)\u003e\u0026variables[include]=)\n\n--\u003e\n\n## PHP versions\n\nRequirements:\n\n- PHP 8.1+ for development\n- PHP 7.1+ for production\n\n### Supported PHP features\n\nCheck the list of [Supported PHP features in `leoloso/PoP`](https://github.com/leoloso/PoP/blob/master/docs/supported-php-features.md)\n\n### Preview downgrade to PHP 7.1\n\nVia [Rector](https://github.com/rectorphp/rector) (dry-run mode):\n\n```bash\ncomposer preview-code-downgrade\n```\n\n## Standards\n\n[PSR-1](https://www.php-fig.org/psr/psr-1), [PSR-4](https://www.php-fig.org/psr/psr-4) and [PSR-12](https://www.php-fig.org/psr/psr-12).\n\nTo check the coding standards via [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer), run:\n\n``` bash\ncomposer check-style\n```\n\nTo automatically fix issues, run:\n\n``` bash\ncomposer fix-style\n```\n\n## Change log\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.\n\n## Testing\n\n```php bash\n$ composer test\n```\n\n## Report issues\n\nTo report a bug or request a new feature please do it on the [PoP monorepo issue tracker](https://github.com/leoloso/PoP/issues).\n\n## Contributing\n\nWe welcome contributions for this package on the [PoP monorepo](https://github.com/leoloso/PoP) (where the source code for this package is hosted).\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details.\n\n## Security\n\nIf you discover any security related issues, please email leo@getpop.org instead of using the issue tracker.\n\n## Credits\n\n- [Leonardo Losoviz][link-author]\n- [All Contributors][link-contributors]\n\n## License\n\nGNU General Public License v2 (or later). Please see [License File](LICENSE.md) for more information.\n\n[ico-version]: https://img.shields.io/packagist/v/getpop/field-query.svg?style=flat-square\n[ico-license]: https://img.shields.io/badge/license-GPLv2-brightgreen.svg?style=flat-square\n[ico-travis]: https://img.shields.io/travis/getpop/field-query/master.svg?style=flat-square\n[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/getpop/field-query.svg?style=flat-square\n[ico-code-quality]: https://img.shields.io/scrutinizer/g/getpop/field-query.svg?style=flat-square\n[ico-downloads]: https://img.shields.io/packagist/dt/getpop/field-query.svg?style=flat-square\n\n[link-packagist]: https://packagist.org/packages/getpop/field-query\n[link-travis]: https://travis-ci.org/getpop/field-query\n[link-scrutinizer]: https://scrutinizer-ci.com/g/getpop/field-query/code-structure\n[link-code-quality]: https://scrutinizer-ci.com/g/getpop/field-query\n[link-downloads]: https://packagist.org/packages/getpop/field-query\n[link-author]: https://github.com/leoloso\n[link-contributors]: ../../../../../../contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetpop%2Ffield-query","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetpop%2Ffield-query","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetpop%2Ffield-query/lists"}