Jump to content

ICT:Drupal Custom module design for automatic numbering

From Costa Sano MediaWiki
Revision as of 15:29, 8 March 2026 by Mngr (talk | contribs)

Automatic numbering of Asset records with custom desing module

= Environment preparation for custom modules

Custom modules live in

/var/www/drupal/web/modules/custom$

Our custom module is called costasano_asset and as such a directory should be created for this module. Minimum 2 files are necessary for a custom module.

/var/www/drupal/web/modules/custom/costasano_asset
costasano_asset.info.yml
costasano_asset.module

A first file costasano_asset.info.yml defines the module inside the Drupal environment

name: Costasano Asset
type: module
description: Asset numbering logic for the Costasano Heritage Project
core_version_requirement: ^11
package: Costasano

Once this files exists, the module can be enabled:

drush en costasano_asset

The module itself

The package = a php mpodule can then be written and step for step tested. After each step don't forget to clean the cash

drush cr

The final code for the custom module costasano_asset.module is as follows:

/**
 * Implements hook_entity_presave().
 */
function costasano_asset_entity_presave(EntityInterface $entity) {

  if ($entity->getEntityTypeId() !== 'node') {
    return;
  }

  if ($entity->bundle() !== 'asset') {
    return;
  }

  if (!$entity->hasField('field_as_counter')) {
    return;
  }

  if ($entity->isNew()) {

    $connection = Database::getConnection();

    $max = $connection->select('node__field_as_counter', 'c')
      ->fields('c', ['field_as_counter_value'])
      ->orderBy('field_as_counter_value', 'DESC')
      ->range(0, 1)
      ->execute()
      ->fetchField();

    $counter = $max ? $max + 1 : 1;

    $entity->set('field_as_counter', $counter);

    $sequence = str_pad($counter, 5, '0', STR_PAD_LEFT);

    $chapter = $entity->get('field_as_chapter')->entity;
    $place = $entity->get('field_as_place')->entity;
    $organisation = $entity->get('field_as_organisation')->entity;

    $chapter_code = $chapter ? $chapter->label() : '';
    $context_code = '';

    if ($place) {
      $context_code = $place->label();
    }

    if ($organisation) {
      $context_code = $organisation->label();
    }

    $identifier = $chapter_code . '-' . $context_code . '-' . $sequence;

    $entity->setTitle($identifier);
  }
}

/**
 * Hide structural fields after creation.
 */
function costasano_asset_form_node_asset_edit_form_alter(&$form, FormStateInterface $form_state) {

  $node = $form_state->getFormObject()->getEntity();

  if (!$node->isNew()) {

    unset($form['field_as_chapter']);
    unset($form['field_as_place']);
    unset($form['field_as_organisation']);

  }

}

costasano_asset Module – Technical Overview - Code analysis and comments

This document explains how the costasano_asset custom module works, why it exists, and how its internal logic is structured. It is intended as a clear, successor‑friendly reference for future maintainers.

Purpose of the Module

The module provides two core behaviors for the asset content type:

  1. Automatically generate a unique, human‑readable identifier when an Asset node is created.
  2. Hide certain structural fields after creation so they cannot be modified later.

These behaviors ensure that Asset nodes receive stable identifiers and that the metadata used to construct those identifiers remains immutable.


1. Auto‑Generation of Asset Identifiers

The module implements hook_entity_presave() to modify Asset nodes before they are saved for the first time.

When the Logic Runs

The hook executes only when all of the following are true:

  • The entity is a node.
  • The node bundle is asset.
  • The node has the field field_as_counter.
  • The node is new (i.e., being created, not edited).

This ensures the identifier is generated exactly once.

Counter Generation

The module queries the database table node__field_as_counter to find the highest existing counter value:

  • If a value exists → increment it by 1.
  • If no value exists → start at 1.

The counter is then stored in field_as_counter.

The counter is padded to 5 digits:

00001, 00002, 00003, …

Building the Identifier

The identifier is constructed from three components:

Chapter code
The label of the referenced field_as_chapter entity.
Context code
Determined by the referenced entities:
  • If field_as_place exists → use its label.
  • If field_as_organisation exists → override the place label.
Sequence number
The padded counter.

The final identifier has the form:

CHAPTER-CONTEXT-00042

This identifier is then assigned as the node title.


2. Locking Structural Fields After Creation

The module implements:

hook_form_node_asset_edit_form_alter()

This hook runs when the edit form for an Asset node is displayed.

Behavior

If the node is not new (i.e., it is being edited), the following fields are removed from the form:

  • field_as_chapter
  • field_as_place
  • field_as_organisation

This prevents users from altering the metadata that was used to generate the identifier.


3. Architectural Summary

Below is a simplified flow of how the module behaves:

User creates Asset node
        ↓
hook_entity_presave()
    • Determine next counter
    • Build identifier (chapter + context + padded counter)
    • Set title and counter field
        ↓
Node is saved
        ↓
User edits Asset node
        ↓
hook_form_alter()
    • Hide structural fields

This ensures stable identifiers and consistent metadata.


4. Strengths of the Current Implementation

  • Simple and easy to understand.
  • Logic runs only when needed.
  • No unnecessary services or complexity.
  • Successor‑friendly and predictable.
  • Works well in low‑traffic environments.

5. Potential Improvements (Optional)

These are not required but may be useful for future refinement.

Replace Direct Database Query with EntityQuery

A more Drupal‑native approach would avoid querying field tables directly.

Clarify Context Priority

Currently, organisation overrides place if both are present. A comment or explicit rule may help future maintainers.

Concurrency Considerations

Simultaneous node creation could theoretically produce duplicate counters. If this becomes a concern, a locking mechanism can be added.

Service‑Based Refactoring

Moving the identifier logic into a service would improve testability and structure.


6. Summary

The costasano_asset module provides a clean, reliable mechanism for generating stable identifiers for Asset nodes and ensuring that the metadata used to construct those identifiers remains unchanged after creation. Its design is intentionally simple, making it easy to maintain and extend.

Future enhancements can focus on improving robustness, clarity, and architectural structure without altering the module’s core behavior.