Skip to content

Upgrading from 1.x to 2.x

jeff-h edited this page Dec 8, 2015 · 8 revisions

Upgrading from 1.x to 2.x will involve the following steps:

  1. Upgrade the Drupal module, and install v2's dependencies (plug, registry_autoload)
  2. Upgrade the 'entity_validator' module, if using it.
  3. Move the CTools plugin definition to an annotation.
  4. Place your new class in the PSR-4 route.
  5. Adapt your resource class to the new API.
  6. API changes.
  7. JSON API (not really a change).

Most of the effort spent in the upgrade will happen in step five, but hopefully that will be easy to take.

At any time during your upgrade use the example plugins to see how they were upgraded. You can open two different browser tabs, one with the 7.x-1.x code in GitHub an the other with 7.x-2.x. That will help you to make sense of the explanations in this docs. I went through the upgrade process in the RESTful Search API module. I left the branch up there so you can follow this with that example. This upgrade is a good example of a complex upgrade (one with a custom data provider). In general you should have enough with the examples in the RESTful module.

1. Upgrade the Drupal module and install new dependencies

Upgrading to RESTful v2 is as per standard Drupal practice. Note that v2 depends on a couple of other modules which you will also need to install.

If you get database errors after upgrading to v2 (e.g. DatabaseSchemaObjectExistsException: Table field_revision_refresh_token_reference already exists) you may need to do the following:

  • disable and then uninstall the RESTful module(s).
  • directly in your database, delete the field_data_refresh_token_reference and field_revision_refresh_token_reference tables entirely (using, for example, phpMyAdmin, or Sequel Pro etc)
  • enable the RESTful module(s)

2. Upgrade entity_validator module

RESTful optionally integrates with the entity_validator module. If you're using this module, you will need to upgrade it to 2.x (like RESTful, Entity Validator also migrated from Ctools to Plug).

3. From CTools to annotations

RESTful 1.x and 2.x both use plugins to define the different resources. Plugins are just classes, that Drupal can discover, and that can be instantiated for you using a plugin manager. This principle has not changed from 1.x to 2.x, that means that you will still have a single plugin for every resource.

Your plugin definition now will live along with your class implementation, instead of in a separate file, in what is called an annotation.

Imagine the following CTools plugin:

$plugin = array(
  'label' => t('Articles'),
  'resource' => 'articles',
  'name' => 'articles__1_6',
  'entity_type' => 'node',
  'bundle' => 'article',
  'description' => t('Export the article content type in XML format.'),
  'class' => 'RestfulExampleArticlesResource__1_6',
  'authentication_types' => TRUE,
  'authentication_optional' => TRUE,
  'minor_version' => 6,
  // Output the data in XML following the common HAL conventions.
  'formatter' => 'hal_xml',
);

In a hypothetical upgrade, it would become the following annotation:

/**
 * Class Articles__1_6
 * @package Drupal\restful\Plugin\resource
 *
 * @Resource(
 *   name = "articles:1.6",
 *   resource = "articles",
 *   label = "Articles",
 *   description = "Export the articles with all authentication providers.",
 *   authenticationTypes = TRUE,
 *   authenticationOptional = TRUE,
 *   dataProvider = {
 *     "entityType": "node",
 *     "bundles": {
 *       "article"
 *     },
 *   },
 *   formatter = "hal_xml",
 *   majorVersion = 1,
 *   minorVersion = 6
 * )
 */
class Articles__1_6 extends ResourceNode implements ResourceInterface {

The first glaring difference is that the previous underscore separated variables, are now in snake case. We go from authentication_types to authenticationTypes.

Another important difference is that we don't have to specify the class this plugin definition is for, since the annotation only applies to the class it precedes.

In RESTful 2.x there is no default values for your versions. The motivation for this is to emphasize the versioning by making the implementors more aware of it. That means that you will need to add your major and minor versions to your plugin definition.

This version introduces the concept of the data provider in the plugin definition. That means that all the options that control how to get the data from your backend, will probably live under the dataProvider key in your annotation. That includes the entityType, bundles, range, idKey, …

4. Place your class in a PSR-4 route

RESTful 2.x no longer uses CTools for class discovery, it will use Registry Autoload (via the Plug dependency). You will need to do the following for Drupal to be able to find your classes:

  1. Add registry_autoload[] = PSR-4 to your my_module.info file. This instructs Drupal to use Registry Autoload to find classes following a PSR-4 structure.
  2. Move your resource class to my_module/src/resource/the/path/you/want, and add the following namespace to your class Drupal\my_module\resource\the\path\you\want.
  3. As the name of the file, use the name of your class with the .php extension. Do not add the .class.php, just .php.

If your resource class is called Articles and it was moved to my_module/src/resource/node/articles/v1/6 (and the corresponding namespace added) then the full name of your class is now Drupal\my_module\resource\node\articles\v1\6\Articles. Note how even if you have different Article classes, there is no risk of collision because the namespaces will be different.

5. Adapt your resource to the new API

In RESTful 1.x all the logic was jammed in the resource class. That made it very easy for an implementor to override any piece of functionality for a given resource. In RESTful 2.x different classes are in charge of different functionality.

There are 3 important layers of functionality (although most probably you don't need to worry about them)

  • The resource plugin. This is where you define the shape of the resource and how you interact with it.
  • The data provider object. A new Data Provider is created for your resource plugin automatically. That means that unless you need to override any method in the data provider, you can forget about this. This object is in charge of interacting with the backend to read and write data.
  • The resource field classes. The resource field class is in charge of getting and formatting the data during read operations. During write operations it is in charge of processing the data and sending it to the data provider to save it to the database. You can define which resource class to use in your field definition by using the class key, although one will be automatically guessed for you.

Resource bases

Right after moving the the class to its new location you will discover that your resource is extending from a base class that no longer exists. \RestfulBase, \RestfulEntityBase, \RestfulNodeBase, etc are no longer available. Instead you will need to use Resource, ResourceEntity, ResourceNode, etc. Look for an example that is similar to your resource and choose the base class that makes sense to you.

The field definitions

First you will need to change the method signature from public function publicFieldsInfo() to protected function publicFields(). 95% of the time this is all you will need to do.

The other change is that now every public field is associated with an object implementing ResourceFieldInterface. All public field logic has been delegated to the resource field objects, with the help of the data provider. For instance to get the contents of the textfield for the formatter to render, or to preprocess the sub-request JSON payload to turn it into an entity ID when writing an entityreference field.

Most of the time RESTful will detect the best resource field class based on the field definition (e.g. ResourceFieldEntityReference for an entityreference field). Some times there is not enough information in Drupal, or you want to provide a custom class for a custom field type. In those situations you will need to use the new key class to specify the class to be used. See the example in Main__1_7 where instead of treating the file_single as a file array we treat it as an entity reference:

    $public_fields['file_single'] = array(
      'property' => 'file_single',
      'class' => '\Drupal\restful\Plugin\resource\Field\ResourceFieldFileEntityReference',
      'referencedIdProperty' => 'uuid',
    );

It's also worth noting, that if you need to do any kind of post processing to the field definitions, you can now use the method processPublicFields as also seen in Main__1_7.

Callback arguments

In an effort to have a better, and more consistent, developer experience, all callbacks now interact with the backend receiving the same parameters. Independently of the type of resource (entity based, DB query, …) all callbacks will receive an interpreter object instead of the EntityMetadataWrapper. To get the wrapper from the interpreter just do $interpreter->getWrapper().

For instance, getEntitySelf transforms from:

  /**
   * Get the "self" url.
   *
   * @param \EntityMetadataWrapper $wrapper
   *   The wrapped entity.
   *
   * @return string
   *   The self URL.
   */
  protected function getEntitySelf(\EntityMetadataWrapper $wrapper) {
    return $this->versionedUrl($wrapper->getIdentifier());
  }

Into:

  /**
   * Get the "self" url.
   *
   * @param DataInterpreterInterface $interpreter
   *   The wrapped entity.
   *
   * @return string
   *   The self URL.
   */
  public function getEntitySelf(DataInterpreterInterface $interpreter) {
    return $this->versionedUrl($interpreter->getWrapper()->getIdentifier());
  }

The process_callbacks should remain unchanged.

create_or_update_passthrough

If you used create_or_update_passthrough in 1.x, check out the documentation on the new methods field descriptor.

6. API changes

There lots of internal methods that have been changed, renamed or deleted. Chances that your resource is using any of those are very slim, but still exist. If after making these changes you get PHP errors or error exceptions, check the examples to find the correct method in this new version.

7. JSON API

This is not a real change but a slight shift in direction. The new version of RESTful comes with an implementation of JSON API. This is the recommended formatter for your output, and the one used in the video tutorial series.

RESTful 2.x is focused to deliver the best experience when using the JSON API formatter, although it still supports the good old Simple JSON and offers the possibility to use your own formatter if needed.

Several extensions have been applied to the official specification to make it more flexible (and offer even more possibilities):

  • Sparse fieldsets per relationship. The official specification suggests that sparse fieldsets are to be specified per resource. Check this link to know more about this issue. The extension specification can be found here (LINK NEEDED).
  • Nested filters. This extension specification explains how you can filter lists of items based not on their properties, but in the properties of their relationships.
Clone this wiki locally