Giới thiệu REST Easy phần 2: Sub Property Boogaloo

Hello again, RESTful friends. Welcome to episode II, The RESTful strikes back, the great RESTful caper, RESTful II SUBPROPERTY BOOGALOO.

In this series we are exploring the RESTful module (7.x-1.x), a wonderful project that will help you turn your Drupal 7 site into an API-serving headless machine. Last week we did the hard work of creating a basic endpoint with the RESTful module. We created a content type and added several fields and through a very small amount of code we were able to output content from Drupal into beautiful JSON. I will assume that you have completed the first tutorial and have a working code base.

In this week’s tutorial we are going to look at adding fields (multi-line text fields like “body” and the link field) with sub properties to our RESTful endpoint.

A body at REST

While we were able to output several different types of fields in our endpoint we skipped the body field. This week we’ll take care of that. The body field is one of a number of fields that need a little bit more care when utilized in a RESTful endpoint because it has a complex implementation.

Recall from last week’s tutorial that we created a custom module with a ctools plugin that defined our endpoint. We had a content type with the following fields:

field_artist_year_formed
field_artist_country_of_origin
field_artist_rnr_hall_of_fame
body
name

In our endpoint we expressed each field in the following format (allowing for different field names and properties):

$public_fields['yearFormed'] = array(
  'property' => 'field_artist_year_formed',
);

Fields like the body field, and really all multi-line text fields (you might refer to them as wysiwyg fields), have several values: the raw value of the field before any text formatting happens, the formatted version with text processing, the format being used and perhaps even a summary value. This is a lot more complex than many of the other fields we dealt with last week.

Let’s try to add the body field to our endpoint in the same way that we added simpler fields and see what happens in our endpoint.

// Example only, don't use this.
$public_fields['description'] = array(
  'property' => 'body',
);

That produces some pretty interesting results:

{
  "data":[
    {
      "id":"4",
      "label":"My Bloody Valentine",
      "self":"http:\/\/resteasy.local.dev\/api\/v0.1\/artists\/4",
      "yearFormed":"1983",
      "countryOfOrigin":"Ireland",
      "rockAndRollHallOfFameInductee":false,
      "description": {
        "value":"My Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983. Since 1987, the band\u0027s lineup has consisted of founding members Kevin Shields (vocals, guitar) and Colm \u00d3 C\u00edos\u00f3ig (drums), with Bilinda Butcher (vocals, guitar) and Debbie Googe (bass). The group is known for their integration of distorted noise and ethereal melody, and for their innovative utilization of unorthodox guitar and production techniques. Their work in the late 1980s and early 1990s resulted in their pioneering a musical style known as shoegazing.\r\n\r\n(from https:\/\/en.wikipedia.org\/wiki\/My_Bloody_Valentine_%28band%29)",
        "summary":"My Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983",
        "format":"filtered_html",
        "safe_value":"\u003Cp\u003EMy Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983. Since 1987, the band\u0027s lineup has consisted of founding members Kevin Shields (vocals, guitar) and Colm \u00d3 C\u00edos\u00f3ig (drums), with Bilinda Butcher (vocals, guitar) and Debbie Googe (bass). The group is known for their integration of distorted noise and ethereal melody, and for their innovative utilization of unorthodox guitar and production techniques. Their work in the late 1980s and early 1990s resulted in their pioneering a musical style known as shoegazing.\u003C\/p\u003E\n\u003Cp\u003E(from \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/My_Bloody_Valentine_%28band%29\u0022\u003Ehttps:\/\/en.wikipedia.org\/wiki\/My_Bloody_Valentine_%28band%29\u003C\/a\u003E)\u003C\/p\u003E\n",
        "safe_summary":"\u003Cp\u003EMy Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983\u003C\/p\u003E\n"
      }
    }
  ],
  "self":{
    "title":"Self",
    "href":"http:\/\/resteasy.local.dev\/api\/v0.1\/artists\/4"
  }
}

That’s quite a difference. We have many different pieces of information as part of the description. What does this information look like to you? If you said Drupal implementation details you’d be right.

In REST Easy part 1 we talked about some of the dangers of exposing implementation details to your API consumers. API consuming applications and developers are going to make assumptions and use all of the parts of your API that you expose. Let’s say (and we know it will) that implementations change between major Drupal versions or even minor ones, the types of items that are included in this wide and generic body field might change as well and that could lead to a broken API or more work when we upgrade our back-end and that’s not a very good thing.

So how can we target some of this information without getting the implementation details of this field? Well, for fields with sub-properties like these, we need to specify which “sub-property” of the field we want in our endpoint. We do that with code like this:

$public_fields['description'] = array(
  'property' => 'body',
  'sub_property' => 'value',
);

And that will give us this less implementation details based response:

{
  "data": [
    {
      "id":"4",
      "label":"My Bloody Valentine",
      "self":"http:\/\/resteasy.local.dev\/api\/v0.1\/artists\/4",
      "yearFormed":"1983",
      "countryOfOrigin":"Ireland",
      "rockAndRollHallOfFameInductee":false,
      "description":"\u003Cp\u003EMy Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983. Since 1987, the band\u0027s lineup has consisted of founding members Kevin Shields (vocals, guitar) and Colm \u00d3 C\u00edos\u00f3ig (drums), with Bilinda Butcher (vocals, guitar) and Debbie Googe (bass). The group is known for their integration of distorted noise and ethereal melody, and for their innovative utilization of unorthodox guitar and production techniques. Their work in the late 1980s and early 1990s resulted in their pioneering a musical style known as shoegazing.\u003C\/p\u003E\n\u003Cp\u003E(from \u003Ca href=\u0022https:\/\/en.wikipedia.org\/wiki\/My_Bloody_Valentine_%28band%29\u0022\u003Ehttps:\/\/en.wikipedia.org\/wiki\/My_Bloody_Valentine_%28band%29\u003C\/a\u003E)\u003C\/p\u003E\n"
    }
  ],
  "self": {
    "title":"Self",
    "href":"http:\/\/resteasy.local.dev\/api\/v0.1\/artists\/4"
  }
}

1) First, ensure that your body fields in your example content are filled out in Drupal (so there will be something to see in the API).
2) After that, let’s add the body field to our endpoint, but let’s do this in the right way, by adding a new sub version of our endpoint. Last week we createdv0.1, so this week let’s create v0.2.

Add the folders and files for v0.2

(edit: Please note that because this is a tutorial series, endpoint versions are not uniform as you’d expect them to be with an official version release read the following comments for more info: http://fourword.fourkitchens.com/article/rest-easy-part-4-taxonomy-man#comment-2230720908)

OK - we’ll be starting off with the folder structure we created last week:

  • Create the following folder and files inside of `plugins/restful/node/artist/
    • plugins/restful/node/artists/0.2/
    • plugins/restful/node/artists/0.2/artists__0_2.inc - This will hold our plug-in definitions
    • plugins/restful/node/artists//0.2/ResteasyRestfulEntityArtistsResource__0_2.class.php- This is our new 0.2 class file for all of our new additions this week.

Your folder structure should look like this now:

3) Now we can modify our plugin definition file atplugins/restful/node/artists/0.2/artists__0_2.inc and it will look like this:

<?php

/**
 * @file
 * Artists plugin definition.
 */

$plugin = array(
  'label' => t('Artists'),
  'resource' => 'artists',
  'name' => 'artists__0_2',
  'entity_type' => 'node',
  'bundle' => 'artist',
  'description' => t('This resource presents artists from REST easy.'),
  'class' => 'ResteasyRestfulEntityArtistsResource__0_2',
  'major_version' => 0,
  'minor_version' => 2,
);

4) Our new class file atplugins/restful/node/artists/0.2/ResteasyRestfulEntityArtistsResource__0_2.class.phpwill look like this:

<?php

/**
 * @file
 * Contains ResteasyRestfulEntityArtistsResource__0_2.
 */


/**
 * Implements RestfulEntityBaseNode class for the "artist" content type.
 */
class ResteasyRestfulEntityArtistsResource__0_2 extends RestfulEntityBaseNode {

  /**
   * Overrides RestfulEntityBaseNode::publicFieldsInfo().
   */
  public function publicFieldsInfo() {
    $public_fields = parent::publicFieldsInfo();

    $public_fields['yearFormed'] = array(
      'property' => 'field_artist_year_formed',
    );

    $public_fields['countryOfOrigin'] = array(
      'property' => 'field_artist_country_of_origin',
    );

    $public_fields['rockAndRollHallOfFameInductee'] = array(
      'property' => 'field_artist_rnr_hall_of_fame',
    );

    $public_fields['description'] = array(
      'property' => 'body',
      'sub_property' => 'value',
    );

    return $public_fields;
  }
}

By adding this folder and these files with the new version information we have created a new sub version of our previous endpoint. Now visit your endpoint /api/v0.2/artists and you should see results very similar to mine above.

body parts

You have access to some of the sub-properties of the body field. So you can print out the various elements of the body field simply by targetting a different sub property. Here is a list of each of the sub-properties you can access of the multi-line text field, body:

// Don't implement this as a matter of course, always consider good endpoint design. Examples only.

$public_fields['description'] = array(
  'property' => 'body',
  'sub_property' => 'value',
);

$public_fields['shortDescription'] = array(
  'property' => 'body',
  'sub_property' => 'summary',
);

$public_fields['descriptionFormat'] = array(
  'property' => 'body',
  'sub_property' => 'format',
);

Sub-properties are part of the implementation of a field in code so you may need to get into the code nitty-gritty to figure out the what’s what and whathaveyous. Notice that safe_value and safe_summary are not available to you. That doesn’t mean you can’t get those values, but that’s yet another tutorial down the road. If you are curious about a field that has subproperties - you can also simply code the field as a simple field in your API and see what’s there and attempt to access it via sub-property, or investigate through dpm or devel. It may or may not work but it won’t take very much time to figure that out.

Let’s take one more look at sub-property use in RESTful with another fairly frequent occurrence, the link field. Let’s add one of those to our content type and put it into our endpoint.

5) Let’s add the link module to our website so we can use a link field.

drush dl link
drush pm-enable link

6) Save and add the following field to the artist content type (or download the newest release of our helper code base: https://github.com/fourkitchens/resteasy/releases/tag/resteasy-part-2):
* Artist website
* machine name: field_artist_website
* type - link
* Absolute URL, Validate URL - checked
* Link Title: Required title
* 1 value

Let’s add this new element to our 0.2 endpoint and really value-pack our new end point sub version. Just as we did with the body field let’s see what happens if we output this field just like a normal field. Here is the php to make that happen:

// Example only, don't use this.
$public_fields['artistWebsite'] = array(
  'property' => 'field_artist_website',
);

Here’s what the output will look like:

{
  "data": [
    {
      "id":"3",
      "label":"The Jimi Hendrix Experience",
      "self":"http:\/\/resteasy.local.dev\/api\/v0.2\/artists\/3",
      "yearFormed":"1966",
      "countryOfOrigin":"USA",
      "rockAndRollHallOfFameInductee":true,
      "description":null,
      "artistWebsite":{
        "url":"http:\/\/jimihendrix.com",
        "title":"The Jimi Hendrix Experience",
        "attributes": []
      }
    }
  ],
  "self": {
    "title":"Self",
    "href":"http:\/\/resteasy.local.dev\/api\/v0.2\/artists\/3"
  }
}

Wow, again, pretty cool, but also attached to implementation details or perhaps not the right thing we want to expose. Let’s use sub-properties to expose on the url from the link field.

Yo Dawg, I heard you like subproperties…

Using the sub-property format for this field we can expose only the portions of the field that are relevant or wanted.

Let’s give it a try, by targeting the sub_property of url.

6) Here is the final version of the 0.2 endpoint class fileplugins/restful/node/artists/0.2/ResteasyRestfulEntityArtistsResource__0_2.class.phpwith the link url sub property:

<?php

/**
 * @file
 * Contains ResteasyRestfulEntityArtistsResource__0_2.
 */


/**
 * Implements RestfulEntityBaseNode class for the "artist" content type.
 */
class ResteasyRestfulEntityArtistsResource__0_2 extends RestfulEntityBaseNode {

  /**
   * Overrides RestfulEntityBaseNode::publicFieldsInfo().
   */
  public function publicFieldsInfo() {
    $public_fields = parent::publicFieldsInfo();

    $public_fields['yearFormed'] = array(
      'property' => 'field_artist_year_formed',
    );

    $public_fields['countryOfOrigin'] = array(
      'property' => 'field_artist_country_of_origin',
    );

    $public_fields['rockAndRollHallOfFameInductee'] = array(
      'property' => 'field_artist_rnr_hall_of_fame',
    );

    $public_fields['description'] = array(
      'property' => 'body',
      'sub_property' => 'value',
    );

    $public_fields['artistWebsite'] = array(
      'property' => 'field_artist_website',
      'sub_property' => 'url',
    );

    return $public_fields;
  }
}

Now our endpoint output looks a bit like this:

{
  "data": [
    {
      "id":"3",
      "label":"The Jimi Hendrix Experience",
      "self":"http:\/\/resteasy.local.dev\/api\/v0.2\/artists\/3",
      "yearFormed":"1966",
      "countryOfOrigin":"USA",
      "rockAndRollHallOfFameInductee":true,
      "description":null,
      "artistWebsite":"http:\/\/jimihendrix.com"
    }
  ],
  "self":{
    "title":"Self",
    "href":"http:\/\/resteasy.local.dev\/api\/v0.2\/artists\/3"
  }
}

Things are looking pretty good: we’ve got a body field in our endpoint. Because the body field is simply an implementation of a multi-line text field, we can use this exact same formula for any other multi-line text field. We also used sub-properties to target only a portion of the implementation revealed for a link field. Using sub-properties will help you build better targetted and less coupled attributes. There still is a ton of ground to cover, so stick with it and we’ll see you next week for another tutorial and more RESTful!