Jump to content

ICT:D Day problem dd/mm/yyyy

From Costa Sano MediaWiki

Drupal 11.3 Heritage Date Validation Module

This module implements a modern, **Object-Oriented (OOP)** approach to validate and hint text fields for historical dates (1700s–Present) using the `dd/mm/yyyy` format.

Project File Structure

To ensure PSR-4 autoloading works in Drupal 11.3, use this exact structure:

/web/modules/custom/heritage_date/
├── heritage_date.info.yml
└── src/
    └── Hook/
        └── DateValidationHooks.php

File: heritage_date.info.yml

name: 'Heritage Date Validator'
type: module
description: 'Enforces dd/mm/yyyy format for fields ending in _day.'
package: Custom
core_version_requirement: ^11

File: src/Hook/DateValidationHooks.php

<?php

namespace Drupal\heritage_date\Hook;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;

/**
 * Modern Drupal 11 Hook class for Heritage Date handling.
 */
class DateValidationHooks {

  /**
   * Implements hook_form_alter().
   * Automatically adds placeholders and validation to *_day fields.
   */
  #[Hook('form_alter')]
  public function validateHeritageDate(array &$form, FormStateInterface $form_state, $form_id): void {
    
    // 1. Add Global Validation Handler
    $form['#validate'][] = [static::class, 'performRegexCheck'];

    // 2. Add Placeholders (UI Hinting)
    // We scan the form structure for our naming convention.
    foreach ($form as $key => &$element) {
      if (str_ends_with($key, '_day') && isset($element['widget'][0]['value'])) {
        $element['widget'][0]['value']['#attributes']['placeholder'] = 'dd/mm/yyyy';
      }
    }
  }

  /**
   * Scans form for fields ending in '_day' and validates format.
   */
  public static function performRegexCheck(array &$form, FormStateInterface $form_state): void {
    $values = $form_state->getValues();

    foreach ($values as $field_name => $value) {
      if (str_ends_with($field_name, '_day')) {
        
        $date_string = is_array($value) ? ($value['value'] ?? '') : '';

        if (!empty($date_string)) {
          // Regex for dd/mm/yyyy (Day/Month/Year)
          $regex = '/^(0[1-9]|[1-2][0-9]|3)\/(0[1-9]|1[0-2])\/([0-9]{4})$/';
          $label = $form[$field_name]['widget']['#title'] ?? $form[$field_name]['#title'] ?? $field_name;

          if (!preg_match($regex, $date_string)) {
            $form_state->setErrorByName($field_name, t('The @label must be in dd/mm/yyyy format.', ['@label' => $label]));
          } else {
            // Calendar sanity check (Month, Day, Year) for PHP checkdate
            $parts = explode('/', $date_string);
            if (!checkdate((int)$parts[1], (int)$parts[0], (int)$parts[2])) {
              $form_state->setErrorByName($field_name, t('The @label contains an invalid calendar date.', ['@label' => $label]));
            }
          }
        }
      }
    }
  }
}

Implementation Commands

  1. Create the directory and files as shown in the diagram.
  2. Enable via Drush: drush en heritage_date
  3. Rebuild Cache: drush cr (Crucial for Hook discovery).