Skip to main content

Using Wrappers Delight for clean, composable, and scalable backend code in Drupal 7

Drupal

Wrappers Delight is an awesome module for developers. If you aren't familiar with it, here are the highlights (from the author):

  • Provides wrapper classes for common entity types, with getters and setters for the entities' base properties. (These classes are wrappers/decorators around EntityMetadataWrapper.)
  • Adds a Drush command that generates wrapper classes for the specific entity bundles on your site, taking care of the boilerplate getter and setter code for all the fields you have configured on the bundles.
  • Returns sanitized values by default for the generated getters for text fields. (raw values can be returned with an optional parameter).
  • Allows the wrapper classes to be customized, so that you can decouple your custom code from specific Drupal field implementation.

Generate Wrapper Classes for your Entities

Wrappers delight generates wrapper classes for your entities. For example, I can wrap the user entity with:

drush wrap user

This creates a new module called `wrappers_custom` in your custom modules folder. (You can specify what module you want these classes to belong to if you want to attach them to an existing module instead, however, I have found it is cleanest to keep all wrapped classes in one module). In this new module, you'll find a new class "UserUserWrapper" that looks like this:

<?php
/**
 * @file
 * class UserUserWrapper
 */

class UserUserWrapper extends WdUserWrapper {

  private static $bundle = 'user';

  /**
   * Create a new user user.
   *
   * @param array $values
   * @param string $language
   * @return UserUserWrapper
   */
  public static function create($values = array(), $language = LANGUAGE_NONE) {
    $values += array('bundle' => self::$bundle);
    $entity_wrapper = parent::create($values, $language);
    return new UserUserWrapper($entity_wrapper->value());
  }
}

Adding getters and setters for fields

Now comes the fun part. Say you need to add first and last name fields to your user. After adding the fields in the UI, go back and run the drush command again. (I added fields named field_u_first_name and field_u_last_name):

drush wrap user

Wrappers delight updates the "UserUserWrapper.php" file to include getters and setters for the new fields: 

<?php
/**
 * @file
 * class UserUserWrapper
 */

class UserUserWrapper extends WdUserWrapper {

  private static $bundle = 'user';

  /**
   * Create a new user user.
   *
   * @param array $values
   * @param string $language
   * @return UserUserWrapper
   */
  public static function create($values = array(), $language = LANGUAGE_NONE) {
    $values += array('bundle' => self::$bundle);
    $entity_wrapper = parent::create($values, $language);
    return new UserUserWrapper($entity_wrapper->value());
  }

  /**
   * Retrieves field_u_first_name
   *
   * @return mixed
   */
  public function getUFirstName($format = WdEntityWrapper::FORMAT_DEFAULT, $markup_format = NULL) {
    return $this->getText('field_u_first_name', $format, $markup_format);
  }

  /**
   * Sets field_u_first_name
   *
   * @param $value
   *
   * @return $this
   */
  public function setUFirstName($value, $format = NULL) {
    $this->setText('field_u_first_name', $value, $format);
    return $this;
  }

  /**
   * Retrieves field_u_last_name
   *
   * @return mixed
   */
  public function getULastName($format = WdEntityWrapper::FORMAT_DEFAULT, $markup_format = NULL) {
    return $this->getText('field_u_last_name', $format, $markup_format);
  }

  /**
   * Sets field_u_last_name
   *
   * @param $value
   *
   * @return $this
   */
  public function setULastName($value, $format = NULL) {
    $this->setText('field_u_last_name', $value, $format);
    return $this;
  }
}

Now I can easily access the first and last name in a custom module:

<?php
/**
 * @file
 * The example module.
 */

/**
 * Implements hook_menu().
 */
function my_example_menu() {
  $items['my-example'] = array(
    'title' => 'Wrappers Delight is Awesome!',
    'page callback' => 'my_example_callback',
    'access callback' => 'user_is_logged_in',
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

/**
 * Callback for my-example
 */
function my_example_callback(){
  global $user;
  $account = new UserUserWrapper($user->uid);
  
  return array(
    'page_content' => array(
      '#type' => 'html_tag',
      '#tag' => 'h1',
      '#value' => 'Welcome ' . $account->getUFirstName() . " " . $account->getULastName() . "!"
    )
  );
}

Composing Methods for Cleaner Code

Now that we have getters and setters for first and last name, we can create other methods that formats the user's name in various ways:

  [...]

  /**
   * Retrieves formatted name
   *
   * @return string
   */
  public function getFullName() {
    return $this->getUFirstName() . " " . $this->getULastName();
  }

  /**
   * Retrieves formatted name
   *
   * @return string
   */
  public function getNameFormattedANewWay() {
    return $this->getULastName() . ", " . $this->getUFirstName();
  }

We can now clean up our module code:

[...]

/**
 * Callback for my-example
 */
function my_example_callback(){
  global $user;
  $account = new UserUserWrapper($user->uid);
  
  return array(
    'page_content' => array(
      '#type' => 'html_tag',
      '#tag' => 'h1',
      '#value' => 'Welcome ' . $account->getFullName() . "!"
    )
  );
}

This is a contrived example, but it shows how Wrappers Delight provides a lot of power for composing methods. This really helps keep code clean, especially as the code base grows. 

 

Smart Methods

Image fields

Wrappers delight also creates additional getters and setters that are extra helpful for certain types of fields. For example, after adding a profile image field (field_user_image'), the following methods would be appended to UserUserWrapper.php:

  [...]

  /**
   * Retrieves field_user_image
   *
   * @return mixed
   */
  public function getUserImage() {
    return $this->get('field_user_image');
  }

  /**
   * Sets field_user_image
   *
   * @param $value
   *
   * @return $this
   */
  public function setUserImage($value) {
    $this->set('field_user_image', $value);
    return $this;
  }

  /**
   * Retrieves field_user_image as an HTML <img> tag
   *
   * @param string $image_style
   *   (optional) Image style for the HTML
   * @param array $attributes
   *   IMG tag attributes
   *
   * @return string
   */
  public function getUserImageHtml($image_style = NULL, $attributes = array()) {
    $image = $this->get('field_user_image');
    $field_info = field_info_field('field_user_image');
    $fid = $field_info['settings']['default_image'];
    $default_image = file_load($fid);
    $default_image = file_load($fid);

    if (empty($image)) {
      $image = (array)$default_image;
    }

    if (!is_null($image_style)) {
      return theme('image_style', array('path' => $image['uri'], 'style_name' => $image_style, 'attributes' => $attributes));
    }
    else {
      return theme('image_style', array('path' => $image['uri'], 'attributes' => $attributes));
    }
    return NULL;
  }


  /**
   * Retrieves field_user_image as a URL
   *
   * @param string $image_style
   *   (optional) Image style for the URL
   * @param bool $absolute
   *   Whether to return an absolute URL or not
   *
   * @return string
   */
  public function getUserImageUrl($image_style = NULL, $absolute = FALSE) {
    $image = $this->get('field_user_image');
    if (!empty($image)) {
      if (!is_null($image_style)) {
        return url(image_style_path($image_style, $image['uri']), array('absolute' => $absolute));
      }
      else {
        return url(file_create_url($image['uri']), array('absolute' => $absolute));
      }
    }
    return NULL;
  }

Notice how wrappers delight generated two additional methods (in addition to the getter and setter):

  • getUserImageHtml()
  • getUserImageUrl()

We can update our custom module to include the image with an image style:

<?php
/**
 * @file
 * The example module.
 */

[...]

/**
 * Callback for my-example
 */
function my_example_callback(){
  global $user;
  $account = new UserUserWrapper($user->uid);
  
  return array(
    'welcome_message' => array(
      '#type' => 'html_tag',
      '#tag' => 'h1',
      '#value' => 'Welcome ' . $account->getFullName() . "!"
    ),
    'profile_picture' => array(
      '#markup' => $account->getUserImageHtml('large')
    ),
  );
}

There's more where that came from:

Wrappers delight provides a lot more than what we covered here. In future posts I'll cover:

  • Entity reference fields
  • Multi-valued fields
  • Wrapped Queries (wraps EntityFieldQuery)
  • Combining wrapped classes with wrapped queries 
  • Provide more complex examples

Be sure to check out this post by the module author as well.