[uml] DemoFormController

Webel module:

Package/Namespace: 

UML element type:

OOE stereotypes:

Relationships
extends [Generalization]: 
UML Diagram
Click on the UML diagram to view it in a lightbox image viewer. You may then zoom in (or just open the image in a new web browser tab using the Download Original link to view large diagrams).

UML modelling domain:

A switch now enables one to explore delegation to different submit handler versions.

This is used to also explore the following matter Support: Translations: A switch enables one to explore delegation to different submit handler versions.:

The examples are from Drupal7, but the conversation is for Drupal8 or Drupal9, as the Drupal7 boat has sailed.

I am working on an object-oriented bridge educational demo module for Drupal7, and one challenge is combining the desirable Don't Repeat Yourself (DRY) coding practice that OO encapsulation promotes (as opposed to the rather Write Everything Twice (WET) coding one finds in a lot of Drupal code, such as error prone repetition of "by convention" structured array key strings) with the Drupal localisation system and the t() function.

Here is an example adapted to OO form (within a FormController) from the form_example_tutorial_7_submit:

  /**
   * Submit function adapted from form_example_tutorial_7_submit().
   *
   * 'Adds a submit handler/function to our form to send a successful
   * completion message to the screen.'
   * 
   * PLease note this alternative version uses a quite error prone
   * @link http://drupal7demo.webel.com.au/node/12 WET @endlink
   * coding style with repetition of the names of form fields
   * in both array keys and in Drupal format string placeholders.
   * 
   * It is however compatible with the Drupal t() method since the
   * format string with placeholders passed to t() can be read
   * by the Drupal localization system outside run time.
   *  
   * @param array $form
   *   A Drupal form array.
   * @param array $form_state
   *   A Drupal form state array.
   */
  static public function submitDrupalStyle($form, &$form_state) {
    drupal_set_message(
       t('The form has been submitted. name="@first @last", year of birth=@year_of_birth',
        array(
      '@first' => $form_state['values']['first'],
      '@last' => $form_state['values']['last'],
      '@year_of_birth' => $form_state['values']['year_of_birth'],
        ))
    );
  }

Each of the fields 'first', 'last', 'year_of_birth' breaks here the DRY principle 3 times: once in the @placeholder, once in the array key for the placeholder, and once in the array key for the form value. It is WET.

A 1st order level (constants only, no objects yet) of encapsulation might look like this:

class DemoFormController extends AbstractFormController {
 
  // DRY! Define constants for field names.
  // We could also use static private $FIELD_ etc. here.
  // (but note PHP5.3 does not support final class variables).
 
  const FIELD_YOB = 'year_of_birth';
 
  const FIELD_NAME_FIRST = 'first';
 
  const FIELD_NAME_LAST = 'last';
 
..
 
  /**
   * Submit function adapted from form_example_tutorial_7_submit().
   *
   * 'Adds a submit handler/function to our form to send a successful
   * completion message to the screen.'
   * 
   * Please note how this OOE version respects the
   * @link http://drupal7demo.webel.com.au/node/10 DRY @endlink
   * principle through use of field name constants !
   * 
   * @todo This is however not yet compatible with Drupal translation
   * (which looks for a literal string with placeholders outside
   * run time).
   * 
   * @param array $form
   *   A Drupal form array.
   * @param array $form_state
   *   A Drupal form state array.
   */
  static public function submit($form, &$form_state) {
 
    // @todo Need utility method for standard formatting
    // of fields by name and value fetched from $form.
    drupal_set_message(
        t('Submitted: name="@' . self::FIELD_NAME_FIRST . ' @' . self::FIELD_NAME_LAST . '", year of birth=@' . self::FIELD_YOB,
            array(
      '@' . self::FIELD_NAME_FIRST => self::getFormValue($form_state, self::FIELD_NAME_FIRST),
      '@' . self::FIELD_NAME_LAST => self::getFormValue($form_state, self::FIELD_NAME_LAST),
      '@' . self::FIELD_YOB => self::getFormValue($form_state, self::FIELD_YOB),
            )
        )
        // Coder: ERROR | Concatenating translatable strings is not allowed,
        // use placeholders instead and only one string literal.
    );        
  }

From an OO perspective this is massively superior to the "Drupal style" version, as one can change the name of the field in one place and, because of DRY coding, it is caught throughout this method (as well as everywhere else in the class). But it is not compatible with the Drupal localisation registry strategy, which as far as I can tell relies on literal strings or strings with placeholders that it can detect outside run-time.

A "2nd order" encapsulation would take this example above further, one could have a Field type with both a name and a label, so that the label also does not appear in directly in the translated drupal_set_message argument, but that would be even less compatible with the Drupal localisation mechanism.

Summary

I appreciate fully why the Drupal localisation system works as it does, however I see an opportunity in future for a new system that can also accept registration of complex strings at run-time, and can thus be more compatible with object-oriented encapsulation of strings and with dynamic generation of strings.


Related: Concerning recommendations for internal links using placeholders
The organisation of the various submit handlers is now a bit different from that report above, see the UML diagram above, one can now switch between WET-Drupal (compatible with t()) and DRY-OO (not yet compatible with t()) versions.
Notes (policies)