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;
}

Hierarchy Awareness

If you need the value resolver to be usable in a hierarchy context, you also need to implement the HierarchyAware interface. It allows the framework to inject the input ValueResolver defined in the parent summary object.

namespace Rekalogika\Analytics\Contracts\Hierarchy;

use Rekalogika\Analytics\Contracts\Summary\ValueResolver;

interface HierarchyAware
{
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\Core\Metadata 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,
);
}
}