...
The current GraphQL flow is as follows:
Client sends request for GraphQL Query.
Query resolver for the given query is called.
The query resolver loads the records from the database and passes the records on to the type resolver.
The type resolver needs to resolve each field and apply the correct format.
In older code without proper models and a lot of special treatment of different fields, a generic solution might not be applicable. For newer (or better structured) code, a unified way to apply formatting is preferable.
...
A formatter takes care of the formatting of each field of a GraphQL type.
...
Usage of a formatter in GraphQL
Code Block | ||
---|---|---|
| ||
class my_type_resolver implements \core\webapi\query_resolver { public static function resolve(string $field, $competency, array $args, execution_context $ec) { ... $context = ... $format = $args['format'] ?? null; $formatter = new my_formatter($competency, $context); return $formatter->format($field, $format); } } |
The formatter must be instantiated with:
The object it should format
The context for which it should apply the format
At a minimum the formatter needs to implement the get_map()
function, which should return a map of fields and format functions to apply.
...
The function for a field can be:
An existing field formatter class name or instance (we ship it with string_field_formatter, text_field_formatter, date_field_formatter)
Any custom method name defined within the same class
A Closure taking only the value as argument, for simple value modifications
A Closure taking the value and a type-hinted field formatter, the field formatter will be automatically instantiated
If using a custom method in the current class, the method must be public, and the method signature is:
...
We provide three general field formatters:
string_field_formatter → runs value through format_string()
text_field_formatter → runs value through format_text()
date_field_formatter → formats date in various ways
Additional field formatters can be implemented either in core or in plugins by extending the base formatter.
The individual field formatters can implement the following functions:
Field formatter example
Code Block | ||||
---|---|---|---|---|
| ||||
class custom_field_formatter extends \core\webapi\formatter\field\base { protected function validate_format(): bool { // validate whether $this->format is the expected one // A good practice is to validate against constants // for example: return defined('self::FORMAT_.'.strtoupper($this->format)); } protected function get_default_format($value) { // This is called when there's no specific format_...() function // If all relevant formats are covered by functions you do not need to override this return ...; } protected function format_html($value) { // This is called for format == 'html' // implement functions for your specific format by following the format_[formatname]() pattern } } |
Testing formatters
...
We have a model , for testing formatters: model_client_settings. Let's try using the formatter to format one field on it:
Code Block | ||
---|---|---|
| ||
$context = context_helper::instance_by_id(CONTEXT_SYSTEM); |
...
$formatter = new client_settings_formatter($model_client_settings, $context); |
...
// Operate |
...
$field = 'default_token_expiry_time'; |
...
$formatted_field_result = $formatter->format($field); |
...
// Assert |
...
self::assertEquals((string)$model_client_settings->default_token_expiry_time, $formatted_field_result); |