Skip to main content

Refdata

Refdata (reference data) is used to define the accepted values for a particular field. These values are defined on the backend and do not change very often. We define an entity class for each field’s refdata containing the field’s possible values and also declare the field and its possible values in module/Application/config/refdata.config.php .

Refdata Structure

Let’s look at an example of where we are using Refdata in Sirius. Here is a copy of the ReportStatus class in module/Opg/src/Opg/Core/Model/Entity/AnnualReportLog/ReportStatus.php:

<?php

namespace Opg\Core\Model\Entity\AnnualReportLog;

use Opg\Core\Model\Refdata;

final class ReportStatus extends Refdata
{
    const CATEGORY = 'reportStatus';
    const PENDING = 'PENDING';
    const ACTIVE = 'ACTIVE';
    const RECEIVED = 'RECEIVED';
    const LODGED = 'LODGED';
    const ABANDONED = 'ABANDONED';

    public function isPending()
    {
        return self::PENDING === $this->getHandle();
    }

    public function isActive()
    {
        return self::ACTIVE === $this->getHandle();
    }

    public function isReceived()
    {
        return self::RECEIVED === $this->getHandle();
    }

    public function isLodged()
    {
        return self::LODGED === $this->getHandle();
    }

    public function isAbandoned()
    {
        return self::ABANDONED === $this->getHandle();
    }
}

The values are defined as contants at the top of the class. Alongside this inside refdata.config.php the label and handle attributes are also defined:

Entity\AnnualReportLog\ReportStatus::class => [
        Entity\AnnualReportLog\ReportStatus::PENDING =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::PENDING, 'label' => 'Pending'],
        Entity\AnnualReportLog\ReportStatus::ACTIVE =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::ACTIVE, 'label' => 'Active'],
        Entity\AnnualReportLog\ReportStatus::RECEIVED =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::RECEIVED, 'label' => 'Received'],
        Entity\AnnualReportLog\ReportStatus::LODGED =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::LODGED, 'label' => 'Lodged'],
        Entity\AnnualReportLog\ReportStatus::ABANDONED =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::ABANDONED, 'label' => 'Abandoned'],
    ],

Other attributes can also be defined if needed. For example OrderSubType has an extra filters attribute that is used to filter out subtypes that are supervised or not in this case:

Entity\CaseItem\Deputyship\OrderSubtype::class => [
        Entity\CaseItem\Deputyship\OrderSubtype::NEW_DEPUTY =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::NEW_DEPUTY, 'label' => 'New deputy', 'filters' => ['supervised']],
        Entity\CaseItem\Deputyship\OrderSubtype::INTERIM_ORDER =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::INTERIM_ORDER, 'label' => 'Interim order', 'filters' => ['supervised']],
        Entity\CaseItem\Deputyship\OrderSubtype::REPLACEMENT =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::REPLACEMENT, 'label' => 'Replacement', 'filters' => ['supervised']],
        Entity\CaseItem\Deputyship\OrderSubtype::SUPPLEMENTARY =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::SUPPLEMENTARY, 'label' => 'Supplementary'],
        Entity\CaseItem\Deputyship\OrderSubtype::TENANCY =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::TENANCY, 'label' => 'Tenancy'],
        Entity\CaseItem\Deputyship\OrderSubtype::TRUSTEE =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::TRUSTEE, 'label' => 'Trustee'],
        Entity\CaseItem\Deputyship\OrderSubtype::VARIATION =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::VARIATION, 'label' => 'Variation'],
        Entity\CaseItem\Deputyship\OrderSubtype::DIRECTION =>
            ['handle' => Entity\CaseItem\Deputyship\OrderSubtype::DIRECTION, 'label' => 'Direction'],
    ],

Using Refdata

To get the refdata values for a particular field we make an api call to GET /api/v1/reference-data and add a filter parameter to the query string to tell it which field(s) we want. For example lets say we want all the possible values for OrderSubType field if we look at the OrderSubType class you will see a constant defined called CATEGORY:

<?php

namespace Opg\Core\Model\Entity\CaseItem\Deputyship;

use Opg\Core\Model\Refdata;

final class OrderSubtype extends Refdata
{
    const CATEGORY = 'orderSubtype';

We use this value: ‘orderSubtype’ as the filter parameter: /api/v1/reference-data?filter=orderSubtype and this will return all the orderSubTypes defined. You can also grab multiple refdata field values at once using CSV format: /api/v1/reference-data?filter=orderSubtype,howDeputyAppointed

Filtering Refdata Values

We saw the filters attribute previously was used to mark orderSubType values as ‘supervised’. We can call the same endpoint and apply the filter to only return these values:

/api/v1/reference-data?filter=orderSubtype:supervised

Deprecating Refdata Values

TODO: This approach needs to be implemented {: .notice–danger}

To mark a field’s value in refdata value as deprecated we add the attribute deprecated => true to the value in refdata.config.php eg:

Entity\AnnualReportLog\ReportStatus::class => [
        Entity\AnnualReportLog\ReportStatus::PENDING =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::PENDING, 'label' => 'Pending'],
        Entity\AnnualReportLog\ReportStatus::ACTIVE =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::ACTIVE, 'label' => 'Active'],
        Entity\AnnualReportLog\ReportStatus::RECEIVED =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::RECEIVED, 'label' => 'Received', 'deprecated' => true], // Shown here
        Entity\AnnualReportLog\ReportStatus::LODGED =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::LODGED, 'label' => 'Lodged'],
        Entity\AnnualReportLog\ReportStatus::ABANDONED =>
            ['handle' => Entity\AnnualReportLog\ReportStatus::ABANDONED, 'label' => 'Abandoned'],
    ],

By default deprecated values are not included. To include them we add deprecated=true to the query string: /api/v1/reference-data?filter=orderSubtype&deprecated=true

Currently we do not completely remove refdata values due to the implications it can have to pre-existing data. There may be legacy records in the database that use refdata values that are now deprecated, but these will remain valid unless they are changed or a decision is made to run a migration to map all deprecated values to new ones.

How does Refdata work under the hood

RefdataListener

A class called RefdataListener (module/Application/src/Application/Doctrine/Refdata/RefdataListener.php) is instantiated and registered during the bootstrapping process of the app. The RefdataListener handles conversion between Refdata value objects in the entity and strings inside the database. It operates on all fields defined as a Refdata type and requires the additional option refdataClass to be set.

On preFlush hydrated refdata objects are converted to string values ready to be persisted to the database.

postLoad does the opposite of preFlush and hydrates values pulled from the database into their corresponding refdata objects.

RefdataService

The RefdataService class is used to get the values inside refdata classes and map corresponding values contained in refdata.config.php . The ReferenceDataController calls getAllAsArray from the RefdataService passing in the class name of the refdata we’re looking for.

This page was last reviewed on 12 November 2020. It needs to be reviewed again on 10 December 2020 by the page owner #opg-sirius-develop .
This page was set to be reviewed before 10 December 2020 by the page owner #opg-sirius-develop. This might mean the content is out of date.