You are here

Extending the entity registration module

Submitted by nicolas on Tue, 11/12/2013 - 17:23

Entity registration module

The Entity Registration module is a nice module for allowing and tracking user registrations on any entity. It even has some nice extras: you can restrict the number of people that can sign up, integrate with drupal commerce for fee-based signups,...

Extra requirements

As part of a project I was asked to deliver an event signup system. A lot of the functionality was already provided by the entity registration module, but some parts were missing:

  • When the number of possible registrants is reached the link to subscribe needed to change in a text stating "fully booked".
  • When subscribing for more than one person, an extra check needed to be made everytime an extra person was added via a field collection field.

 

Extending the module

Part one: registration link that changes into text when fully booked.

To make our first item possible: a link to the registration that becomes a text stating "fully booked" when all spaces have been occupied, can be done by creating a new formatter. I was adding all the customizations regarding this feature request in a Drupal feature. This code goes in the .module file and will make sure there's an extra formatter to select when going to "Manage Display" on the content type/entity you want to allow registrations on.I copied code from an existing formatter in the entity registration module and adapted it to add an extra formatter for entity registration fields.

/**
 * Implements hook_field_formatter_info
 */
function my_event_feature_field_formatter_info() {
  return array(
    'registration_link_full_text' => array(
      'label' => t('Registration Link - capacity full text'),
      'field types' => array('registration'),
      'settings' => array(
        'label' => ' ',
      ),
    ),
  );
}

This part registers the formatter in Drupal. Here you also define for which field type(s) this formatter may be used. We tell Drupal it is for "registration" fields.

/**
 * Implements hook_field_formatter_settings_form().
 */
function my_event_feature_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $element = array();

  if ($display['type'] == 'registration_link_full_text') {
    $element['label'] = array(
      '#title' => t('Label'),
      '#type' => 'textfield',
      '#size' => 20,
      '#default_value' => $settings['label'],
      '#required' => FALSE,
      '#description' => t("Optional label to use when displaying the registration title or link. Leave blank to use the parent event's label."),
    );
  }

  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function my_event_feature_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $summary = '';

  if ($display['type'] == 'registration_link_full_text') {
    if (!empty($settings['label'])) {
      $summary = t('Registration label: @label.', array('@label' => $settings['label']));
    }
    else {
      $summary = t('Registration label: Parent label.');
    }
  }

  return $summary;
}

/**
 * 
 * Implements hook_field_formatter_view
 * 
 */
function my_event_feature_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();

  // we know we should only have a single item
  if (isset($items[0]['registration_type']) && !empty($items[0]['registration_type'])) {
    $reg_type = registration_type_load($items[0]['registration_type']);
    $settings = $display['settings'];
    $label = !empty($settings['label']) ? $settings['label'] : $reg_type->label;

    switch ($display['type']) {
      case 'registration_link_full_text':
        // Enable registration link if accessible.
        if (registration_register_page_access($entity_type, $entity)) {
          list($entity_id) = entity_extract_ids($entity_type, $entity);
            if (registration_status($entity_type, $entity_id)) {
            $uri = entity_uri($entity_type, $entity);
            $element[0] = array(
              '#markup' => theme('registration_link',
                array(
                  'label' => $label,
                  'path' => $uri['path'] . '/register'
                )
              )
            );
          }
          else {
            $error_message = t('Fully booked.');
            $element[0] = array(
              '#markup' => check_plain($error_message),
            );
          }
        }
        break;
    }
  }

  return $element;
}

Here the important part is where we check if the subscription limit has been reached. Don't try to do this with your own database queries. The module has its own API. Try to use those functions instead. I found out they used registration_status($entity_type, $entity_id) to know if an event is fully booked or not. Instead of always displaying the registration link, we check if it is fully booked. If not we display the link, else we display the message.

Part two: subscribing more than one person with check

When using the entity registration module, it comes already with a field called spaces. And you can allow to subscribe more than one person. But you can only do this by providing your information and fill in the number of spaces you would like to take. So, you're not providing any information on the extra people you're subscribing too.

In our case they wanted a field collection field on the registration entity. And when subscribing more people, you have to fill in information for every extra person. This is quite easy to do. The tricky part? Adding a check every time someone clicked on the button "Add item" of the field collection.

It took me some time to find the correct way to do it. But this issue showed me how to do it: https://drupal.org/node/1519946

/*
 * Adds a validation hook to the beginning of the #element_validate
 * to provide custom validation on the required fields within the
 * widget.
 */
function my_event_feature_field_widget_form_alter(&$element, &$form_state, $context) {
  switch ($context['instance']['widget']['type']) {
    case 'field_collection_embed':
      // Check for our specific field.
      if ($context['instance']['field_name'] == 'field_reg_extra_registrants') {
        array_unshift($element['#element_validate'], 'my_event_feature_field_collection_validate');
      }
      break;
  }
}

The problem I had: I tried to add a validator when using a hook_form_alter. But this was too late in the Drupal process to add a validator and it wasn't picked up. I had to use hook_field_form_alter to add an extra validation on the element (being our field collection field).

function my_event_feature_field_collection_validate($element, &$form_state, $form) {
  if ($form_state['clicked_button']['#name'] == 'field_reg_extra_registrants_add_more') { 
    // ID and entity_type of the entity (in our case event node).
    $entity_id = $form_state['registration']->entity_id;
    $entity_type = $form_state['registration']->entity_type;

    // Get registration settings for the node
    $settings = registration_entity_settings($entity_type, $entity_id);

    // Get total capacity
    $capacity = $settings['capacity'];
    if ($capacity != 0) {
      $used_slots = registration_event_count($entity_type, $entity_id);
      $extra_registrants_count = 0;
      foreach ($form_state['values']['field_reg_extra_registrants'][LANGUAGE_NONE] as $item) {
        $extra_registrants_count++;
      }
      // 1 from the registration, add the extra registrants already filled in and one more as we want to add one.
      $registration_slots = 1 + $extra_registrants_count;

      if ($capacity - $used_slots - $registration_slots < 0) {
        $node = node_load($entity_id);
        $error_message = t('This event is fully booked. You cannot add another person.');
        form_error($form['field_reg_extra_registrants'][LANGUAGE_NONE][0]['field_reg_extra_name'], $error_message);
      }
    }
  }
}

This part is the validator. It checks whether or not there is still space available to subscribe more people. If not: an error message is shown.

To complete this extra functionality, I did hide the "spaces" field, so people wouldn't be able to control it, or add extra spaces. Everything would work with the field collection. But to make everything work, I needed to add an extra hook_entity_presave

/**
 * Implements hook_entity_presave
 * @param type $entity
 * @param type $type
 */
function my_event_feature_entity_presave($entity, $type) {
  if ($type == 'registration') {
    $count = 1;
    $field_reg_extra_registrants = $entity->field_reg_extra_registrants;
    foreach ( $field_reg_extra_registrants[LANGUAGE_NONE] as $item) {
      $count++;
    }
    $entity->count = $count;
  }
}

This hook is going to count all the registrants of this one registration and then fill the number in the spaces field, in order to not break the default functionality in the entity registration module.

Conclusion

I hope this will help the people trying to do the same thing. And remember, don't reinvent the wheel. Try to use functions already present in a module, and do not rewrite them from scratch with your own functionality, as you might break the logic. Have fun!

Blog category:

Technology: