Formatter Services
Formatter services are utility services that transform values in the query result to the format expected by other parts of the framework. There are four types of formatter services available:
Stringifier: Converts a value to a string.Numberifier: Converts a value to an integer or float. Used byChartGeneratorto convert measures to numbers.Htmlifier: Converts a value to an HTML string. Used by anything that renders the result to HTML.Cellifier: Converts a value to aCellPropertiesobject, which is an object that describes a cell in a spreadsheet. Used bySpreadsheetRenderer.
The default formatter services try to do the conversion on a best-effort basis, but if they can't, you need to implement the formatter service for the types you need. Your implementations are registered automatically if you use autoconfiguration, and take precedence over the default ones.
Default Stringification
Without a custom Stringifier, the values are converted to strings as follows:
- A
TranslatableInterfaceis translated. - An integer or a float measure value is formatted according to the current
locale, e.g.
1,234.5. A non-zero number between -1 and 1 is formatted with up to 3 significant digits, so that0.000123is not rendered as0. - An integer dimension member, like a year, is not formatted as a number in the
pivot table, the chart labels, and the filters, so
2024is not rendered as2,024. - A
Stringableobject is converted using its__toString()method. - An enum is rendered as its case name.
nullis rendered as-.- Any other object is rendered as
object. The class name and the identifier are not shown, so internal details do not leak to the user interface.
Therefore, if a dimension uses an entity that is neither Stringable nor
TranslatableInterface, e.g. a Country entity, you should implement a
Stringifier for it. Otherwise, all of its members are rendered as object.
In a spreadsheet, integers and finite floats are written as numeric cells, so they can be summed and sorted. Other values are written as text, using the stringifier.
Example: MoneyNumberifier
This is the numberifier for the Money object of the brick/money package. The
Money::__toString() does not return a number, but a string with the currency
code, so the framework cannot automatically convert it to a number. This is how
you can implement the Numberifier for the Money object:
use Brick\Money\Money;
use Rekalogika\Analytics\Frontend\Formatter\Numberifier;
use Rekalogika\Analytics\Frontend\Formatter\ValueNotSupportedException;
final class MoneyNumberifier implements Numberifier
{
#[\Override]
public function toNumber(mixed $input): float
{
if (!$input instanceof Money) {
throw new ValueNotSupportedException();
}
return $input->getAmount()->toFloat();
}
}
Example: CountryWithFlagHtmlifier
Suppose you have a Country object, and it would be nice to render it with a
flag emoji. You can implement the Htmlifier for the Country object like this:
use Rekalogika\Analytics\Frontend\Formatter\Htmlifier;
use Rekalogika\Analytics\Frontend\Formatter\ValueNotSupportedException;
use App\Entity\Country;
final class CountryHtmlifier implements Htmlifier
{
#[\Override]
public function toHtml(mixed $input): string
{
if (!$input instanceof Country) {
throw new ValueNotSupportedException();
}
$emoji = $this->countryCodeToEmojiFlag($input->getCode());
return \sprintf(
'%s %s',
$emoji,
$input->getName() ?? '',
);
}
/**
* @see https://nick.blog/2018/07/27/php-display-country-flag-emoji-from-iso-3166-1-alpha-2-country-codes/
*/
private function countryCodeToEmojiFlag(string $countryCode): string
{
$countryCode = strtoupper($countryCode); // Ensure uppercase
$flag = '';
foreach (str_split($countryCode) as $char) {
/** @psalm-suppress PossiblyFalseOperand */
$flag .= mb_convert_encoding('&#' . (127397 + \ord($char)) . ';', 'UTF-8', 'HTML-ENTITIES');
}
return $flag;
}
}