Skip to content

[Candidate Identifier (Rework)] Database Schema and Configuration Migration - #11209

Open
HachemJ wants to merge 23 commits into
aces:mainfrom
HachemJ:ReworkIdentifierStructure
Open

HachemJ wants to merge 23 commits into
aces:mainfrom
HachemJ:ReworkIdentifierStructure

Conversation

@HachemJ

@HachemJ HachemJ commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Brief summary of changes

This PR adds the database structure for the candidate identifier rework discussed in #9036.

It introduces three tables:

  • candidate_identifier_types Stores the configuration for each identifier type.
  • candidate_identifier_types_project_rel Links identifier types to projects.
  • candidate_identifiers Stores the identifier values assigned to candidates.

It also adds a migration script, tools/single_use/migrate_candidate_identifiers.php, to migrate the existing PSCID and ExternalID configuration from config.xml into the new tables.

Existing identifier values and config.xml are left unchanged.

This is the first PR in the candidate identifier rework. The identifier generation and validation logic is added in #11212, and the REST API changes are added in #11253.

Database structure and design decisions

Identifier types

candidate_identifier_types stores the configuration for each identifier type.

Field Description
CandidateIdentifierTypeID Identifier type ID
Name Identifier name, such as PSCID or ExternalID
Format Template used to generate or validate the identifier
IsRequired Whether a candidate must have at least one value of this type
IsUnique Whether values must be unique across candidates
AutoGenerate Whether LORIS generates the value automatically
AllowMultiple Whether a candidate can have multiple values of this type
permID Optional permission required to view the identifier type

For example, PSCID can be configured as required, unique, automatically generated, and limited to one value per candidate. Other identifiers can be optional, manually entered, or allow multiple values.

Projects and permissions

candidate_identifier_types_project_rel links identifier types to the projects where they can be used. An identifier type can belong to multiple projects.

For visibility, I used the existing LORIS permission system instead of adding a separate Visible setting.

The optional permission is stored in permID. Each identifier type can have one permission, but the same permission can be assigned to multiple identifier types.

If a permission is configured, users need that permission to view the identifier type. Otherwise, anyone with access to the candidate should be able to view it.

Permission checks are handled in the application implementation rather than in this PR.

Candidate identifiers

candidate_identifiers stores the identifier values assigned to candidates.

Field Description
CandidateIdentifierID Identifier value ID
CandidateID Candidate the value belongs to
CandidateIdentifierTypeID Identifier type
Value Stored identifier value

I added a separate CandidateIdentifierID primary key because a candidate can have multiple values of the same identifier type when AllowMultiple is enabled.

The AllowMultiple and IsUnique settings are enforced in the application rather than through database constraints.

For example, a unique constraint on (CandidateID, CandidateIdentifierTypeID) would prevent multiple values for all identifier types, including those where AllowMultiple is enabled.

This means direct database inserts could bypass these rules, so the application needs to check the identifier type configuration when adding or updating values.

Deletion behavior

Identifier values are deleted when their candidate is deleted.

I chose not to cascade deletion from identifier types to their values. Accidentally deleting an identifier type, such as PSCID, should not also delete all the stored values.

Instead, the foreign key prevents an identifier type from being deleted while values still reference it.

Project relationships are deleted when their related configuration is deleted, since they only store associations.

If a permission is deleted, permID is set to NULL rather than deleting the identifier type.

Identifier formats

Identifier formats are now stored as templates in the Format column, replacing the previous structure and generation settings in config.xml.

The templates define the identifier structure and, when AutoGenerate is enabled, how the value should be generated.

The logic for interpreting these templates is implemented in #11212.

Identifier template syntax

Templates can contain literal text and placeholders enclosed in curly braces.

For example:

{SITE:ALIAS}-{PROJECT:ALIAS}-{SEQUENCE:4,FORMAT:numeric}

The supported placeholders are:

Placeholder Description
{SITE:ALIAS} Site alias
{PROJECT:ALIAS} Project alias
{SEQUENCE:N} Sequentially generated value of length N
{RANDOM:N} Randomly generated value of length N
{NUMERIC:N} Validates exactly N digits
{ALPHA:N} Validates exactly N letters
{ALPHANUMERIC:N} Validates exactly N alphanumeric characters

SEQUENCE and RANDOM also support:

  • FORMAT — numeric, alpha, or alphanumeric (defaults to numeric).
  • PADDING — character used for left-padding (defaults to 0 or A, depending on the alphabet).
  • MIN — minimum generator value.
  • MAX — maximum generator value.

An automatically generated identifier uses one SEQUENCE or RANDOM placeholder, which can be combined with literal text, site aliases, and project aliases.

When AutoGenerate is disabled, the identifier is entered manually and validated against the template. The NUMERIC, ALPHA, and ALPHANUMERIC placeholders are used for validation only.

Testing instructions (if applicable)

  1. Apply the SQL patch and check that the three new tables are created.

  2. Run the migration script:

    php tools/single_use/migrate_candidate_identifiers.php
  3. Check that PSCID and ExternalID are added to candidate_identifier_types with the expected formats and settings.

  4. Verify that the project relationships are migrated correctly.

  5. Confirm that config.xml and the existing PSCID and ExternalID values in the candidate table are unchanged.

@github-actions github-actions Bot added the Language: PHP PR or issue that update PHP code label Sep 17, 2026
@HachemJ HachemJ added the State: Blocking PR should be prioritized because it is blocking the progress of another task label Oct 7, 2026
@github-actions github-actions Bot added the Language: SQL PR or issue that update SQL code label Oct 12, 2026
@HachemJ HachemJ changed the title [libraries] Refactor identifier configuration to use template strings Add candidate identifier tables and configuration migration Oct 13, 2026
HachemJ added a commit to HachemJ/Loris that referenced this pull request Oct 13, 2026
@HachemJ HachemJ changed the title Add candidate identifier tables and configuration migration [Candidate Identifier rework] Database Schema and Configuration Migration Oct 14, 2026
@HachemJ HachemJ changed the title [Candidate Identifier rework] Database Schema and Configuration Migration [Candidate Identifier Rework] Database Schema and Configuration Migration Oct 16, 2026
@HachemJ HachemJ changed the title [Candidate Identifier Rework] Database Schema and Configuration Migration [Candidate Identifier (Rework)] Database Schema and Configuration Migration Oct 16, 2026
@HachemJ
HachemJ force-pushed the ReworkIdentifierStructure branch from 609b334 to c31e7df Compare October 27, 2026 15:20

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Language: PHP PR or issue that update PHP code Language: SQL PR or issue that update SQL code State: Blocking PR should be prioritized because it is blocking the progress of another task

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants