Skip to main content

Creating Value Resolvers

A ValueResolver basically returns a DQL expression. Usually, we need to take user input using the constructor and then return the DQL expression in the getExpression() method.

A ValueResolver can be used to get a value from a property in the source entity, or it can take another ValueResolver as input and return a transformed value.

Interfaces​

Aggregate function Aggregate function

Implementing ValueResolver​

To create a value resolver, you need to implement the ValueResolver interface.

namespace Rekalogika\Analytics\Contracts\Summary;

use Rekalogika\Analytics\Contracts\Context\SourceQueryContext;

interface ValueResolver
{
public function getExpression(SourceQueryContext $context): string;

/**
* @return list<string>
*/
public function getInvolvedProperties(): array;
}

Usage in Dimension Groups​

If you need the value resolver to be usable inside a dimension group, you also need to implement the DimensionGroupAware interface. It allows the framework to inject the input ValueResolver defined in the dimension holding the group, from which your value resolver derives its own value, e.g. by extracting the year from a date.

namespace Rekalogika\Analytics\Contracts\DimensionGroup;

use Rekalogika\Analytics\Contracts\Summary\ValueResolver;

interface DimensionGroupAware
{
public function withInput(ValueResolver $input): static;
}

Usage in Partitions​

A partition also uses a value resolver. If you want to use your value resolver for a partition, you need to implement the PartitionValueResolver interface.

namespace Rekalogika\Analytics\Contracts\Summary;

interface PartitionValueResolver extends ValueResolver
{
public function getInvolvedProperties(): array;

public function transformSourceValueToSummaryValue(mixed $value): int;

public function transformSummaryValueToSourceValue(int $value): mixed;
}

Context Object​

The SourceQueryContext object is passed to the getExpression() method. It contains the context of the query, including the metadata, and the resolve() method to access the source entity properties.

The resolve() method takes a property path as its argument and returns the DQL expression for that property. Read more about property paths in the Property Paths section.

User Value Transformer​

After a query, the framework will instantiate the summary entity using the result values. Rather than getting the value as returned by Doctrine, the user has the option to create a getter method that transforms the value into a format more suitable for the caller.

However, doing that manually can be a very tedious task. You have the option to streamline this process by embedding the logic into the value resolver itself, so the user can use the same logic every time they use the same value resolver. To achieve that, you need to make your value resolver implement the UserValueTransformer interface.

use Rekalogika\Analytics\Contracts\Summary\ValueResolver;
use Rekalogika\Analytics\Contracts\Summary\UserValueTransformer;
use Rekalogika\Analytics\Contracts\Context\SourceQueryContext;

final readonly class QualityValueResolver implements ValueResolver, UserValueTransformer
{
public function getExpression(SourceQueryContext $context): string
{
// ...
}

public function getInvolvedProperties(): array
{
// ...
}

public function transformUserValue(mixed $value): mixed
{
// $value is the value returned by Doctrine

return new Quality($value);
}
}

Then in the summary class, the user will be able to do something like this:

use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Types\Types;
use Rekalogika\Analytics\Metadata\Attribute as Analytics;
use Rekalogika\Analytics\Core\Entity\BaseSummary;

#[ORM\Entity()]
#[Analytics\Summary(
sourceClass: Order::class,
)]
class OrderSummary extends BaseSummary
{
#[ORM\Column(type: Types::INTEGER)]
#[Analytics\Dimension(
source: new QualityValueResolver('quality'),
)]
private ?int $quality = null;

public function getQuality(): ?Quality
{
return $this->getContext()->getUserValue(
property: 'quality',
class: Quality::class,
);
}
}